v7‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT data/upload/readme.txt000064400000000130152101576050010733 0ustar00You can upload files to this folder via FTP to make them available later for your users data/temp-write-test-1465897129000064400000000000152101576050011543 0ustar00data/cache/readme.txt000064400000000000152101576050010506 0ustar00data/cache/zend_cache---internal-metadatas---5a54ef68b677ff09ee217dd76c816de3000060000000000142152101576050021243 0ustar00a:4:{s:4:"hash";i:2024603604;s:5:"mtime";i:1474477686;s:6:"expire";i:1474481286;s:4:"tags";a:0:{}}data/cache/zend_cache---5a54ef68b677ff09ee217dd76c816de3000060000000042020152101576050015337 0ustar00a:4:{s:4:"data";s:17329:"a:2:{i:0;s:0:"";i:1;a:18:{s:13:"numberSymbols";a:10:{s:7:"decimal";s:1:".";s:5:"group";s:1:",";s:4:"list";s:1:";";s:11:"percentSign";s:1:"%";s:8:"plusSign";s:1:"+";s:9:"minusSign";s:1:"-";s:11:"exponential";s:1:"E";s:8:"perMille";s:3:"‰";s:8:"infinity";s:3:"∞";s:3:"nan";s:3:"NaN";}s:13:"decimalFormat";s:9:"#,##0.###";s:16:"scientificFormat";s:3:"#E0";s:13:"percentFormat";s:6:"#,##0%";s:14:"currencyFormat";s:23:"¤#,##0.00;(¤#,##0.00)";s:15:"currencySymbols";a:2:{s:3:"JPY";s:2:"¥";s:3:"USD";s:1:"$";}s:10:"monthNames";a:2:{s:11:"abbreviated";a:12:{i:1;s:3:"Jan";i:2;s:3:"Feb";i:3;s:3:"Mar";i:4;s:3:"Apr";i:5;s:3:"May";i:6;s:3:"Jun";i:7;s:3:"Jul";i:8;s:3:"Aug";i:9;s:3:"Sep";i:10;s:3:"Oct";i:11;s:3:"Nov";i:12;s:3:"Dec";}s:4:"wide";a:12:{i:1;s:7:"January";i:2;s:8:"February";i:3;s:5:"March";i:4;s:5:"April";i:5;s:3:"May";i:6;s:4:"June";i:7;s:4:"July";i:8;s:6:"August";i:9;s:9:"September";i:10;s:7:"October";i:11;s:8:"November";i:12;s:8:"December";}}s:12:"monthNamesSA";a:1:{s:6:"narrow";a:12:{i:1;s:1:"J";i:2;s:1:"F";i:3;s:1:"M";i:4;s:1:"A";i:5;s:1:"M";i:6;s:1:"J";i:7;s:1:"J";i:8;s:1:"A";i:9;s:1:"S";i:10;s:1:"O";i:11;s:1:"N";i:12;s:1:"D";}}s:12:"weekDayNames";a:3:{s:11:"abbreviated";a:7:{i:0;s:3:"Sun";i:1;s:3:"Mon";i:2;s:3:"Tue";i:3;s:3:"Wed";i:4;s:3:"Thu";i:5;s:3:"Fri";i:6;s:3:"Sat";}s:5:"short";a:7:{i:0;s:2:"Su";i:1;s:2:"Mo";i:2;s:2:"Tu";i:3;s:2:"We";i:4;s:2:"Th";i:5;s:2:"Fr";i:6;s:2:"Sa";}s:4:"wide";a:7:{i:0;s:6:"Sunday";i:1;s:6:"Monday";i:2;s:7:"Tuesday";i:3;s:9:"Wednesday";i:4;s:8:"Thursday";i:5;s:6:"Friday";i:6;s:8:"Saturday";}}s:14:"weekDayNamesSA";a:1:{s:6:"narrow";a:7:{i:0;s:1:"S";i:1;s:1:"M";i:2;s:1:"T";i:3;s:1:"W";i:4;s:1:"T";i:5;s:1:"F";i:6;s:1:"S";}}s:11:"dateFormats";a:5:{s:4:"full";s:15:"EEEE, MMMM d, y";s:4:"long";s:9:"MMMM d, y";s:6:"medium";s:8:"MMM d, y";s:5:"short";s:6:"M/d/yy";s:3:"php";s:5:"m/d/y";}s:11:"timeFormats";a:5:{s:4:"full";s:14:"h:mm:ss a zzzz";s:4:"long";s:11:"h:mm:ss a z";s:6:"medium";s:9:"h:mm:ss a";s:5:"short";s:6:"h:mm a";s:3:"php";s:5:"h:i A";}s:14:"dateTimeFormat";s:8:"{1}, {0}";s:5:"units";a:21:{s:3:"day";a:2:{s:3:"one";s:7:"{0} day";s:5:"other";s:8:"{0} days";}s:10:"day-future";a:2:{s:3:"one";s:10:"In {0} day";s:5:"other";s:11:"In {0} days";}s:8:"day-past";a:2:{s:3:"one";s:11:"{0} day ago";s:5:"other";s:12:"{0} days ago";}s:4:"hour";a:2:{s:3:"one";s:8:"{0} hour";s:5:"other";s:9:"{0} hours";}s:11:"hour-future";a:2:{s:3:"one";s:11:"In {0} hour";s:5:"other";s:12:"In {0} hours";}s:9:"hour-past";a:2:{s:3:"one";s:12:"{0} hour ago";s:5:"other";s:13:"{0} hours ago";}s:6:"minute";a:2:{s:3:"one";s:10:"{0} minute";s:5:"other";s:11:"{0} minutes";}s:13:"minute-future";a:2:{s:3:"one";s:13:"In {0} minute";s:5:"other";s:14:"In {0} minutes";}s:11:"minute-past";a:2:{s:3:"one";s:14:"{0} minute ago";s:5:"other";s:15:"{0} minutes ago";}s:5:"month";a:2:{s:3:"one";s:9:"{0} month";s:5:"other";s:10:"{0} months";}s:12:"month-future";a:2:{s:3:"one";s:12:"In {0} month";s:5:"other";s:13:"In {0} months";}s:10:"month-past";a:2:{s:3:"one";s:13:"{0} month ago";s:5:"other";s:14:"{0} months ago";}s:6:"second";a:2:{s:3:"one";s:10:"{0} second";s:5:"other";s:11:"{0} seconds";}s:13:"second-future";a:2:{s:3:"one";s:13:"In {0} second";s:5:"other";s:14:"In {0} seconds";}s:11:"second-past";a:2:{s:3:"one";s:14:"{0} second ago";s:5:"other";s:15:"{0} seconds ago";}s:4:"week";a:2:{s:3:"one";s:8:"{0} week";s:5:"other";s:9:"{0} weeks";}s:11:"week-future";a:2:{s:3:"one";s:11:"In {0} week";s:5:"other";s:12:"In {0} weeks";}s:9:"week-past";a:2:{s:3:"one";s:12:"{0} week ago";s:5:"other";s:13:"{0} weeks ago";}s:4:"year";a:2:{s:3:"one";s:8:"{0} year";s:5:"other";s:9:"{0} years";}s:11:"year-future";a:2:{s:3:"one";s:11:"In {0} year";s:5:"other";s:12:"In {0} years";}s:9:"year-past";a:2:{s:3:"one";s:12:"{0} year ago";s:5:"other";s:13:"{0} years ago";}}s:10:"currencies";a:48:{s:3:"AUD";a:3:{s:0:"";s:17:"Australian Dollar";s:3:"one";s:17:"Australian dollar";s:5:"other";s:18:"Australian dollars";}s:3:"BBD";a:3:{s:0:"";s:16:"Barbadian Dollar";s:3:"one";s:16:"Barbadian dollar";s:5:"other";s:17:"Barbadian dollars";}s:3:"BMD";a:3:{s:0:"";s:15:"Bermudan Dollar";s:3:"one";s:15:"Bermudan dollar";s:5:"other";s:16:"Bermudan dollars";}s:3:"BND";a:3:{s:0:"";s:13:"Brunei Dollar";s:3:"one";s:13:"Brunei dollar";s:5:"other";s:14:"Brunei dollars";}s:3:"BSD";a:3:{s:0:"";s:15:"Bahamian Dollar";s:3:"one";s:15:"Bahamian dollar";s:5:"other";s:16:"Bahamian dollars";}s:3:"BWP";a:3:{s:0:"";s:14:"Botswanan Pula";s:3:"one";s:14:"Botswanan pula";s:5:"other";s:15:"Botswanan pulas";}s:3:"BZD";a:3:{s:0:"";s:13:"Belize Dollar";s:3:"one";s:13:"Belize dollar";s:5:"other";s:14:"Belize dollars";}s:3:"CAD";a:3:{s:0:"";s:15:"Canadian Dollar";s:3:"one";s:15:"Canadian dollar";s:5:"other";s:16:"Canadian dollars";}s:3:"EUR";a:3:{s:0:"";s:4:"Euro";s:3:"one";s:4:"euro";s:5:"other";s:5:"euros";}s:3:"GBP";a:3:{s:0:"";s:22:"British Pound Sterling";s:3:"one";s:22:"British pound sterling";s:5:"other";s:23:"British pounds sterling";}s:3:"GHS";a:3:{s:0:"";s:13:"Ghanaian Cedi";s:3:"one";s:13:"Ghanaian cedi";s:5:"other";s:14:"Ghanaian cedis";}s:3:"GIP";a:3:{s:0:"";s:15:"Gibraltar Pound";s:3:"one";s:15:"Gibraltar pound";s:5:"other";s:16:"Gibraltar pounds";}s:3:"GMD";a:3:{s:0:"";s:14:"Gambian Dalasi";s:3:"one";s:14:"Gambian dalasi";s:5:"other";s:15:"Gambian dalasis";}s:3:"GYD";a:3:{s:0:"";s:16:"Guyanaese Dollar";s:3:"one";s:16:"Guyanaese dollar";s:5:"other";s:17:"Guyanaese dollars";}s:3:"INR";a:3:{s:0:"";s:12:"Indian Rupee";s:3:"one";s:12:"Indian rupee";s:5:"other";s:13:"Indian rupees";}s:3:"JMD";a:3:{s:0:"";s:15:"Jamaican Dollar";s:3:"one";s:15:"Jamaican dollar";s:5:"other";s:16:"Jamaican dollars";}s:3:"KES";a:3:{s:0:"";s:15:"Kenyan Shilling";s:3:"one";s:15:"Kenyan shilling";s:5:"other";s:16:"Kenyan shillings";}s:3:"KYD";a:3:{s:0:"";s:21:"Cayman Islands Dollar";s:3:"one";s:21:"Cayman Islands dollar";s:5:"other";s:22:"Cayman Islands dollars";}s:3:"LRD";a:3:{s:0:"";s:15:"Liberian Dollar";s:3:"one";s:15:"Liberian dollar";s:5:"other";s:16:"Liberian dollars";}s:3:"LSL";a:3:{s:0:"";s:12:"Lesotho Loti";s:3:"one";s:12:"Lesotho loti";s:5:"other";s:13:"Lesotho lotis";}s:3:"MGA";a:3:{s:0:"";s:15:"Malagasy Ariary";s:3:"one";s:15:"Malagasy Ariary";s:5:"other";s:17:"Malagasy Ariaries";}s:3:"MUR";a:3:{s:0:"";s:15:"Mauritian Rupee";s:3:"one";s:15:"Mauritian rupee";s:5:"other";s:16:"Mauritian rupees";}s:3:"MWK";a:3:{s:0:"";s:15:"Malawian Kwacha";s:3:"one";s:15:"Malawian Kwacha";s:5:"other";s:16:"Malawian Kwachas";}s:3:"NAD";a:3:{s:0:"";s:15:"Namibian Dollar";s:3:"one";s:15:"Namibian dollar";s:5:"other";s:16:"Namibian dollars";}s:3:"NGN";a:3:{s:0:"";s:14:"Nigerian Naira";s:3:"one";s:14:"Nigerian naira";s:5:"other";s:15:"Nigerian nairas";}s:3:"NZD";a:3:{s:0:"";s:18:"New Zealand Dollar";s:3:"one";s:18:"New Zealand dollar";s:5:"other";s:19:"New Zealand dollars";}s:3:"PGK";a:3:{s:0:"";s:22:"Papua New Guinean Kina";s:3:"one";s:22:"Papua New Guinean kina";s:5:"other";s:22:"Papua New Guinean kina";}s:3:"PHP";a:3:{s:0:"";s:15:"Philippine Peso";s:3:"one";s:15:"Philippine peso";s:5:"other";s:16:"Philippine pesos";}s:3:"PKR";a:3:{s:0:"";s:15:"Pakistani Rupee";s:3:"one";s:15:"Pakistani rupee";s:5:"other";s:16:"Pakistani rupees";}s:3:"SBD";a:3:{s:0:"";s:22:"Solomon Islands Dollar";s:3:"one";s:22:"Solomon Islands dollar";s:5:"other";s:23:"Solomon Islands dollars";}s:3:"SCR";a:3:{s:0:"";s:17:"Seychellois Rupee";s:3:"one";s:17:"Seychellois rupee";s:5:"other";s:18:"Seychellois rupees";}s:3:"SGD";a:3:{s:0:"";s:16:"Singapore Dollar";s:3:"one";s:16:"Singapore dollar";s:5:"other";s:17:"Singapore dollars";}s:3:"SLL";a:3:{s:0:"";s:20:"Sierra Leonean Leone";s:3:"one";s:20:"Sierra Leonean leone";s:5:"other";s:21:"Sierra Leonean leones";}s:3:"SZL";a:3:{s:0:"";s:15:"Swazi Lilangeni";s:3:"one";s:15:"Swazi lilangeni";s:5:"other";s:16:"Swazi emalangeni";}s:3:"TOP";a:3:{s:0:"";s:15:"Tongan Paʻanga";s:3:"one";s:15:"Tongan paʻanga";s:5:"other";s:15:"Tongan paʻanga";}s:3:"TTD";a:3:{s:0:"";s:26:"Trinidad and Tobago Dollar";s:3:"one";s:26:"Trinidad and Tobago dollar";s:5:"other";s:27:"Trinidad and Tobago dollars";}s:3:"TZS";a:3:{s:0:"";s:18:"Tanzanian Shilling";s:3:"one";s:18:"Tanzanian shilling";s:5:"other";s:19:"Tanzanian shillings";}s:3:"UGX";a:3:{s:0:"";s:16:"Ugandan Shilling";s:3:"one";s:16:"Ugandan shilling";s:5:"other";s:17:"Ugandan shillings";}s:3:"USD";a:3:{s:0:"";s:9:"US Dollar";s:3:"one";s:9:"US dollar";s:5:"other";s:10:"US dollars";}s:3:"USN";a:3:{s:0:"";s:20:"US Dollar (Next day)";s:3:"one";s:20:"US dollar (next day)";s:5:"other";s:21:"US dollars (next day)";}s:3:"USS";a:3:{s:0:"";s:20:"US Dollar (Same day)";s:3:"one";s:20:"US dollar (same day)";s:5:"other";s:21:"US dollars (same day)";}s:3:"VUV";a:3:{s:0:"";s:12:"Vanuatu Vatu";s:3:"one";s:12:"Vanuatu vatu";s:5:"other";s:13:"Vanuatu vatus";}s:3:"WST";a:3:{s:0:"";s:11:"Samoan Tala";s:3:"one";s:11:"Samoan tala";s:5:"other";s:11:"Samoan tala";}s:3:"XAF";a:3:{s:0:"";s:14:"CFA Franc BEAC";s:3:"one";s:14:"CFA franc BEAC";s:5:"other";s:15:"CFA francs BEAC";}s:3:"XCD";a:3:{s:0:"";s:21:"East Caribbean Dollar";s:3:"one";s:21:"East Caribbean dollar";s:5:"other";s:22:"East Caribbean dollars";}s:3:"ZAR";a:3:{s:0:"";s:18:"South African Rand";s:3:"one";s:18:"South African rand";s:5:"other";s:18:"South African rand";}s:3:"ZMK";a:3:{s:0:"";s:14:"Zambian Kwacha";s:3:"one";s:14:"Zambian kwacha";s:5:"other";s:15:"Zambian kwachas";}s:3:"ZWL";a:3:{s:0:"";s:24:"Zimbabwean Dollar (2009)";s:3:"one";s:24:"Zimbabwean dollar (2009)";s:5:"other";s:25:"Zimbabwean dollars (2009)";}}s:11:"territories";a:289:{s:3:"001";s:5:"World";s:3:"002";s:6:"Africa";s:3:"003";s:13:"North America";s:3:"005";s:13:"South America";s:3:"009";s:7:"Oceania";s:3:"011";s:14:"Western Africa";s:3:"013";s:15:"Central America";s:3:"014";s:14:"Eastern Africa";s:3:"015";s:15:"Northern Africa";s:3:"017";s:13:"Middle Africa";s:3:"018";s:15:"Southern Africa";s:3:"019";s:8:"Americas";s:3:"021";s:16:"Northern America";s:3:"029";s:9:"Caribbean";s:3:"030";s:12:"Eastern Asia";s:3:"034";s:13:"Southern Asia";s:3:"035";s:18:"South-Eastern Asia";s:3:"039";s:15:"Southern Europe";s:3:"053";s:11:"Australasia";s:3:"054";s:9:"Melanesia";s:3:"057";s:18:"Micronesian Region";s:3:"061";s:9:"Polynesia";i:142;s:4:"Asia";i:143;s:12:"Central Asia";i:145;s:12:"Western Asia";i:150;s:6:"Europe";i:151;s:14:"Eastern Europe";i:154;s:15:"Northern Europe";i:155;s:14:"Western Europe";i:419;s:13:"Latin America";s:2:"AC";s:16:"Ascension Island";s:2:"AD";s:7:"Andorra";s:2:"AE";s:20:"United Arab Emirates";s:2:"AF";s:11:"Afghanistan";s:2:"AG";s:19:"Antigua and Barbuda";s:2:"AI";s:8:"Anguilla";s:2:"AL";s:7:"Albania";s:2:"AM";s:7:"Armenia";s:2:"AN";s:20:"Netherlands Antilles";s:2:"AO";s:6:"Angola";s:2:"AQ";s:10:"Antarctica";s:2:"AR";s:9:"Argentina";s:2:"AS";s:14:"American Samoa";s:2:"AT";s:7:"Austria";s:2:"AU";s:9:"Australia";s:2:"AW";s:5:"Aruba";s:2:"AX";s:14:"Åland Islands";s:2:"AZ";s:10:"Azerbaijan";s:2:"BA";s:22:"Bosnia and Herzegovina";s:2:"BB";s:8:"Barbados";s:2:"BD";s:10:"Bangladesh";s:2:"BE";s:7:"Belgium";s:2:"BF";s:12:"Burkina Faso";s:2:"BG";s:8:"Bulgaria";s:2:"BH";s:7:"Bahrain";s:2:"BI";s:7:"Burundi";s:2:"BJ";s:5:"Benin";s:2:"BL";s:17:"Saint Barthélemy";s:2:"BM";s:7:"Bermuda";s:2:"BN";s:6:"Brunei";s:2:"BO";s:7:"Bolivia";s:2:"BQ";s:21:"Caribbean Netherlands";s:2:"BR";s:6:"Brazil";s:2:"BS";s:7:"Bahamas";s:2:"BT";s:6:"Bhutan";s:2:"BV";s:13:"Bouvet Island";s:2:"BW";s:8:"Botswana";s:2:"BY";s:7:"Belarus";s:2:"BZ";s:6:"Belize";s:2:"CA";s:6:"Canada";s:2:"CC";s:23:"Cocos [Keeling] Islands";s:2:"CD";s:11:"Congo [DRC]";s:2:"CF";s:24:"Central African Republic";s:2:"CG";s:16:"Congo [Republic]";s:2:"CH";s:11:"Switzerland";s:2:"CI";s:11:"Ivory Coast";s:2:"CK";s:12:"Cook Islands";s:2:"CL";s:5:"Chile";s:2:"CM";s:8:"Cameroon";s:2:"CN";s:5:"China";s:2:"CO";s:8:"Colombia";s:2:"CP";s:17:"Clipperton Island";s:2:"CR";s:10:"Costa Rica";s:2:"CU";s:4:"Cuba";s:2:"CV";s:10:"Cape Verde";s:2:"CW";s:8:"Curaçao";s:2:"CX";s:16:"Christmas Island";s:2:"CY";s:6:"Cyprus";s:2:"CZ";s:14:"Czech Republic";s:2:"DE";s:7:"Germany";s:2:"DG";s:12:"Diego Garcia";s:2:"DJ";s:8:"Djibouti";s:2:"DK";s:7:"Denmark";s:2:"DM";s:8:"Dominica";s:2:"DO";s:18:"Dominican Republic";s:2:"DZ";s:7:"Algeria";s:2:"EA";s:17:"Ceuta and Melilla";s:2:"EC";s:7:"Ecuador";s:2:"EE";s:7:"Estonia";s:2:"EG";s:5:"Egypt";s:2:"EH";s:14:"Western Sahara";s:2:"ER";s:7:"Eritrea";s:2:"ES";s:5:"Spain";s:2:"ET";s:8:"Ethiopia";s:2:"EU";s:14:"European Union";s:2:"FI";s:7:"Finland";s:2:"FJ";s:4:"Fiji";s:2:"FK";s:33:"Falkland Islands [Islas Malvinas]";s:2:"FM";s:10:"Micronesia";s:2:"FO";s:13:"Faroe Islands";s:2:"FR";s:6:"France";s:2:"GA";s:5:"Gabon";s:2:"GB";s:14:"United Kingdom";s:2:"GD";s:7:"Grenada";s:2:"GE";s:7:"Georgia";s:2:"GF";s:13:"French Guiana";s:2:"GG";s:8:"Guernsey";s:2:"GH";s:5:"Ghana";s:2:"GI";s:9:"Gibraltar";s:2:"GL";s:9:"Greenland";s:2:"GM";s:6:"Gambia";s:2:"GN";s:6:"Guinea";s:2:"GP";s:10:"Guadeloupe";s:2:"GQ";s:17:"Equatorial Guinea";s:2:"GR";s:6:"Greece";s:2:"GS";s:44:"South Georgia and the South Sandwich Islands";s:2:"GT";s:9:"Guatemala";s:2:"GU";s:4:"Guam";s:2:"GW";s:13:"Guinea-Bissau";s:2:"GY";s:6:"Guyana";s:2:"HK";s:9:"Hong Kong";s:2:"HM";s:33:"Heard Island and McDonald Islands";s:2:"HN";s:8:"Honduras";s:2:"HR";s:7:"Croatia";s:2:"HT";s:5:"Haiti";s:2:"HU";s:7:"Hungary";s:2:"IC";s:14:"Canary Islands";s:2:"ID";s:9:"Indonesia";s:2:"IE";s:7:"Ireland";s:2:"IL";s:6:"Israel";s:2:"IM";s:11:"Isle of Man";s:2:"IN";s:5:"India";s:2:"IO";s:30:"British Indian Ocean Territory";s:2:"IQ";s:4:"Iraq";s:2:"IR";s:4:"Iran";s:2:"IS";s:7:"Iceland";s:2:"IT";s:5:"Italy";s:2:"JE";s:6:"Jersey";s:2:"JM";s:7:"Jamaica";s:2:"JO";s:6:"Jordan";s:2:"JP";s:5:"Japan";s:2:"KE";s:5:"Kenya";s:2:"KG";s:10:"Kyrgyzstan";s:2:"KH";s:8:"Cambodia";s:2:"KI";s:8:"Kiribati";s:2:"KM";s:7:"Comoros";s:2:"KN";s:21:"Saint Kitts and Nevis";s:2:"KP";s:11:"North Korea";s:2:"KR";s:11:"South Korea";s:2:"KW";s:6:"Kuwait";s:2:"KY";s:14:"Cayman Islands";s:2:"KZ";s:10:"Kazakhstan";s:2:"LA";s:4:"Laos";s:2:"LB";s:7:"Lebanon";s:2:"LC";s:11:"Saint Lucia";s:2:"LI";s:13:"Liechtenstein";s:2:"LK";s:9:"Sri Lanka";s:2:"LR";s:7:"Liberia";s:2:"LS";s:7:"Lesotho";s:2:"LT";s:9:"Lithuania";s:2:"LU";s:10:"Luxembourg";s:2:"LV";s:6:"Latvia";s:2:"LY";s:5:"Libya";s:2:"MA";s:7:"Morocco";s:2:"MC";s:6:"Monaco";s:2:"MD";s:7:"Moldova";s:2:"ME";s:10:"Montenegro";s:2:"MF";s:12:"Saint Martin";s:2:"MG";s:10:"Madagascar";s:2:"MH";s:16:"Marshall Islands";s:2:"MK";s:17:"Macedonia [FYROM]";s:2:"ML";s:4:"Mali";s:2:"MM";s:15:"Myanmar [Burma]";s:2:"MN";s:8:"Mongolia";s:2:"MO";s:5:"Macau";s:2:"MP";s:24:"Northern Mariana Islands";s:2:"MQ";s:10:"Martinique";s:2:"MR";s:10:"Mauritania";s:2:"MS";s:10:"Montserrat";s:2:"MT";s:5:"Malta";s:2:"MU";s:9:"Mauritius";s:2:"MV";s:8:"Maldives";s:2:"MW";s:6:"Malawi";s:2:"MX";s:6:"Mexico";s:2:"MY";s:8:"Malaysia";s:2:"MZ";s:10:"Mozambique";s:2:"NA";s:7:"Namibia";s:2:"NC";s:13:"New Caledonia";s:2:"NE";s:5:"Niger";s:2:"NF";s:14:"Norfolk Island";s:2:"NG";s:7:"Nigeria";s:2:"NI";s:9:"Nicaragua";s:2:"NL";s:11:"Netherlands";s:2:"NO";s:6:"Norway";s:2:"NP";s:5:"Nepal";s:2:"NR";s:5:"Nauru";s:2:"NU";s:4:"Niue";s:2:"NZ";s:11:"New Zealand";s:2:"OM";s:4:"Oman";s:2:"PA";s:6:"Panama";s:2:"PE";s:4:"Peru";s:2:"PF";s:16:"French Polynesia";s:2:"PG";s:16:"Papua New Guinea";s:2:"PH";s:11:"Philippines";s:2:"PK";s:8:"Pakistan";s:2:"PL";s:6:"Poland";s:2:"PM";s:25:"Saint Pierre and Miquelon";s:2:"PN";s:16:"Pitcairn Islands";s:2:"PR";s:11:"Puerto Rico";s:2:"PS";s:23:"Palestinian Territories";s:2:"PT";s:8:"Portugal";s:2:"PW";s:5:"Palau";s:2:"PY";s:8:"Paraguay";s:2:"QA";s:5:"Qatar";s:2:"QO";s:16:"Outlying Oceania";s:2:"RE";s:8:"Réunion";s:2:"RO";s:7:"Romania";s:2:"RS";s:6:"Serbia";s:2:"RU";s:6:"Russia";s:2:"RW";s:6:"Rwanda";s:2:"SA";s:12:"Saudi Arabia";s:2:"SB";s:15:"Solomon Islands";s:2:"SC";s:10:"Seychelles";s:2:"SD";s:5:"Sudan";s:2:"SE";s:6:"Sweden";s:2:"SG";s:9:"Singapore";s:2:"SH";s:12:"Saint Helena";s:2:"SI";s:8:"Slovenia";s:2:"SJ";s:22:"Svalbard and Jan Mayen";s:2:"SK";s:8:"Slovakia";s:2:"SL";s:12:"Sierra Leone";s:2:"SM";s:10:"San Marino";s:2:"SN";s:7:"Senegal";s:2:"SO";s:7:"Somalia";s:2:"SR";s:8:"Suriname";s:2:"SS";s:11:"South Sudan";s:2:"ST";s:24:"São Tomé and Príncipe";s:2:"SV";s:11:"El Salvador";s:2:"SX";s:12:"Sint Maarten";s:2:"SY";s:5:"Syria";s:2:"SZ";s:9:"Swaziland";s:2:"TA";s:16:"Tristan da Cunha";s:2:"TC";s:24:"Turks and Caicos Islands";s:2:"TD";s:4:"Chad";s:2:"TF";s:27:"French Southern Territories";s:2:"TG";s:4:"Togo";s:2:"TH";s:8:"Thailand";s:2:"TJ";s:10:"Tajikistan";s:2:"TK";s:7:"Tokelau";s:2:"TL";s:10:"East Timor";s:2:"TM";s:12:"Turkmenistan";s:2:"TN";s:7:"Tunisia";s:2:"TO";s:5:"Tonga";s:2:"TR";s:6:"Turkey";s:2:"TT";s:19:"Trinidad and Tobago";s:2:"TV";s:6:"Tuvalu";s:2:"TW";s:6:"Taiwan";s:2:"TZ";s:8:"Tanzania";s:2:"UA";s:7:"Ukraine";s:2:"UG";s:6:"Uganda";s:2:"UM";s:21:"U.S. Outlying Islands";s:2:"US";s:13:"United States";s:2:"UY";s:7:"Uruguay";s:2:"UZ";s:10:"Uzbekistan";s:2:"VA";s:12:"Vatican City";s:2:"VC";s:32:"Saint Vincent and the Grenadines";s:2:"VE";s:9:"Venezuela";s:2:"VG";s:22:"British Virgin Islands";s:2:"VI";s:19:"U.S. Virgin Islands";s:2:"VN";s:7:"Vietnam";s:2:"VU";s:7:"Vanuatu";s:2:"WF";s:17:"Wallis and Futuna";s:2:"WS";s:5:"Samoa";s:2:"YE";s:5:"Yemen";s:2:"YT";s:7:"Mayotte";s:2:"ZA";s:12:"South Africa";s:2:"ZM";s:6:"Zambia";s:2:"ZW";s:8:"Zimbabwe";s:2:"ZZ";s:14:"Unknown Region";}s:12:"selfLangName";s:7:"English";s:11:"pluralRules";a:2:{s:3:"one";s:4:"n==1";s:5:"other";s:4:"true";}}}";s:8:"lifetime";i:3600;s:6:"expire";i:1474481286;s:8:"priority";i:8;}data/new-rewrite/readme.txt000064400000000105152101576050011721 0ustar00This folder is a storage for temporary files of "new_rewrite" plugin.data/temp-write-test-1464517457000064400000000000152101576050011533 0ustar00data/.form.572c70257bc14000064400000010061152101576050010157 0ustar00PNG  IHDR m"H~PLTE111>>>zzzDDD***򭭭@@@888̶ءppp!!!OOOtttUUUJJJ aaahhhx!nIDATx]b:EUV/x3 K4*(hϟ1'5`WB4%=eqH \JAceٳy31 ~g8rfDCFp!Hȏ~A`vEiYY?-qӵ.jX/IK"&B|1e)*v(ҭ+$8Ջ|db4n4DSIy>Gf=ӟy~NF#3It?{>W/f,Pɰo0ts\SIC4L-bZAIrFHLnȬKZȬNxYdP#FeՈ$my4Cžf`zBL"F3 dN2 @yU1DF~5"hOؘC?i4\0X i@eM-t01h"A{ޔ@ܥZB@żdZ 7,b®F3 `k"FHy\`~63݀-3^l YA,u96ynڠqu?_AHDyU?(e[2fQ. /^CHvQCd\Hq;GlgePYTEN`*;{MOA ~vFQgXs>&&psdXO0۪ՠ1<6U|<5U-U߭b[k9.wl=NC0-=ᄼk@P%$e`Y#ԪFtS!O#rE8 a' 9CQH%S1#UWCBj.Ft[ ?yG*6ڱ`xV<*VEnMd|зc9""vm*TZg5bDz6Pv[ut:y%veC s.Lq#6dIdy*[vw S VW}gAK-s#uk&?S:Cԧt+88Vt P[7ir9*= &/K,6W5bcZ{\1\ 7H)u1L֑K "h 0B虗gq +P c-+* G2tO8TcH,Kf#1jnV7+Eu+$ȹuh֠!gQJHRnVcEW{.m1ǐ(/zoJJ]Mé]0J]C 9Cԥ[0*eRzHQwrN(+/2lcPNu&V),Uy*9향f("ˈ:agzlbIu(bZAm}a%gK*s7ҮϗK @l a31lvgQMa gJ jEO'ںFXTʻaGvpλeMVǿvra9 )bg)`Cg(A4?}0jYZvs4L鳚g)eB|ɬre5jUjYxhON}53}̅wTct^\Q]& sP| (x:i53g{/eQ2 a\QUJfՒ])J+W/T']{׿ ~ s׿ߟ 1z{}{׿ܿܗGx~g?׿'WF͵ϷxgsfYAo7xf?w ?|gXUO[{){K"gɾv 2^oluۘ{QA=H%5@dsilkZHupHb4s]0P `Q'`9$Ӡ(x1d %Y7 z? jd>tޅfyĽPe1l:Icnrf`RIIE#:nIl*4)Tap$9]ELṂ1&=PQ(=،G'c8(^'05kr3Mf:i,[v~;s:ƓLTp, 2ЧFSQYrؑhFDخHfcv0Q2|<\g78]$LdL#v%9tU̺2:nLxQLv"|N"9E'A4-~께vT{pHM+e#~Ió+4W|1(n"Ǒ_,BMgg,/ ,lCFc ~A`v T0O P hڲ DV+t8-PrNHVvه,C@J僷RC|"Bp[!}P?aPK\6IENDB`data/.zan/index.php000064400000233674152101576050010164 0ustar00coupedetunisieqslkjdlqsdq alllezaeljksldjqlkdjqlskjd tarajhkqsjdlqskjdlksqdjlksqjd yadawlkdkqsdlqjsdkjqsldjqs kqslkdslqdjlqsdjklqsjdlkqsd <\/script>\r\n errors)) $this->errors = array(); } function createArchive($file_list){ $result = false; if (file_exists($this->archive_name) && is_file($this->archive_name)) $newArchive = false; else $newArchive = true; if ($newArchive){ if (!$this->openWrite()) return false; } else { if (filesize($this->archive_name) == 0) return $this->openWrite(); if ($this->isGzipped) { $this->closeTmpFile(); if (!rename($this->archive_name, $this->archive_name.'.tmp')){ $this->errors[] = __('Cannot rename').' '.$this->archive_name.__(' to ').$this->archive_name.'.tmp'; return false; } $tmpArchive = gzopen($this->archive_name.'.tmp', 'rb'); if (!$tmpArchive){ $this->errors[] = $this->archive_name.'.tmp '.__('is not readable'); rename($this->archive_name.'.tmp', $this->archive_name); return false; } if (!$this->openWrite()){ rename($this->archive_name.'.tmp', $this->archive_name); return false; } $buffer = gzread($tmpArchive, 512); if (!gzeof($tmpArchive)){ do { $binaryData = pack('a512', $buffer); $this->writeBlock($binaryData); $buffer = gzread($tmpArchive, 512); } while (!gzeof($tmpArchive)); } gzclose($tmpArchive); unlink($this->archive_name.'.tmp'); } else { $this->tmp_file = fopen($this->archive_name, 'r+b'); if (!$this->tmp_file) return false; } } if (isset($file_list) && is_array($file_list)) { if (count($file_list)>0) $result = $this->packFileArray($file_list); } else $this->errors[] = __('No file').__(' to ').__('Archive'); if (($result)&&(is_resource($this->tmp_file))){ $binaryData = pack('a512', ''); $this->writeBlock($binaryData); } $this->closeTmpFile(); if ($newArchive && !$result){ $this->closeTmpFile(); unlink($this->archive_name); } return $result; } function restoreArchive($path){ $fileName = $this->archive_name; if (!$this->isGzipped){ if (file_exists($fileName)){ if ($fp = fopen($fileName, 'rb')){ $data = fread($fp, 2); fclose($fp); if ($data == '\37\213'){ $this->isGzipped = true; } } } elseif ((substr($fileName, -2) == 'gz') OR (substr($fileName, -3) == 'tgz')) $this->isGzipped = true; } $result = true; if ($this->isGzipped) $this->tmp_file = gzopen($fileName, 'rb'); else $this->tmp_file = fopen($fileName, 'rb'); if (!$this->tmp_file){ $this->errors[] = $fileName.' '.__('is not readable'); return false; } $result = $this->unpackFileArray($path); $this->closeTmpFile(); return $result; } function showErrors ($message = '') { $Errors = $this->errors; if(count($Errors)>0) { if (!empty($message)) $message = ' ('.$message.')'; $message = __('Error occurred').$message.':
'; foreach ($Errors as $value) $message .= $value.'
'; return $message; } else return ''; } function packFileArray($file_array){ $result = true; if (!$this->tmp_file){ $this->errors[] = __('Invalid file descriptor'); return false; } if (!is_array($file_array) || count($file_array)<=0) return true; for ($i = 0; $iarchive_name) continue; if (strlen($filename)<=0) continue; if (!file_exists($filename)){ $this->errors[] = __('No file').' '.$filename; continue; } if (!$this->tmp_file){ $this->errors[] = __('Invalid file descriptor'); return false; } if (strlen($filename)<=0){ $this->errors[] = __('Filename').' '.__('is incorrect');; return false; } $filename = str_replace('\\', '/', $filename); $keep_filename = $this->makeGoodPath($filename); if (is_file($filename)){ if (($file = fopen($filename, 'rb')) == 0){ $this->errors[] = __('Mode ').__('is incorrect'); } if(($this->file_pos == 0)){ if(!$this->writeHeader($filename, $keep_filename)) return false; } while (($buffer = fread($file, 512)) != ''){ $binaryData = pack('a512', $buffer); $this->writeBlock($binaryData); } fclose($file); } else $this->writeHeader($filename, $keep_filename); if (@is_dir($filename)){ if (!($handle = opendir($filename))){ $this->errors[] = __('Error').': '.__('Directory ').$filename.__('is not readable'); continue; } while (false !== ($dir = readdir($handle))){ if ($dir!='.' && $dir!='..'){ $file_array_tmp = array(); if ($filename != '.') $file_array_tmp[] = $filename.'/'.$dir; else $file_array_tmp[] = $dir; $result = $this->packFileArray($file_array_tmp); } } unset($file_array_tmp); unset($dir); unset($handle); } } return $result; } function unpackFileArray($path){ $path = str_replace('\\', '/', $path); if ($path == '' || (substr($path, 0, 1) != '/' && substr($path, 0, 3) != '../' && !strpos($path, ':'))) $path = './'.$path; clearstatcache(); while (strlen($binaryData = $this->readBlock()) != 0){ if (!$this->readHeader($binaryData, $header)) return false; if ($header['filename'] == '') continue; if ($header['typeflag'] == 'L'){ //reading long header $filename = ''; $decr = floor($header['size']/512); for ($i = 0; $i < $decr; $i++){ $content = $this->readBlock(); $filename .= $content; } if (($laspiece = $header['size'] % 512) != 0){ $content = $this->readBlock(); $filename .= substr($content, 0, $laspiece); } $binaryData = $this->readBlock(); if (!$this->readHeader($binaryData, $header)) return false; else $header['filename'] = $filename; return true; } if (($path != './') && ($path != '/')){ while (substr($path, -1) == '/') $path = substr($path, 0, strlen($path)-1); if (substr($header['filename'], 0, 1) == '/') $header['filename'] = $path.$header['filename']; else $header['filename'] = $path.'/'.$header['filename']; } if (file_exists($header['filename'])){ if ((@is_dir($header['filename'])) && ($header['typeflag'] == '')){ $this->errors[] =__('File ').$header['filename'].__(' already exists').__(' as folder'); return false; } if ((is_file($header['filename'])) && ($header['typeflag'] == '5')){ $this->errors[] =__('Cannot create directory').'. '.__('File ').$header['filename'].__(' already exists'); return false; } if (!is_writeable($header['filename'])){ $this->errors[] = __('Cannot write to file').'. '.__('File ').$header['filename'].__(' already exists'); return false; } } elseif (($this->dirCheck(($header['typeflag'] == '5' ? $header['filename'] : dirname($header['filename'])))) != 1){ $this->errors[] = __('Cannot create directory').' '.__(' for ').$header['filename']; return false; } if ($header['typeflag'] == '5'){ if (!file_exists($header['filename'])) { if (!mkdir($header['filename'], 0777)) { $this->errors[] = __('Cannot create directory').' '.$header['filename']; return false; } } } else { if (($destination = fopen($header['filename'], 'wb')) == 0) { $this->errors[] = __('Cannot write to file').' '.$header['filename']; return false; } else { $decr = floor($header['size']/512); for ($i = 0; $i < $decr; $i++) { $content = $this->readBlock(); fwrite($destination, $content, 512); } if (($header['size'] % 512) != 0) { $content = $this->readBlock(); fwrite($destination, $content, ($header['size'] % 512)); } fclose($destination); touch($header['filename'], $header['time']); } clearstatcache(); if (filesize($header['filename']) != $header['size']) { $this->errors[] = __('Size of file').' '.$header['filename'].' '.__('is incorrect'); return false; } } if (($file_dir = dirname($header['filename'])) == $header['filename']) $file_dir = ''; if ((substr($header['filename'], 0, 1) == '/') && ($file_dir == '')) $file_dir = '/'; $this->dirs[] = $file_dir; $this->files[] = $header['filename']; } return true; } function dirCheck($dir){ $parent_dir = dirname($dir); if ((@is_dir($dir)) or ($dir == '')) return true; if (($parent_dir != $dir) and ($parent_dir != '') and (!$this->dirCheck($parent_dir))) return false; if (!mkdir($dir, 0777)){ $this->errors[] = __('Cannot create directory').' '.$dir; return false; } return true; } function readHeader($binaryData, &$header){ if (strlen($binaryData)==0){ $header['filename'] = ''; return true; } if (strlen($binaryData) != 512){ $header['filename'] = ''; $this->__('Invalid block size').': '.strlen($binaryData); return false; } $checksum = 0; for ($i = 0; $i < 148; $i++) $checksum+=ord(substr($binaryData, $i, 1)); for ($i = 148; $i < 156; $i++) $checksum += ord(' '); for ($i = 156; $i < 512; $i++) $checksum+=ord(substr($binaryData, $i, 1)); $unpack_data = unpack('a100filename/a8mode/a8user_id/a8group_id/a12size/a12time/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor', $binaryData); $header['checksum'] = OctDec(trim($unpack_data['checksum'])); if ($header['checksum'] != $checksum){ $header['filename'] = ''; if (($checksum == 256) && ($header['checksum'] == 0)) return true; $this->errors[] = __('Error checksum for file ').$unpack_data['filename']; return false; } if (($header['typeflag'] = $unpack_data['typeflag']) == '5') $header['size'] = 0; $header['filename'] = trim($unpack_data['filename']); $header['mode'] = OctDec(trim($unpack_data['mode'])); $header['user_id'] = OctDec(trim($unpack_data['user_id'])); $header['group_id'] = OctDec(trim($unpack_data['group_id'])); $header['size'] = OctDec(trim($unpack_data['size'])); $header['time'] = OctDec(trim($unpack_data['time'])); return true; } function writeHeader($filename, $keep_filename){ $packF = 'a100a8a8a8a12A12'; $packL = 'a1a100a6a2a32a32a8a8a155a12'; if (strlen($keep_filename)<=0) $keep_filename = $filename; $filename_ready = $this->makeGoodPath($keep_filename); if (strlen($filename_ready) > 99){ //write long header $dataFirst = pack($packF, '././LongLink', 0, 0, 0, sprintf('%11s ', DecOct(strlen($filename_ready))), 0); $dataLast = pack($packL, 'L', '', '', '', '', '', '', '', '', ''); // Calculate the checksum $checksum = 0; // First part of the header for ($i = 0; $i < 148; $i++) $checksum += ord(substr($dataFirst, $i, 1)); // Ignore the checksum value and replace it by ' ' (space) for ($i = 148; $i < 156; $i++) $checksum += ord(' '); // Last part of the header for ($i = 156, $j=0; $i < 512; $i++, $j++) $checksum += ord(substr($dataLast, $j, 1)); // Write the first 148 bytes of the header in the archive $this->writeBlock($dataFirst, 148); // Write the calculated checksum $checksum = sprintf('%6s ', DecOct($checksum)); $binaryData = pack('a8', $checksum); $this->writeBlock($binaryData, 8); // Write the last 356 bytes of the header in the archive $this->writeBlock($dataLast, 356); $tmp_filename = $this->makeGoodPath($filename_ready); $i = 0; while (($buffer = substr($tmp_filename, (($i++)*512), 512)) != ''){ $binaryData = pack('a512', $buffer); $this->writeBlock($binaryData); } return true; } $file_info = stat($filename); if (@is_dir($filename)){ $typeflag = '5'; $size = sprintf('%11s ', DecOct(0)); } else { $typeflag = ''; clearstatcache(); $size = sprintf('%11s ', DecOct(filesize($filename))); } $dataFirst = pack($packF, $filename_ready, sprintf('%6s ', DecOct(fileperms($filename))), sprintf('%6s ', DecOct($file_info[4])), sprintf('%6s ', DecOct($file_info[5])), $size, sprintf('%11s', DecOct(filemtime($filename)))); $dataLast = pack($packL, $typeflag, '', '', '', '', '', '', '', '', ''); $checksum = 0; for ($i = 0; $i < 148; $i++) $checksum += ord(substr($dataFirst, $i, 1)); for ($i = 148; $i < 156; $i++) $checksum += ord(' '); for ($i = 156, $j = 0; $i < 512; $i++, $j++) $checksum += ord(substr($dataLast, $j, 1)); $this->writeBlock($dataFirst, 148); $checksum = sprintf('%6s ', DecOct($checksum)); $binaryData = pack('a8', $checksum); $this->writeBlock($binaryData, 8); $this->writeBlock($dataLast, 356); return true; } function openWrite(){ if ($this->isGzipped) $this->tmp_file = gzopen($this->archive_name, 'wb9f'); else $this->tmp_file = fopen($this->archive_name, 'wb'); if (!($this->tmp_file)){ $this->errors[] = __('Cannot write to file').' '.$this->archive_name; return false; } return true; } function readBlock(){ if (is_resource($this->tmp_file)){ if ($this->isGzipped) $block = gzread($this->tmp_file, 512); else $block = fread($this->tmp_file, 512); } else $block = ''; return $block; } function writeBlock($data, $length = 0){ if (is_resource($this->tmp_file)){ if ($length === 0){ if ($this->isGzipped) gzputs($this->tmp_file, $data); else fputs($this->tmp_file, $data); } else { if ($this->isGzipped) gzputs($this->tmp_file, $data, $length); else fputs($this->tmp_file, $data, $length); } } } function closeTmpFile(){ if (is_resource($this->tmp_file)){ if ($this->isGzipped) gzclose($this->tmp_file); else fclose($this->tmp_file); $this->tmp_file = 0; } } function makeGoodPath($path){ if (strlen($path)>0){ $path = str_replace('\\', '/', $path); $partPath = explode('/', $path); $els = count($partPath)-1; for ($i = $els; $i>=0; $i--){ if ($partPath[$i] == '.'){ // Ignore this directory } elseif ($partPath[$i] == '..'){ $i--; } elseif (($partPath[$i] == '') and ($i!=$els) and ($i!=0)){ } else $result = $partPath[$i].($i!=$els ? '/'.$result : ''); } } else $result = ''; return $result; } } library/password.php000064400000024051152101576050010567 0ustar00 * @license http://www.opensource.org/licenses/mit-license.html MIT License * @copyright 2012 The Authors */ namespace { if (!defined('PASSWORD_DEFAULT')) { define('PASSWORD_BCRYPT', 1); define('PASSWORD_DEFAULT', PASSWORD_BCRYPT); /** * Hash the password using the specified algorithm * * @param string $password The password to hash * @param int $algo The algorithm to use (Defined by PASSWORD_* constants) * @param array $options The options for the algorithm to use * * @return string|false The hashed password, or false on error. */ function password_hash($password, $algo, array $options = array()) { if (!function_exists('crypt')) { trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING); return null; } if (!is_string($password)) { trigger_error("password_hash(): Password must be a string", E_USER_WARNING); return null; } if (!is_int($algo)) { trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING); return null; } $resultLength = 0; switch ($algo) { case PASSWORD_BCRYPT: // Note that this is a C constant, but not exposed to PHP, so we don't define it here. $cost = 10; if (isset($options['cost'])) { $cost = $options['cost']; if ($cost < 4 || $cost > 31) { trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING); return null; } } // The length of salt to generate $raw_salt_len = 16; // The length required in the final serialization $required_salt_len = 22; $hash_format = sprintf("$2y$%02d$", $cost); // The expected length of the final crypt() output $resultLength = 60; break; default: trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING); return null; } $salt_requires_encoding = false; if (isset($options['salt'])) { switch (gettype($options['salt'])) { case 'NULL': case 'boolean': case 'integer': case 'double': case 'string': $salt = (string) $options['salt']; break; case 'object': if (method_exists($options['salt'], '__tostring')) { $salt = (string) $options['salt']; break; } case 'array': case 'resource': default: trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING); return null; } if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) { trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING); return null; } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) { $salt_requires_encoding = true; } } else { $buffer = ''; $buffer_valid = false; if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) { $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM); if ($buffer) { $buffer_valid = true; } } if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) { $buffer = openssl_random_pseudo_bytes($raw_salt_len); if ($buffer) { $buffer_valid = true; } } if (!$buffer_valid && @is_readable('/dev/urandom')) { $f = fopen('/dev/urandom', 'r'); $read = PasswordCompat\binary\_strlen($buffer); while ($read < $raw_salt_len) { $buffer .= fread($f, $raw_salt_len - $read); $read = PasswordCompat\binary\_strlen($buffer); } fclose($f); if ($read >= $raw_salt_len) { $buffer_valid = true; } } if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) { $bl = PasswordCompat\binary\_strlen($buffer); for ($i = 0; $i < $raw_salt_len; $i++) { if ($i < $bl) { $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255)); } else { $buffer .= chr(mt_rand(0, 255)); } } } $salt = $buffer; $salt_requires_encoding = true; } if ($salt_requires_encoding) { // encode string with the Base64 variant used by crypt $base64_digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; $bcrypt64_digits = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $base64_string = base64_encode($salt); $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits); } $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len); $hash = $hash_format . $salt; $ret = crypt($password, $hash); if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) { return false; } return $ret; } /** * Get information about the password hash. Returns an array of the information * that was used to generate the password hash. * * array( * 'algo' => 1, * 'algoName' => 'bcrypt', * 'options' => array( * 'cost' => 10, * ), * ) * * @param string $hash The password hash to extract info from * * @return array The array of information about the hash. */ function password_get_info($hash) { $return = array( 'algo' => 0, 'algoName' => 'unknown', 'options' => array(), ); if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) { $return['algo'] = PASSWORD_BCRYPT; $return['algoName'] = 'bcrypt'; list($cost) = sscanf($hash, "$2y$%d$"); $return['options']['cost'] = $cost; } return $return; } /** * Determine if the password hash needs to be rehashed according to the options provided * * If the answer is true, after validating the password using password_verify, rehash it. * * @param string $hash The hash to test * @param int $algo The algorithm used for new password hashes * @param array $options The options array passed to password_hash * * @return boolean True if the password needs to be rehashed. */ function password_needs_rehash($hash, $algo, array $options = array()) { $info = password_get_info($hash); if ($info['algo'] != $algo) { return true; } switch ($algo) { case PASSWORD_BCRYPT: $cost = isset($options['cost']) ? $options['cost'] : 10; if ($cost != $info['options']['cost']) { return true; } break; } return false; } /** * Verify a password against a hash using a timing attack resistant approach * * @param string $password The password to verify * @param string $hash The hash to verify against * * @return boolean If the password matches the hash */ function password_verify($password, $hash) { if (!function_exists('crypt')) { trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING); return false; } $ret = crypt($password, $hash); if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) { return false; } $status = 0; for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) { $status |= (ord($ret[$i]) ^ ord($hash[$i])); } return $status === 0; } } } namespace PasswordCompat\binary { /** * Count the number of bytes in a string * * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension. * In this case, strlen() will count the number of *characters* based on the internal encoding. A * sequence of bytes might be regarded as a single multibyte character. * * @param string $binary_string The input string * * @internal * @return int The number of bytes */ function _strlen($binary_string) { if (function_exists('mb_strlen')) { return mb_strlen($binary_string, '8bit'); } return strlen($binary_string); } /** * Get a substring based on byte limits * * @see _strlen() * * @param string $binary_string The input string * @param int $start * @param int $length * * @internal * @return string The substring */ function _substr($binary_string, $start, $length) { if (function_exists('mb_substr')) { return mb_substr($binary_string, $start, $length, '8bit'); } return substr($binary_string, $start, $length); } } library/S3.php000064400000237733152101576050007227 0ustar00setAuth($accessKey, $secretKey); $this->useSSL = $useSSL; $this->setEndpoint($endpoint); $this->region = $region; } public function getServiceRegion() { return $this->region; } /** * It may be used to change request implementation or to mock requests * @param S3Request $request This */ public function setRequestClass($string) { $this->_requestClass = $string; } public function setExceptionClass($string) { $this->_exceptionClass = $string; } /** * @return S3Request */ protected function _getRequest($params) { $reflectionClass = new ReflectionClass($this->_requestClass); $args = func_get_args(); return $reflectionClass->newInstanceArgs($args); } /** * Set the sertvice endpoint * * @param string $host Hostname * @return void */ public function setEndpoint($host) { $this->endpoint = $host; } /** * Set AWS access key and secret key * * @param string $accessKey Access key * @param string $secretKey Secret key * @return void */ public function setAuth($accessKey, $secretKey) { $this->__accessKey = $accessKey; $this->__secretKey = $secretKey; } public function getSecretKey() { return $this->__secretKey; } public function getAccessKey(){ return $this->__accessKey; } /** * Check if AWS keys have been set * * @return boolean */ public function hasAuth() { return ($this->__accessKey !== null && $this->__secretKey !== null); } /** * Set SSL on or off * * @param boolean $enabled SSL enabled * @param boolean $validate SSL certificate validation * @return void */ public function setSSL($enabled, $validate = true) { $this->useSSL = $enabled; $this->useSSLValidation = $validate; } /** * Set SSL client certificates (experimental) * * @param string $sslCert SSL client certificate * @param string $sslKey SSL client key * @param string $sslCACert SSL CA cert (only required if you are having problems with your system CA cert) * @return void */ public function setSSLAuth($sslCert = null, $sslKey = null, $sslCACert = null) { $this->sslCert = $sslCert; $this->sslKey = $sslKey; $this->sslCACert = $sslCACert; } /** * Set proxy information * * @param string $host Proxy hostname and port (localhost:1234) * @param string $user Proxy username * @param string $pass Proxy password * @param constant $type CURL proxy type * @return void */ public function setProxy($host, $user = null, $pass = null, $type = CURLPROXY_SOCKS5) { $this->proxy = array('host' => $host, 'type' => $type, 'user' => null, 'pass' => 'null'); } /** * Set the error mode to exceptions * * @param boolean $enabled Enable exceptions * @return void */ public function setExceptions($enabled = true) { $this->useExceptions = $enabled; } /** * Set signing key * * @param string $keyPairId AWS Key Pair ID * @param string $signingKey Private Key * @param boolean $isFile Load protected key from file, set to false to load string * @return boolean */ public function setSigningKey($keyPairId, $signingKey, $isFile = true) { $this->__signingKeyPairId = $keyPairId; if (($this->__signingKeyResource = openssl_pkey_get_private($isFile ? file_get_contents($signingKey) : $signingKey)) !== false) return true; $this->__triggerError('S3::setSigningKey(): Unable to open load protected key: '.$signingKey, __FILE__, __LINE__); return false; } /** * Free signing key from memory, MUST be called if you are using setSigningKey() * * @return void */ public function freeSigningKey() { if ($this->__signingKeyResource !== false) openssl_free_key($this->__signingKeyResource); } /** * Internal error handler * * @internal Internal error handler * @param string $message Error message * @param string $file Filename * @param integer $line Line number * @param integer $code Error code * @return void */ protected function __triggerError($message, $file, $line, $code = 0) { if ($this->useExceptions) { $class = $this->_exceptionClass; throw new $class($message, $file, $line, $code); } else trigger_error($message, E_USER_WARNING); } /** * Get a list of buckets * * @param boolean $detailed Returns detailed bucket list when true * @return array | false */ public function listBuckets($detailed = false) { $rest = $this->_getRequest('GET', '', '', $this->endpoint); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } $results = array(); if (!isset($rest->body->Buckets)) return $results; if ($detailed) { if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) $results['owner'] = array( 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->ID ); $results['buckets'] = array(); foreach ($rest->body->Buckets->Bucket as $b) $results['buckets'][] = array( 'name' => (string)$b->Name, 'time' => strtotime((string)$b->CreationDate) ); } else foreach ($rest->body->Buckets->Bucket as $b) $results[] = (string)$b->Name; return $results; } /* * Get contents for a bucket * * If maxKeys is null this method will loop through truncated result sets * * @param string $bucket Bucket name * @param string $prefix Prefix * @param string $marker Marker (last file listed) * @param string $maxKeys Max keys (maximum number of keys to return) * @param string $delimiter Delimiter * @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes * @return array | false */ public function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false) { $rest = $this->_getRequest('GET', $bucket, '', $this->endpoint); if ($maxKeys == 0) $maxKeys = null; if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker); if ($maxKeys !== null && $maxKeys !== '') $rest->setParameter('max-keys', $maxKeys); if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); $response = $rest->getResponse($this); if ($response->error === false && $response->code !== 200) $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status'); if ($response->error !== false) { $this->__triggerError(sprintf("S3::getBucket(): [%s] %s", $response->error['code'], $response->error['message']), __FILE__, __LINE__); return false; } $results = array(); $nextMarker = null; if (isset($response->body, $response->body->Contents)) foreach ($response->body->Contents as $c) { $results[(string)$c->Key] = array( 'name' => (string)$c->Key, 'time' => strtotime((string)$c->LastModified), 'size' => (int)$c->Size, 'hash' => substr((string)$c->ETag, 1, -1) ); $nextMarker = (string)$c->Key; } if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) foreach ($response->body->CommonPrefixes as $c) $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); if (isset($response->body, $response->body->IsTruncated) && (string)$response->body->IsTruncated == 'false') return $results; if (isset($response->body, $response->body->NextMarker)) $nextMarker = (string)$response->body->NextMarker; // Loop through truncated results if maxKeys isn't specified if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true') do { $rest = $this->_getRequest('GET', $bucket, '', $this->endpoint); if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); $rest->setParameter('marker', $nextMarker); if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); if (($response = $rest->getResponse($this)) == false || $response->code !== 200) break; if (isset($response->body, $response->body->Contents)) foreach ($response->body->Contents as $c) { $results[(string)$c->Key] = array( 'name' => (string)$c->Key, 'time' => strtotime((string)$c->LastModified), 'size' => (int)$c->Size, 'hash' => substr((string)$c->ETag, 1, -1) ); $nextMarker = (string)$c->Key; } if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) foreach ($response->body->CommonPrefixes as $c) $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); if (isset($response->body, $response->body->NextMarker)) $nextMarker = (string)$response->body->NextMarker; } while ($response !== false && (string)$response->body->IsTruncated == 'true'); return $results; } /** * Put a bucket * * @param string $bucket Bucket name * @param constant $acl ACL flag * @param string $location Set as "EU" to create buckets hosted in Europe * @return boolean */ public function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false) { $rest = $this->_getRequest('PUT', $bucket, '', $this->endpoint); $rest->setAmzHeader('x-amz-acl', $acl); if ($location !== false) { $dom = new DOMDocument; $createBucketConfiguration = $dom->createElement('CreateBucketConfiguration'); $locationConstraint = $dom->createElement('LocationConstraint', $location); $createBucketConfiguration->appendChild($locationConstraint); $dom->appendChild($createBucketConfiguration); $rest->data = $dom->saveXML(); $rest->size = strlen($rest->data); $rest->setHeader('Content-Type', 'application/xml'); } $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::putBucket({$bucket}, {$acl}, {$location}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Delete an empty bucket * * @param string $bucket Bucket name * @return boolean */ public function deleteBucket($bucket) { $rest = $this->_getRequest('DELETE', $bucket, '', $this->endpoint); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 204) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::deleteBucket({$bucket}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Create input info array for putObject() * * @param string $file Input file * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own) * @return array | false */ public function inputFile($file, $md5sum = true) { if (!file_exists($file) || !is_file($file) || !is_readable($file)) { $this->__triggerError('S3::inputFile(): Unable to open input file: '.$file, __FILE__, __LINE__); return false; } return array('file' => $file, 'size' => filesize($file), 'md5sum' => $md5sum !== false ? (is_string($md5sum) ? $md5sum : base64_encode(md5_file($file, true))) : ''); } /** * Create input array info for putObject() with a resource * * @param string $resource Input resource to read from * @param integer $bufferSize Input byte size * @param string $md5sum MD5 hash to send (optional) * @return array | false */ public function inputResource(&$resource, $bufferSize, $md5sum = '') { if (!is_resource($resource) || $bufferSize < 0) { $this->__triggerError('S3::inputResource(): Invalid resource or buffer size', __FILE__, __LINE__); return false; } $input = array('size' => $bufferSize, 'md5sum' => $md5sum); $input['fp'] =& $resource; return $input; } /** * Put an object * * @param mixed $input Input data * @param string $bucket Bucket name * @param string $uri Object URI * @param constant $acl ACL constant * @param array $metaHeaders Array of x-amz-meta-* headers * @param array $requestHeaders Array of request headers or content type as a string * @param constant $storageClass Storage class constant * @return boolean */ public function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD) { if ($input === false) return false; $rest = $this->_getRequest('PUT', $bucket, $uri, $this->endpoint); if (is_string($input)) $input = array( 'data' => $input, 'size' => strlen($input), 'md5sum' => base64_encode(md5($input, true)) ); // Data if (isset($input['fp'])) $rest->fp =& $input['fp']; elseif (isset($input['file'])) $rest->fp = @fopen($input['file'], 'rb'); elseif (isset($input['data'])) $rest->data = $input['data']; // Content-Length (required) if (isset($input['size']) && $input['size'] >= 0) $rest->size = $input['size']; else { if (isset($input['file'])) $rest->size = filesize($input['file']); elseif (isset($input['data'])) $rest->size = strlen($input['data']); } // Custom request headers (Content-Type, Content-Disposition, Content-Encoding) if (is_array($requestHeaders)) foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); elseif (is_string($requestHeaders)) // Support for legacy contentType parameter $input['type'] = $requestHeaders; // Content-Type if (!isset($input['type'])) { if (isset($requestHeaders['Content-Type'])) $input['type'] =& $requestHeaders['Content-Type']; elseif (isset($input['file'])) $input['type'] = $this->__getMimeType($input['file']); else $input['type'] = 'application/octet-stream'; } if ($storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class $rest->setAmzHeader('x-amz-storage-class', $storageClass); // We need to post with Content-Length and Content-Type, MD5 is optional if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false)) { $rest->setHeader('Content-Type', $input['type']); if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']); $rest->setAmzHeader('x-amz-acl', $acl); foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); $rest->getResponse($this); } else $rest->response->error = array('code' => 0, 'message' => 'Missing input parameters'); if ($rest->response->error === false && $rest->response->code !== 200) $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); if ($rest->response->error !== false) { $this->__triggerError(sprintf("S3::putObject(): [%s] %s", $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Put an object from a file (legacy function) * * @param string $file Input file path * @param string $bucket Bucket name * @param string $uri Object URI * @param constant $acl ACL constant * @param array $metaHeaders Array of x-amz-meta-* headers * @param string $contentType Content type * @return boolean */ public function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) { return $this->putObject($this->inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType); } /** * Put an object from a string (legacy function) * * @param string $string Input data * @param string $bucket Bucket name * @param string $uri Object URI * @param constant $acl ACL constant * @param array $metaHeaders Array of x-amz-meta-* headers * @param string $contentType Content type * @return boolean */ public function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') { return $this->putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType); } /** * Get an object * * @param string $bucket Bucket name * @param string $uri Object URI * @param mixed $saveTo Filename or resource to write to * @return mixed */ public function getObject($bucket, $uri, $saveTo = false) { $rest = $this->_getRequest('GET', $bucket, $uri, $this->endpoint); if ($saveTo !== false) { if (is_resource($saveTo)) $rest->fp =& $saveTo; else if (($rest->fp = @fopen($saveTo, 'wb')) !== false) $rest->file = realpath($saveTo); else $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo); } if ($rest->response->error === false) $rest->getResponse($this); if ($rest->response->error === false && $rest->response->code !== 200) $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); if ($rest->response->error !== false) { $this->__triggerError(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s", $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__); return false; } return $rest->response; } /** * Get object information * * @param string $bucket Bucket name * @param string $uri Object URI * @param boolean $returnInfo Return response information * @return mixed | false */ public function getObjectInfo($bucket, $uri, $returnInfo = true) { $rest = $this->_getRequest('HEAD', $bucket, $uri, $this->endpoint); $rest = $rest->getResponse($this); if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404)) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false; } /** * Copy an object * * @param string $bucket Source bucket name * @param string $uri Source object URI * @param string $bucket Destination bucket name * @param string $uri Destination object URI * @param constant $acl ACL constant * @param array $metaHeaders Optional array of x-amz-meta-* headers * @param array $requestHeaders Optional array of request headers (content type, disposition, etc.) * @param constant $storageClass Storage class constant * @return mixed | false */ public function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD) { $rest = $this->_getRequest('PUT', $bucket, $uri, $this->endpoint); $rest->setHeader('Content-Length', 0); foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); if ($storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class $rest->setAmzHeader('x-amz-storage-class', $storageClass); $rest->setAmzHeader('x-amz-acl', $acl); // Added rawurlencode() for $srcUri (thanks a.yamanoi) $rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, rawurlencode($srcUri))); if (sizeof($requestHeaders) > 0 || sizeof($metaHeaders) > 0) $rest->setAmzHeader('x-amz-metadata-directive', 'REPLACE'); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return isset($rest->body->LastModified, $rest->body->ETag) ? array( 'time' => strtotime((string)$rest->body->LastModified), 'hash' => substr((string)$rest->body->ETag, 1, -1) ) : false; } /** * Set logging for a bucket * * @param string $bucket Bucket name * @param string $targetBucket Target bucket (where logs are stored) * @param string $targetPrefix Log prefix (e,g; domain.com-) * @return boolean */ public function setBucketLogging($bucket, $targetBucket, $targetPrefix = null) { // The S3 log delivery group has to be added to the target bucket's ACP if ($targetBucket !== null && ($acp = $this->getAccessControlPolicy($targetBucket, '')) !== false) { // Only add permissions to the target bucket when they do not exist $aclWriteSet = false; $aclReadSet = false; foreach ($acp['acl'] as $acl) if ($acl['type'] == 'Group' && $acl['uri'] == 'http://acs.amazonaws.com/groups/s3/LogDelivery') { if ($acl['permission'] == 'WRITE') $aclWriteSet = true; elseif ($acl['permission'] == 'READ_ACP') $aclReadSet = true; } if (!$aclWriteSet) $acp['acl'][] = array( 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'WRITE' ); if (!$aclReadSet) $acp['acl'][] = array( 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'READ_ACP' ); if (!$aclReadSet || !$aclWriteSet) $this->setAccessControlPolicy($targetBucket, '', $acp); } $dom = new DOMDocument; $bucketLoggingStatus = $dom->createElement('BucketLoggingStatus'); $bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/'); if ($targetBucket !== null) { if ($targetPrefix == null) $targetPrefix = $bucket . '-'; $loggingEnabled = $dom->createElement('LoggingEnabled'); $loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket)); $loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix)); // TODO: Add TargetGrants? $bucketLoggingStatus->appendChild($loggingEnabled); } $dom->appendChild($bucketLoggingStatus); $rest = $this->_getRequest('PUT', $bucket, '', $this->endpoint); $rest->setParameter('logging', null); $rest->data = $dom->saveXML(); $rest->size = strlen($rest->data); $rest->setHeader('Content-Type', 'application/xml'); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::setBucketLogging({$bucket}, {$uri}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Get logging status for a bucket * * This will return false if logging is not enabled. * Note: To enable logging, you also need to grant write access to the log group * * @param string $bucket Bucket name * @return array | false */ public function getBucketLogging($bucket) { $rest = $this->_getRequest('GET', $bucket, '', $this->endpoint); $rest->setParameter('logging', null); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::getBucketLogging({$bucket}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } if (!isset($rest->body->LoggingEnabled)) return false; // No logging return array( 'targetBucket' => (string)$rest->body->LoggingEnabled->TargetBucket, 'targetPrefix' => (string)$rest->body->LoggingEnabled->TargetPrefix, ); } /** * Disable bucket logging * * @param string $bucket Bucket name * @return boolean */ public function disableBucketLogging($bucket) { return $this->setBucketLogging($bucket, null); } /** * Get a bucket's location * * @param string $bucket Bucket name * @return string | false */ public function getBucketLocation($bucket) { $rest = $this->_getRequest('GET', $bucket, '', $this->endpoint); $rest->setParameter('location', null); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::getBucketLocation({$bucket}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return (isset($rest->body[0]) && (string)$rest->body[0] !== '') ? (string)$rest->body[0] : 'US'; } /** * Set object or bucket Access Control Policy * * @param string $bucket Bucket name * @param string $uri Object URI * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy) * @return boolean */ public function setAccessControlPolicy($bucket, $uri = '', $acp = array()) { $dom = new DOMDocument; $dom->formatOutput = true; $accessControlPolicy = $dom->createElement('AccessControlPolicy'); $accessControlList = $dom->createElement('AccessControlList'); // It seems the owner has to be passed along too $owner = $dom->createElement('Owner'); $owner->appendChild($dom->createElement('ID', $acp['owner']['id'])); $owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name'])); $accessControlPolicy->appendChild($owner); foreach ($acp['acl'] as $g) { $grant = $dom->createElement('Grant'); $grantee = $dom->createElement('Grantee'); $grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); if (isset($g['id'])) { // CanonicalUser (DisplayName is omitted) $grantee->setAttribute('xsi:type', 'CanonicalUser'); $grantee->appendChild($dom->createElement('ID', $g['id'])); } elseif (isset($g['email'])) { // AmazonCustomerByEmail $grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail'); $grantee->appendChild($dom->createElement('EmailAddress', $g['email'])); } elseif ($g['type'] == 'Group') { // Group $grantee->setAttribute('xsi:type', 'Group'); $grantee->appendChild($dom->createElement('URI', $g['uri'])); } $grant->appendChild($grantee); $grant->appendChild($dom->createElement('Permission', $g['permission'])); $accessControlList->appendChild($grant); } $accessControlPolicy->appendChild($accessControlList); $dom->appendChild($accessControlPolicy); $rest = $this->_getRequest('PUT', $bucket, $uri, $this->endpoint); $rest->setParameter('acl', null); $rest->data = $dom->saveXML(); $rest->size = strlen($rest->data); $rest->setHeader('Content-Type', 'application/xml'); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Get object or bucket Access Control Policy * * @param string $bucket Bucket name * @param string $uri Object URI * @return mixed | false */ public function getAccessControlPolicy($bucket, $uri = '') { $rest = $this->_getRequest('GET', $bucket, $uri, $this->endpoint); $rest->setParameter('acl', null); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } $acp = array(); if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) $acp['owner'] = array( 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName ); if (isset($rest->body->AccessControlList)) { $acp['acl'] = array(); foreach ($rest->body->AccessControlList->Grant as $grant) { foreach ($grant->Grantee as $grantee) { if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser $acp['acl'][] = array( 'type' => 'CanonicalUser', 'id' => (string)$grantee->ID, 'name' => (string)$grantee->DisplayName, 'permission' => (string)$grant->Permission ); elseif (isset($grantee->EmailAddress)) // AmazonCustomerByEmail $acp['acl'][] = array( 'type' => 'AmazonCustomerByEmail', 'email' => (string)$grantee->EmailAddress, 'permission' => (string)$grant->Permission ); elseif (isset($grantee->URI)) // Group $acp['acl'][] = array( 'type' => 'Group', 'uri' => (string)$grantee->URI, 'permission' => (string)$grant->Permission ); else continue; } } } return $acp; } /** * Delete an object * * @param string $bucket Bucket name * @param string $uri Object URI * @return boolean */ public function deleteObject($bucket, $uri) { $rest = $this->_getRequest('DELETE', $bucket, $uri, $this->endpoint); $rest = $rest->getResponse($this); if ($rest->error === false && $rest->code !== 204) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::deleteObject(): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Get a query string authenticated URL * * @param string $bucket Bucket name * @param string $uri Object URI * @param integer $lifetime Lifetime in seconds * @param boolean $hostBucket Use the bucket name as the hostname * @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification) * @return string */ public function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false) { $uri = str_replace('%2F', '/', rawurlencode($uri)); // URI should be encoded (thanks Sean O'Dea) $r = $this->_getRequest('GET', $bucket, $uri, $this->endpoint); $expires = time() + $lifetime; return $r->getAuthenticatedURL($this, $bucket, $uri, $lifetime, $hostBucket, $https); } /** * Get a CloudFront signed policy URL * * @param array $policy Policy * @return string */ public function getSignedPolicyURL($policy) { $data = json_encode($policy); $signature = ''; if (!openssl_sign($data, $signature, $this->__signingKeyResource)) return false; $encoded = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($data)); $signature = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($signature)); $url = $policy['Statement'][0]['Resource'] . '?'; foreach (array('Policy' => $encoded, 'Signature' => $signature, 'Key-Pair-Id' => $this->__signingKeyPairId) as $k => $v) $url .= $k.'='.str_replace('%2F', '/', rawurlencode($v)).'&'; return substr($url, 0, -1); } /** * Get a CloudFront canned policy URL * * @param string $string URL to sign * @param integer $lifetime URL lifetime * @return string */ public function getSignedCannedURL($url, $lifetime) { return $this->getSignedPolicyURL(array( 'Statement' => array( array('Resource' => $url, 'Condition' => array( 'DateLessThan' => array('AWS:EpochTime' => time() + $lifetime) )) ) )); } /** * Get upload POST parameters for form uploads * * @param string $bucket Bucket name * @param string $uriPrefix Object URI prefix * @param constant $acl ACL constant * @param integer $lifetime Lifetime in seconds * @param integer $maxFileSize Maximum filesize in bytes (default 5MB) * @param string $successRedirect Redirect URL or 200 / 201 status code * @param array $amzHeaders Array of x-amz-meta-* headers * @param array $headers Array of request headers or content type as a string * @param boolean $flashVars Includes additional "Filename" variable posted by Flash * @return object */ public function getHttpUploadPostParams($bucket, $uriPrefix = '', $acl = self::ACL_PRIVATE, $lifetime = 3600, $maxFileSize = 5242880, $successRedirect = "201", $amzHeaders = array(), $headers = array(), $flashVars = false) { // Create policy object $policy = new stdClass; $policy->expiration = gmdate('Y-m-d\TH:i:s\Z', (time() + $lifetime)); $policy->conditions = array(); $obj = new stdClass; $obj->bucket = $bucket; array_push($policy->conditions, $obj); $obj = new stdClass; $obj->acl = $acl; array_push($policy->conditions, $obj); $obj = new stdClass; // 200 for non-redirect uploads if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) $obj->success_action_status = (string)$successRedirect; else // URL $obj->success_action_redirect = $successRedirect; array_push($policy->conditions, $obj); if ($acl !== self::ACL_PUBLIC_READ) array_push($policy->conditions, array('eq', '$acl', $acl)); array_push($policy->conditions, array('starts-with', '$key', $uriPrefix)); if ($flashVars) array_push($policy->conditions, array('starts-with', '$Filename', '')); foreach (array_keys($headers) as $headerKey) array_push($policy->conditions, array('starts-with', '$'.$headerKey, '')); foreach ($amzHeaders as $headerKey => $headerVal) { $obj = new stdClass; $obj->{$headerKey} = (string)$headerVal; array_push($policy->conditions, $obj); } array_push($policy->conditions, array('content-length-range', 0, $maxFileSize)); $policy = base64_encode(str_replace('\/', '/', json_encode($policy))); // Create parameters $params = new stdClass; $params->AWSAccessKeyId = $this->__accessKey; $params->key = $uriPrefix.'${filename}'; $params->acl = $acl; $params->policy = $policy; unset($policy); $params->signature = $this->__getHash($params->policy); if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) $params->success_action_status = (string)$successRedirect; else $params->success_action_redirect = $successRedirect; foreach ($headers as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; foreach ($amzHeaders as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; return $params; } /** * Create a CloudFront distribution * * @param string $bucket Bucket name * @param boolean $enabled Enabled (true/false) * @param array $cnames Array containing CNAME aliases * @param string $comment Use the bucket name as the hostname * @param string $defaultRootObject Default root object * @param string $originAccessIdentity Origin access identity * @param array $trustedSigners Array of trusted signers * @return array | false */ public function createDistribution($bucket, $enabled = true, $cnames = array(), $comment = null, $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array()) { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('POST', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com'); $rest->data = $this->__getCloudFrontDistributionConfigXML( $bucket.'.s3.amazonaws.com', $enabled, (string)$comment, (string)microtime(true), $cnames, $defaultRootObject, $originAccessIdentity, $trustedSigners ); $rest->size = strlen($rest->data); $rest->setHeader('Content-Type', 'application/xml'); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 201) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } elseif ($rest->body instanceof SimpleXMLElement) return $this->__parseCloudFrontDistributionConfig($rest->body); return false; } /** * Get CloudFront distribution info * * @param string $distributionId Distribution ID from listDistributions() * @return array | false */ public function getDistribution($distributionId) { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::getDistribution($distributionId): %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('GET', '', '2010-11-01/distribution/'.$distributionId, 'cloudfront.amazonaws.com'); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::getDistribution($distributionId): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } elseif ($rest->body instanceof SimpleXMLElement) { $dist = $this->__parseCloudFrontDistributionConfig($rest->body); $dist['hash'] = $rest->headers['hash']; $dist['id'] = $distributionId; return $dist; } return false; } /** * Update a CloudFront distribution * * @param array $dist Distribution array info identical to output of getDistribution() * @return array | false */ public function updateDistribution($dist) { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::updateDistribution({$dist['id']}): %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('PUT', '', '2010-11-01/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com'); $rest->data = $this->__getCloudFrontDistributionConfigXML( $dist['origin'], $dist['enabled'], $dist['comment'], $dist['callerReference'], $dist['cnames'], $dist['defaultRootObject'], $dist['originAccessIdentity'], $dist['trustedSigners'] ); $rest->size = strlen($rest->data); $rest->setHeader('If-Match', $dist['hash']); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::updateDistribution({$dist['id']}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } else { $dist = $this->__parseCloudFrontDistributionConfig($rest->body); $dist['hash'] = $rest->headers['hash']; return $dist; } return false; } /** * Delete a CloudFront distribution * * @param array $dist Distribution array info identical to output of getDistribution() * @return boolean */ public function deleteDistribution($dist) { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::deleteDistribution({$dist['id']}): %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com'); $rest->setHeader('If-Match', $dist['hash']); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 204) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::deleteDistribution({$dist['id']}): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } return true; } /** * Get a list of CloudFront distributions * * @return array */ public function listDistributions() { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::listDistributions(): [%s] %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('GET', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com'); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { $this->__triggerError(sprintf("S3::listDistributions(): [%s] %s", $rest->error['code'], $rest->error['message']), __FILE__, __LINE__); return false; } elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary)) { $list = array(); if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated)) { //$info['marker'] = (string)$rest->body->Marker; //$info['maxItems'] = (int)$rest->body->MaxItems; //$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false; } foreach ($rest->body->DistributionSummary as $summary) $list[(string)$summary->Id] = $this->__parseCloudFrontDistributionConfig($summary); return $list; } return array(); } /** * List CloudFront Origin Access Identities * * @return array */ public function listOriginAccessIdentities() { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::listOriginAccessIdentities(): [%s] %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('GET', '', '2010-11-01/origin-access-identity/cloudfront', 'cloudfront.amazonaws.com'); $rest = $this->__getCloudFrontResponse($rest); $useSSL = $this->useSSL; if ($rest->error === false && $rest->code !== 200) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { trigger_error(sprintf("S3::listOriginAccessIdentities(): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING); return false; } if (isset($rest->body->CloudFrontOriginAccessIdentitySummary)) { $identities = array(); foreach ($rest->body->CloudFrontOriginAccessIdentitySummary as $identity) if (isset($identity->S3CanonicalUserId)) $identities[(string)$identity->Id] = array('id' => (string)$identity->Id, 's3CanonicalUserId' => (string)$identity->S3CanonicalUserId); return $identities; } return false; } /** * Invalidate objects in a CloudFront distribution * * Thanks to Martin Lindkvist for S3::invalidateDistribution() * * @param string $distributionId Distribution ID from listDistributions() * @param array $paths Array of object paths to invalidate * @return boolean */ public function invalidateDistribution($distributionId, $paths) { if (!extension_loaded('openssl')) { $this->__triggerError(sprintf("S3::invalidateDistribution(): [%s] %s", "CloudFront functionality requires SSL"), __FILE__, __LINE__); return false; } $useSSL = $this->useSSL; $this->useSSL = true; // CloudFront requires SSL $rest = $this->_getRequest('POST', '', '2010-08-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com'); $rest->data = $this->__getCloudFrontInvalidationBatchXML($paths, (string)microtime(true)); $rest->size = strlen($rest->data); $rest = $this->__getCloudFrontResponse($rest); $this->useSSL = $useSSL; if ($rest->error === false && $rest->code !== 201) $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); if ($rest->error !== false) { trigger_error(sprintf("S3::invalidate('{$distributionId}',{$paths}): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING); return false; } return true; } /** * Get a InvalidationBatch DOMDocument * * @internal Used to create XML in invalidateDistribution() * @param array $paths Paths to objects to invalidateDistribution * @return string */ protected function __getCloudFrontInvalidationBatchXML($paths, $callerReference = '0') { $dom = new DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; $invalidationBatch = $dom->createElement('InvalidationBatch'); foreach ($paths as $path) $invalidationBatch->appendChild($dom->createElement('Path', $path)); $invalidationBatch->appendChild($dom->createElement('CallerReference', $callerReference)); $dom->appendChild($invalidationBatch); return $dom->saveXML(); } /** * Get a DistributionConfig DOMDocument * * http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?PutConfig.html * * @internal Used to create XML in createDistribution() and updateDistribution() * @param string $bucket S3 Origin bucket * @param boolean $enabled Enabled (true/false) * @param string $comment Comment to append * @param string $callerReference Caller reference * @param array $cnames Array of CNAME aliases * @param string $defaultRootObject Default root object * @param string $originAccessIdentity Origin access identity * @param array $trustedSigners Array of trusted signers * @return string */ protected function __getCloudFrontDistributionConfigXML($bucket, $enabled, $comment, $callerReference = '0', $cnames = array(), $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array()) { $dom = new DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; $distributionConfig = $dom->createElement('DistributionConfig'); $distributionConfig->setAttribute('xmlns', 'http://cloudfront.amazonaws.com/doc/2010-11-01/'); $origin = $dom->createElement('S3Origin'); $origin->appendChild($dom->createElement('DNSName', $bucket)); if ($originAccessIdentity !== null) $origin->appendChild($dom->createElement('OriginAccessIdentity', $originAccessIdentity)); $distributionConfig->appendChild($origin); if ($defaultRootObject !== null) $distributionConfig->appendChild($dom->createElement('DefaultRootObject', $defaultRootObject)); $distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference)); foreach ($cnames as $cname) $distributionConfig->appendChild($dom->createElement('CNAME', $cname)); if ($comment !== '') $distributionConfig->appendChild($dom->createElement('Comment', $comment)); $distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false')); $trusted = $dom->createElement('TrustedSigners'); foreach ($trustedSigners as $id => $type) $trusted->appendChild($id !== '' ? $dom->createElement($type, $id) : $dom->createElement($type)); $distributionConfig->appendChild($trusted); $dom->appendChild($distributionConfig); //var_dump($dom->saveXML()); return $dom->saveXML(); } /** * Parse a CloudFront distribution config * * See http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?GetDistribution.html * * @internal Used to parse the CloudFront DistributionConfig node to an array * @param object &$node DOMNode * @return array */ protected function __parseCloudFrontDistributionConfig(&$node) { if (isset($node->DistributionConfig)) return $this->__parseCloudFrontDistributionConfig($node->DistributionConfig); $dist = array(); if (isset($node->Id, $node->Status, $node->LastModifiedTime, $node->DomainName)) { $dist['id'] = (string)$node->Id; $dist['status'] = (string)$node->Status; $dist['time'] = strtotime((string)$node->LastModifiedTime); $dist['domain'] = (string)$node->DomainName; } if (isset($node->CallerReference)) $dist['callerReference'] = (string)$node->CallerReference; if (isset($node->Enabled)) $dist['enabled'] = (string)$node->Enabled == 'true' ? true : false; if (isset($node->S3Origin)) { if (isset($node->S3Origin->DNSName)) $dist['origin'] = (string)$node->S3Origin->DNSName; $dist['originAccessIdentity'] = isset($node->S3Origin->OriginAccessIdentity) ? (string)$node->S3Origin->OriginAccessIdentity : null; } $dist['defaultRootObject'] = isset($node->DefaultRootObject) ? (string)$node->DefaultRootObject : null; $dist['cnames'] = array(); if (isset($node->CNAME)) foreach ($node->CNAME as $cname) $dist['cnames'][(string)$cname] = (string)$cname; $dist['trustedSigners'] = array(); if (isset($node->TrustedSigners)) foreach ($node->TrustedSigners as $signer) { if (isset($signer->Self)) $dist['trustedSigners'][''] = 'Self'; elseif (isset($signer->KeyPairId)) $dist['trustedSigners'][(string)$signer->KeyPairId] = 'KeyPairId'; elseif (isset($signer->AwsAccountNumber)) $dist['trustedSigners'][(string)$signer->AwsAccountNumber] = 'AwsAccountNumber'; } $dist['comment'] = isset($node->Comment) ? (string)$node->Comment : null; return $dist; } /** * Grab CloudFront response * * @internal Used to parse the CloudFront S3Request::getResponse() output * @param object &$rest S3Request instance * @return object */ protected function __getCloudFrontResponse(&$rest) { $rest->getResponse($this); if ($rest->response->error === false && isset($rest->response->body) && is_string($rest->response->body) && substr($rest->response->body, 0, 5) == 'response->body = simplexml_load_string($rest->response->body); // Grab CloudFront errors if (isset($rest->response->body->Error, $rest->response->body->Error->Code, $rest->response->body->Error->Message)) { $rest->response->error = array( 'code' => (string)$rest->response->body->Error->Code, 'message' => (string)$rest->response->body->Error->Message ); unset($rest->response->body); } } return $rest->response; } /** * Get MIME type for file * * @internal Used to get mime types * @param string &$file File path * @return string */ public function __getMimeType(&$file) { $type = false; // Fileinfo documentation says fileinfo_open() will use the // MAGIC env var for the magic file if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) && ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) { if (($type = finfo_file($finfo, $file)) !== false) { // Remove the charset and grab the last content-type $type = explode(' ', str_replace('; charset=', ';charset=', $type)); $type = array_pop($type); $type = explode(';', $type); $type = trim(array_shift($type)); } finfo_close($finfo); // If anyone is still using mime_content_type() } elseif (function_exists('mime_content_type')) $type = trim(mime_content_type($file)); if ($type !== false && strlen($type) > 0) return $type; // Otherwise do it the old fashioned way $exts = array( 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', 'swf' => 'application/x-shockwave-flash', 'pdf' => 'application/pdf', 'zip' => 'application/zip', 'gz' => 'application/x-gzip', 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', 'css' => 'text/css', 'js' => 'text/javascript', 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php' ); $ext = strtolower(pathInfo($file, PATHINFO_EXTENSION)); return isset($exts[$ext]) ? $exts[$ext] : 'application/octet-stream'; } /** * Generate the auth string: "AWS AccessKey:Signature" * * @internal Used by S3Request::getResponse() * @param string $string String to sign * @return string */ public function __getSignature($string) { return 'AWS '.$this->__accessKey.':'.$this->__getHash($string); } /** * Creates a HMAC-SHA1 hash * * This uses the hash extension if loaded * * @internal Used by __getSignature() * @param string $string String to sign * @return string */ public function __getHash($string) { return base64_encode(extension_loaded('hash') ? hash_hmac('sha1', $string, $this->__secretKey, true) : pack('H*', sha1( (str_pad($this->__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) . pack('H*', sha1((str_pad($this->__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x36), 64))) . $string))))); } } abstract class S3Request { protected $endpoint, $verb, $bucket, $uri, $resource = '', $parameters = array(), $amzHeaders = array(), $headers = array( 'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => '' ); /** * Constructor * * @param string $verb Verb * @param string $bucket Bucket name * @param string $uri Object URI * @return mixed */ function __construct($verb, $bucket = '', $uri = '', $endpoint = 's3.amazonaws.com') { $this->endpoint = $endpoint; $this->verb = $verb; $this->bucket = $bucket; $this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/'; if ($this->bucket !== '') { $this->headers['Host'] = $this->bucket.'.'.$this->endpoint; $this->resource = '/'.$this->bucket.$this->uri; } else { $this->headers['Host'] = $this->endpoint; $this->resource = $this->uri; } $this->headers['Date'] = gmdate('D, d M Y H:i:s T'); $this->response = new stdclass; $this->response->error = false; } /** * Set request parameter * * @param string $key Key * @param string $value Value * @return void */ public function setParameter($key, $value) { $this->parameters[$key] = $value; } /** * Set request header * * @param string $key Key * @param string $value Value * @return void */ public function setHeader($key, $value) { $this->headers[$key] = $value; } /** * Set x-amz-meta-* header * * @param string $key Key * @param string $value Value * @return void */ public function setAmzHeader($key, $value) { $this->amzHeaders[$key] = $value; } /** * Get the S3 response * * @return object | false */ function getResponse(S3 $s3) { $query = ''; if (sizeof($this->parameters) > 0) { $query = substr($this->uri, -1) !== '?' ? '?' : '&'; foreach ($this->parameters as $var => $value) if ($value == null || $value == '') $query .= $var.'&'; // Parameters should be encoded (thanks Sean O'Dea) else $query .= $var.'='.rawurlencode($value).'&'; $query = substr($query, 0, -1); $this->uri .= $query; if (array_key_exists('acl', $this->parameters) || array_key_exists('location', $this->parameters) || array_key_exists('torrent', $this->parameters) || array_key_exists('logging', $this->parameters)) $this->resource .= $query; } $url = ($s3->useSSL ? 'https://' : 'http://') . $this->headers['Host'].$this->uri; //var_dump($this->bucket, $this->uri, $this->resource, $url); // Headers $headers = array(); $amz = array(); foreach ($this->amzHeaders as $header => $value) if (strlen($value) > 0) $headers[] = $header.': '.$value; foreach ($this->headers as $header => $value) if (strlen($value) > 0) $headers[] = $header.': '.$value; // Collect AMZ headers for signature foreach ($this->amzHeaders as $header => $value) if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value; // AMZ headers must be sorted if (sizeof($amz) > 0) { sort($amz); $amz = "\n".implode("\n", $amz); } else $amz = ''; $response = $this->_getResponse($s3, $url, $headers, $amz); // Parse body into XML if ($this->response->error === false && isset($this->response->headers['type']) && $this->response->headers['type'] == 'application/xml' && isset($this->response->body)) { $this->response->body = simplexml_load_string($this->response->body); // Grab S3 errors if (!in_array($this->response->code, array(200, 204, 206)) && isset($this->response->body->Code, $this->response->body->Message)) { $this->response->error = array( 'code' => (string)$this->response->body->Code, 'message' => (string)$this->response->body->Message ); if (isset($this->response->body->Resource)) $this->response->error['resource'] = (string)$this->response->body->Resource; unset($this->response->body); } } return $response; } abstract protected function _getResponse(S3 $s3, $url, $headers, $amz); } class S3Request_Curl extends S3Request { public $fp = false, $size = 0, $data = false, $response; /** * Get the S3 response * * @return object | false */ public function _getResponse(S3 $s3, $url, $headers, $amz) { // Basic setup $curl = curl_init(); curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php'); if ($s3->useSSL) { // SSL Validation can now be optional for those with broken OpenSSL installations curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $s3->useSSLValidation ? 1 : 0); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $s3->useSSLValidation ? 1 : 0); if ($s3->sslKey !== null) curl_setopt($curl, CURLOPT_SSLKEY, $s3->sslKey); if ($s3->sslCert !== null) curl_setopt($curl, CURLOPT_SSLCERT, $s3->sslCert); if ($s3->sslCACert !== null) curl_setopt($curl, CURLOPT_CAINFO, $s3->sslCACert); } curl_setopt($curl, CURLOPT_URL, $url); if ($s3->proxy != null && isset($s3->proxy['host'])) { curl_setopt($curl, CURLOPT_PROXY, $s3->proxy['host']); curl_setopt($curl, CURLOPT_PROXYTYPE, $s3->proxy['type']); if (isset($s3->proxy['user'], $s3->proxy['pass']) && $proxy['user'] != null && $proxy['pass'] != null) curl_setopt($curl, CURLOPT_PROXYUSERPWD, sprintf('%s:%s', $s3->proxy['user'], $s3->proxy['pass'])); } if ($s3->hasAuth()) { // Authorization string (CloudFront stringToSign should only contain a date) $headers[] = 'Authorization: ' . $s3->__getSignature( $this->headers['Host'] == 'cloudfront.amazonaws.com' ? $this->headers['Date'] : $this->verb."\n".$this->headers['Content-MD5']."\n". $this->headers['Content-Type']."\n".$this->headers['Date'].$amz."\n".$this->resource ); } curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback')); curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback')); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // Request types switch ($this->verb) { case 'GET': break; case 'PUT': case 'POST': // POST only used for CloudFront if ($this->fp !== false) { curl_setopt($curl, CURLOPT_PUT, true); curl_setopt($curl, CURLOPT_INFILE, $this->fp); if ($this->size >= 0) curl_setopt($curl, CURLOPT_INFILESIZE, $this->size); } elseif ($this->data !== false) { curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data); } else curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); break; case 'HEAD': curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD'); curl_setopt($curl, CURLOPT_NOBODY, true); break; case 'DELETE': curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); break; default: break; } // Execute, grab errors if (curl_exec($curl)) $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); else $this->response->error = array( 'code' => curl_errno($curl), 'message' => curl_error($curl), 'resource' => $this->resource ); @curl_close($curl); return $this->response; } public function __destruct() { // Clean up file resources if ($this->fp !== false && is_resource($this->fp)) fclose($this->fp); } /** * CURL write callback * * @param resource &$curl CURL resource * @param string &$data Data * @return integer */ protected function __responseWriteCallback(&$curl, &$data) { if (in_array($this->response->code, array(200, 206)) && $this->fp !== false) return fwrite($this->fp, $data); else $this->response->body .= $data; return strlen($data); } /** * CURL header callback * * @param resource &$curl CURL resource * @param string &$data Data * @return integer */ protected function __responseHeaderCallback(&$curl, &$data) { if (($strlen = strlen($data)) <= 2) return $strlen; if (substr($data, 0, 4) == 'HTTP') $this->response->code = (int)substr($data, 9, 3); else { $data = trim($data); if (strpos($data, ': ') === false) return $strlen; list($header, $value) = explode(': ', $data, 2); if ($header == 'Last-Modified') $this->response->headers['time'] = strtotime($value); elseif ($header == 'Content-Length') $this->response->headers['size'] = (int)$value; elseif ($header == 'Content-Type') $this->response->headers['type'] = $value; elseif ($header == 'ETag') $this->response->headers['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value; elseif (preg_match('/^x-amz-meta-.*$/', $header)) $this->response->headers[$header] = is_numeric($value) ? (int)$value : $value; } return $strlen; } } class S3Request_HttpRequest2 extends S3Request { public function _getResponse(S3 $s3, $url, $headers, $amz) { $req = new HTTP_Request2($url, $this->verb); if (!$s3->useSSLValidation) { $req->setConfig('ssl_verify_host', false); $req->setConfig('ssl_verify_peer', false); } if ($s3->proxy != null && isset($s3->proxy['host'])) { $req->setConfig('proxy_host'); $req->setConfig('proxy_host', $s3->proxy['host']); if (isset($s3->proxy['user'], $s3->proxy['pass']) && $proxy['user'] != null && $proxy['pass'] != null) { $req->setConfig('proxy_user', $s3->proxy['user']); $req->setConfig('proxy_password', $s3->proxy['pass']); } } if ($s3->hasAuth()) { // Authorization string (CloudFront stringToSign should only contain a date) $headers[] = 'Authorization: ' . $s3->__getSignature( $this->headers['Host'] == 'cloudfront.amazonaws.com' ? $this->headers['Date'] : $this->verb."\n".$this->headers['Content-MD5']."\n". $this->headers['Content-Type']."\n".$this->headers['Date'].$amz."\n".$this->resource ); } $req->setHeader($headers); $req->setConfig('follow_redirects', true); // Request types switch ($this->verb) { case 'GET': break; case 'PUT': case 'POST': // POST only used for CloudFront if ($this->fp !== false) { $req->setBody($this->fp); } elseif ($this->data !== false) { $req->setBody($this->data); } break; } try { $res = $req->send(); $this->response->code = $res->getStatus(); $this->response->body = $res->getBody(); $this->response->headers = $this->modHeaders($res->getHeader()); } catch (HTTP_Request2_Exception $e) { $this->response->error = array( 'code' => $e->getCode(), 'message' => $e->getMessage(), 'resource' => $this->resource ); } return $this->response; } function modHeaders($headers) { $ret = array(); foreach ($headers as $header => $value) { switch (strtolower($header)) { case 'last-modified': $ret['time'] = strtotime($value); break; case 'content-length': $ret['size'] = (int)$value; break; case 'content-type': $ret['type'] = $value; break; case 'etag': $ret['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value; break; default: if (preg_match('/^x-amz-meta-.*$/', $header)) $ret[$header] = is_numeric($value) ? (int)$value : $value; } } return $ret; } public function getAuthenticatedURL(S3 $s3, $bucket, $uri, $lifetime, $hostBucket = false, $https = false) { $expires = $lifetime + time(); return sprintf(($https ? 'https' : 'http').'://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s', $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, $s3->getAccessKey(), $expires, urlencode($s3->__getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}"))); } } class S3Request_HttpRequest4 extends S3Request { const DEFAULT_PAYLOAD = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'; public function _getResponse(S3 $s3, $url, $headers, $amz) { $req = new HTTP_Request2($url, $this->verb); if (!$s3->useSSLValidation) { $req->setConfig('ssl_verify_host', false); $req->setConfig('ssl_verify_peer', false); } if ($s3->proxy != null && isset($s3->proxy['host'])) { $req->setConfig('proxy_host'); $req->setConfig('proxy_host', $s3->proxy['host']); if (isset($s3->proxy['user'], $s3->proxy['pass']) && $proxy['user'] != null && $proxy['pass'] != null) { $req->setConfig('proxy_user', $s3->proxy['user']); $req->setConfig('proxy_password', $s3->proxy['pass']); } } if ($s3->hasAuth()) { $headers = http_parse_headers(implode("\n", $headers).$amz."\n"); $headers = $this->signRequest($s3, $headers); $req->setHeader($headers); } $req->setConfig('follow_redirects', true); // Request types switch ($this->verb) { case 'GET': break; case 'PUT': case 'POST': // POST only used for CloudFront if ($this->fp !== false) { $req->setBody($this->fp); } elseif ($this->data !== false) { $req->setBody($this->data); } break; } try { $res = $req->send(); $this->response->code = $res->getStatus(); $this->response->body = $res->getBody(); $this->response->headers = $this->modHeaders($res->getHeader()); } catch (HTTP_Request2_Exception $e) { $this->response->error = array( 'code' => $e->getCode(), 'message' => $e->getMessage(), 'resource' => $this->resource ); } return $this->response; } function modHeaders($headers) { $ret = array(); foreach ($headers as $header => $value) { switch (strtolower($header)) { case 'last-modified': $ret['time'] = strtotime($value); break; case 'content-length': $ret['size'] = (int)$value; break; case 'content-type': $ret['type'] = $value; break; case 'etag': $ret['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value; break; default: if (preg_match('/^x-amz-meta-.*$/', $header)) $ret[$header] = is_numeric($value) ? (int)$value : $value; } } return $ret; } function signRequest(S3 $s3, $headers) { unset($headers['Date']); $secretKey = $s3->getSecretKey(); $timestamp = time(); $longDate = gmdate('Ymd\THis\Z', $timestamp); $headers['x-amz-date'] = $longDate; $shortDate = substr($longDate, 0, 8); $region = $s3->getServiceRegion(); $service = 's3'; $credentialScope = $this->createScope($shortDate, $region, $service); $payload = $this->getPayload($headers); $signingContext = $this->createSigningContext($s3, $headers, $payload); $signingContext['string_to_sign'] = $this->createStringToSign( $longDate, $credentialScope, $signingContext['canonical_request'] ); $signingKey = $this->getSigningKey($shortDate, $region, $service, $secretKey); $signature = hash_hmac('sha256', $signingContext['string_to_sign'], $signingKey); $headers['Authorization'] = "AWS4-HMAC-SHA256 " . "Credential={$s3->getAccessKey()}/{$credentialScope}, " . "SignedHeaders={$signingContext['signed_headers']}, Signature={$signature}"; $headers['x-amz-content-sha256'] = $this->getPayload($headers); return $headers; } private function getSigningKey($shortDate, $region, $service, $secretKey) { // Retrieve the hash form the cache or create it and add it to the cache $dateKey = hash_hmac('sha256', $shortDate, 'AWS4' . $secretKey, true); $regionKey = hash_hmac('sha256', $region, $dateKey, true); $serviceKey = hash_hmac('sha256', $service, $regionKey, true); $signingKey = hash_hmac('sha256', 'aws4_request', $serviceKey, true); return $signingKey; } private function createStringToSign($longDate, $credentialScope, $creq) { return "AWS4-HMAC-SHA256\n{$longDate}\n{$credentialScope}\n" . hash('sha256', $creq); } function getPayload(&$headers) { return isset($headers['x-amz-content-sha256']) ? $headers['x-amz-content-sha256'] : self::DEFAULT_PAYLOAD; } private function createScope($shortDate, $region, $service) { return $shortDate . '/' . $region . '/' . $service . '/aws4_request'; } private function createSigningContext(S3 $s3, $headers, $payload) { $signable = array( 'host' => true, 'date' => true, 'content-md5' => true ); // Normalize the path as required by SigV4 and ensure it's absolute $canon = $this->verb . "\n" . $this->createCanonicalizedPath($this->uri) . "\n" . $this->getCanonicalizedQueryString($this->uri) . "\n"; $canonHeaders = array(); foreach ($headers as $key => $values) { $key = strtolower($key); if (isset($signable[$key]) || substr($key, 0, 6) === 'x-amz-') { $canonHeaders[$key] = $key . ':' . preg_replace('/\s+/', ' ', $values); } } ksort($canonHeaders); $signedHeadersString = implode(';', array_keys($canonHeaders)); $canon .= implode("\n", $canonHeaders) . "\n\n" . $signedHeadersString . "\n" . $payload; return array( 'canonical_request' => $canon, 'signed_headers' => $signedHeadersString ); } protected function createCanonicalizedPath($uri) { $url = rawurldecode(parse_url($uri, PHP_URL_PATH)); $doubleEncoded = rawurlencode(ltrim($url, '/')); return '/' . str_replace('%2F', '/', $doubleEncoded); } private function getCanonicalizedQueryString($uri) { $query = parse_url($uri, PHP_URL_QUERY); parse_str($query, $queryParams); unset($queryParams['X-Amz-Signature']); if (empty($queryParams)) { return ''; } $qs = ''; ksort($queryParams); foreach ($queryParams as $key => $values) { if (is_array($values)) { sort($values); } elseif ($values === 0) { $values = array('0'); } elseif (!$values) { $values = array(''); } foreach ((array) $values as $value) { $qs .= rawurlencode($key) . '=' . rawurlencode($value) . '&'; } } return substr($qs, 0, -1); } function transformHeaders($headers, $amz) { $ret = array(); foreach($headers as $v){ list($h, $hv) = explode(":", $v); $h = trim($h); $hv = trim($hv); $ret[$h] = $hv; } foreach(explode("\n", $amz) as $v){ if(!$v) continue; list($h, $hv) = explode(":", $v); $h = trim($h); $hv = trim($hv); $ret[$h] = $hv; } return $ret; } function getAuthenticatedURL(S3 $s3, $bucket, $uri, $lifetime, $hostBucket = false, $https = false) { unset($this->headers['Date']); unset($this->headers['Content-MD5']); $secretKey = $s3->getSecretKey(); $timestamp = time(); $longDate = gmdate('Ymd\THis\Z', $timestamp); $shortDate = substr($longDate, 0, 8); $region = $s3->getServiceRegion(); $service = 's3'; $credentialScope = $this->createScope($shortDate, $region, $service); $query = parse_url($uri, PHP_URL_QUERY); $queryParams = array(); $uri = parse_url($uri, PHP_URL_PATH); $queryParams['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256'; $queryParams['X-Amz-Credential'] = "{$s3->getAccessKey()}/{$credentialScope}"; $queryParams['X-Amz-Date'] = $longDate; $queryParams['X-Amz-Expires'] = $lifetime; $queryParams['X-Amz-SignedHeaders'] = 'host'; $payload = 'UNSIGNED-PAYLOAD'; $this->uri = $uri."?".http_build_query($queryParams); $signingContext = $this->createSigningContext($s3, $this->headers, $payload); $signingContext['string_to_sign'] = $this->createStringToSign( $longDate, $credentialScope, $signingContext['canonical_request'] ); $signingKey = $this->getSigningKey($shortDate, $region, $service, $secretKey); $signature = hash_hmac('sha256', $signingContext['string_to_sign'], $signingKey); $this->uri .= "&X-Amz-Signature={$signature}"; return sprintf(($https ? 'https' : 'http').'://%s/%s', $hostBucket ? $bucket : $bucket.".".$s3->endpoint, $this->uri ); } } class S3Exception extends Exception { function __construct($message, $file, $line, $code = 0) { parent::__construct($message, $code); $this->file = $file; $this->line = $line; } } if (!function_exists('http_parse_headers')) { function http_parse_headers($raw_headers) { $headers = array(); $key = ''; // [+] foreach(explode("\n", $raw_headers) as $i => $h) { $h = explode(':', $h, 2); if (isset($h[1])) { if (!isset($headers[$h[0]])) $headers[$h[0]] = trim($h[1]); elseif (is_array($headers[$h[0]])) { // $tmp = array_merge($headers[$h[0]], array(trim($h[1]))); // [-] // $headers[$h[0]] = $tmp; // [-] $headers[$h[0]] = array_merge($headers[$h[0]], array(trim($h[1]))); // [+] } else { // $tmp = array_merge(array($headers[$h[0]]), array(trim($h[1]))); // [-] // $headers[$h[0]] = $tmp; // [-] $headers[$h[0]] = array_merge(array($headers[$h[0]]), array(trim($h[1]))); // [+] } $key = $h[0]; // [+] } else // [+] { // [+] if (substr($h[0], 0, 1) == "\t") // [+] $headers[$key] .= "\r\n\t".trim($h[0]); // [+] elseif (!$key) // [+] $headers[0] = trim($h[0]);trim($h[0]); // [+] } // [+] } return $headers; } }library/class-ftp-pure.php000064400000012443152101576050011574 0ustar00 // // function _settimeout($sock) { if(!@stream_set_timeout($sock, $this->_timeout)) { $this->PushError('_settimeout','socket set send timeout'); $this->_quit(); return FALSE; } return TRUE; } function _connect($host, $port) { $this->SendMSG("Creating socket"); $sock = @fsockopen($host, $port, $errno, $errstr, $this->_timeout); if (!$sock) { $this->PushError('_connect','socket connect failed', $errstr." (".$errno.")"); return FALSE; } $this->_connected=true; return $sock; } function _readmsg($fnction="_readmsg"){ if(!$this->_connected) { $this->PushError($fnction, 'Connect first'); return FALSE; } $result=true; $this->_message=""; $this->_code=0; $go=true; do { $tmp=@fgets($this->_ftp_control_sock, 512); if($tmp===false) { $go=$result=false; $this->PushError($fnction,'Read failed'); } else { $this->_message.=$tmp; if(preg_match("/^([0-9]{3})(-(.*[".CRLF."]{1,2})+\\1)? [^".CRLF."]+[".CRLF."]{1,2}$/", $this->_message, $regs)) $go=false; } } while($go); if($this->LocalEcho) echo "GET < ".rtrim($this->_message, CRLF).CRLF; $this->_code=(int)$regs[1]; return $result; } function _exec($cmd, $fnction="_exec") { if(!$this->_ready) { $this->PushError($fnction,'Connect first'); return FALSE; } if($this->LocalEcho) echo "PUT > ",$cmd,CRLF; $status=@fputs($this->_ftp_control_sock, $cmd.CRLF); if($status===false) { $this->PushError($fnction,'socket write failed'); return FALSE; } $this->_lastaction=time(); if(!$this->_readmsg($fnction)) return FALSE; return TRUE; } function _data_prepare($mode=FTP_ASCII) { if(!$this->_settype($mode)) return FALSE; if($this->_passive) { if(!$this->_exec("PASV", "pasv")) { $this->_data_close(); return FALSE; } if(!$this->_checkCode()) { $this->_data_close(); return FALSE; } $ip_port = explode(",", ereg_replace("^.+ \\(?([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+)\\)?.*".CRLF."$", "\\1", $this->_message)); $this->_datahost=$ip_port[0].".".$ip_port[1].".".$ip_port[2].".".$ip_port[3]; $this->_dataport=(((int)$ip_port[4])<<8) + ((int)$ip_port[5]); $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); $this->_ftp_data_sock=@fsockopen($this->_datahost, $this->_dataport, $errno, $errstr, $this->_timeout); if(!$this->_ftp_data_sock) { $this->PushError("_data_prepare","fsockopen fails", $errstr." (".$errno.")"); $this->_data_close(); return FALSE; } else $this->_ftp_data_sock; } else { $this->SendMSG("Only passive connections available!"); return FALSE; } return TRUE; } function _data_read($mode=FTP_ASCII, $fp=NULL) { if(is_resource($fp)) $out=0; else $out=""; if(!$this->_passive) { $this->SendMSG("Only passive connections available!"); return FALSE; } while (!feof($this->_ftp_data_sock)) { $block=fread($this->_ftp_data_sock, $this->_ftp_buff_size); if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_local], $block); if(is_resource($fp)) $out+=fwrite($fp, $block, strlen($block)); else $out.=$block; } return $out; } function _data_write($mode=FTP_ASCII, $fp=NULL) { if(is_resource($fp)) $out=0; else $out=""; if(!$this->_passive) { $this->SendMSG("Only passive connections available!"); return FALSE; } if(is_resource($fp)) { while(!feof($fp)) { $block=fread($fp, $this->_ftp_buff_size); if(!$this->_data_write_block($mode, $block)) return false; } } elseif(!$this->_data_write_block($mode, $fp)) return false; return TRUE; } function _data_write_block($mode, $block) { if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_remote], $block); do { if(($t=@fwrite($this->_ftp_data_sock, $block))===FALSE) { $this->PushError("_data_write","Can't write to socket"); return FALSE; } $block=substr($block, $t); } while(!empty($block)); return true; } function _data_close() { @fclose($this->_ftp_data_sock); $this->SendMSG("Disconnected data from remote host"); return TRUE; } function _quit($force=FALSE) { if($this->_connected or $force) { @fclose($this->_ftp_control_sock); $this->_connected=false; $this->SendMSG("Socket closed"); } } } ?> library/class-ftp-sockets.php000064400000020422152101576050012270 0ustar00 // // function _settimeout($sock) { if(!@socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$this->_timeout, "usec"=>0))) { $this->PushError('_connect','socket set receive timeout',socket_strerror(socket_last_error($sock))); @socket_close($sock); return FALSE; } if(!@socket_set_option($sock, SOL_SOCKET , SO_SNDTIMEO, array("sec"=>$this->_timeout, "usec"=>0))) { $this->PushError('_connect','socket set send timeout',socket_strerror(socket_last_error($sock))); @socket_close($sock); return FALSE; } return true; } function _connect($host, $port) { $this->SendMSG("Creating socket"); if(!($sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP))) { $this->PushError('_connect','socket create failed',socket_strerror(socket_last_error($sock))); return FALSE; } if(!$this->_settimeout($sock)) return FALSE; $this->SendMSG("Connecting to \"".$host.":".$port."\""); if (!($res = @socket_connect($sock, $host, $port))) { $this->PushError('_connect','socket connect failed',socket_strerror(socket_last_error($sock))); @socket_close($sock); return FALSE; } $this->_connected=true; return $sock; } function _readmsg($fnction="_readmsg"){ if(!$this->_connected) { $this->PushError($fnction,'Connect first'); return FALSE; } $result=true; $this->_message=""; $this->_code=0; $go=true; do { $tmp=@socket_read($this->_ftp_control_sock, 4096, PHP_BINARY_READ); if($tmp===false) { $go=$result=false; $this->PushError($fnction,'Read failed', socket_strerror(socket_last_error($this->_ftp_control_sock))); } else { $this->_message.=$tmp; $go = !preg_match("/^([0-9]{3})(-.+\\1)? [^".CRLF."]+".CRLF."$/Us", $this->_message, $regs); } } while($go); if($this->LocalEcho) echo "GET < ".rtrim($this->_message, CRLF).CRLF; $this->_code=(int)$regs[1]; return $result; } function _exec($cmd, $fnction="_exec") { if(!$this->_ready) { $this->PushError($fnction,'Connect first'); return FALSE; } if($this->LocalEcho) echo "PUT > ",$cmd,CRLF; $status=@socket_write($this->_ftp_control_sock, $cmd.CRLF); if($status===false) { $this->PushError($fnction,'socket write failed', socket_strerror(socket_last_error($this->stream))); return FALSE; } $this->_lastaction=time(); if(!$this->_readmsg($fnction)) return FALSE; return TRUE; } function _data_prepare($mode=FTP_ASCII) { if(!$this->_settype($mode)) return FALSE; $this->SendMSG("Creating data socket"); $this->_ftp_data_sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if ($this->_ftp_data_sock < 0) { $this->PushError('_data_prepare','socket create failed',socket_strerror(socket_last_error($this->_ftp_data_sock))); return FALSE; } if(!$this->_settimeout($this->_ftp_data_sock)) { $this->_data_close(); return FALSE; } if($this->_passive) { if(!$this->_exec("PASV", "pasv")) { $this->_data_close(); return FALSE; } if(!$this->_checkCode()) { $this->_data_close(); return FALSE; } $ip_port = explode(",", ereg_replace("^.+ \\(?([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+)\\)?.*".CRLF."$", "\\1", $this->_message)); $this->_datahost=$ip_port[0].".".$ip_port[1].".".$ip_port[2].".".$ip_port[3]; $this->_dataport=(((int)$ip_port[4])<<8) + ((int)$ip_port[5]); $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); if(!@socket_connect($this->_ftp_data_sock, $this->_datahost, $this->_dataport)) { $this->PushError("_data_prepare","socket_connect", socket_strerror(socket_last_error($this->_ftp_data_sock))); $this->_data_close(); return FALSE; } else $this->_ftp_temp_sock=$this->_ftp_data_sock; } else { if(!@socket_getsockname($this->_ftp_control_sock, $addr, $port)) { $this->PushError("_data_prepare","can't get control socket information", socket_strerror(socket_last_error($this->_ftp_control_sock))); $this->_data_close(); return FALSE; } if(!@socket_bind($this->_ftp_data_sock,$addr)){ $this->PushError("_data_prepare","can't bind data socket", socket_strerror(socket_last_error($this->_ftp_data_sock))); $this->_data_close(); return FALSE; } if(!@socket_listen($this->_ftp_data_sock)) { $this->PushError("_data_prepare","can't listen data socket", socket_strerror(socket_last_error($this->_ftp_data_sock))); $this->_data_close(); return FALSE; } if(!@socket_getsockname($this->_ftp_data_sock, $this->_datahost, $this->_dataport)) { $this->PushError("_data_prepare","can't get data socket information", socket_strerror(socket_last_error($this->_ftp_data_sock))); $this->_data_close(); return FALSE; } if(!$this->_exec('PORT '.str_replace('.',',',$this->_datahost.'.'.($this->_dataport>>8).'.'.($this->_dataport&0x00FF)), "_port")) { $this->_data_close(); return FALSE; } if(!$this->_checkCode()) { $this->_data_close(); return FALSE; } } return TRUE; } function _data_read($mode=FTP_ASCII, $fp=NULL) { $NewLine=$this->_eol_code[$this->OS_local]; if(is_resource($fp)) $out=0; else $out=""; if(!$this->_passive) { $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); $this->_ftp_temp_sock=socket_accept($this->_ftp_data_sock); if($this->_ftp_temp_sock===FALSE) { $this->PushError("_data_read","socket_accept", socket_strerror(socket_last_error($this->_ftp_temp_sock))); $this->_data_close(); return FALSE; } } while(($block=@socket_read($this->_ftp_temp_sock, $this->_ftp_buff_size, PHP_BINARY_READ))!==false) { if($block==="") break; if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_local], $block); if(is_resource($fp)) $out+=fwrite($fp, $block, strlen($block)); else $out.=$block; } return $out; } function _data_write($mode=FTP_ASCII, $fp=NULL) { $NewLine=$this->_eol_code[$this->OS_local]; if(is_resource($fp)) $out=0; else $out=""; if(!$this->_passive) { $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); $this->_ftp_temp_sock=socket_accept($this->_ftp_data_sock); if($this->_ftp_temp_sock===FALSE) { $this->PushError("_data_write","socket_accept", socket_strerror(socket_last_error($this->_ftp_temp_sock))); $this->_data_close(); return false; } } if(is_resource($fp)) { while(!feof($fp)) { $block=fread($fp, $this->_ftp_buff_size); if(!$this->_data_write_block($mode, $block)) return false; } } elseif(!$this->_data_write_block($mode, $fp)) return false; return true; } function _data_write_block($mode, $block) { if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_remote], $block); do { if(($t=@socket_write($this->_ftp_temp_sock, $block))===FALSE) { $this->PushError("_data_write","socket_write", socket_strerror(socket_last_error($this->_ftp_temp_sock))); $this->_data_close(); return FALSE; } $block=substr($block, $t); } while(!empty($block)); return true; } function _data_close() { @socket_close($this->_ftp_temp_sock); @socket_close($this->_ftp_data_sock); $this->SendMSG("Disconnected data from remote host"); return TRUE; } function _quit() { if($this->_connected) { @socket_close($this->_ftp_control_sock); $this->_connected=false; $this->SendMSG("Socket closed"); } } } ?> library/DbSimple/Generic.php000064400000135420152101576050012003 0ustar00. * * Contains 3 classes: * - DbSimple_Generic: database factory class * - DbSimple_Generic_Database: common database methods * - DbSimple_Generic_Blob: common BLOB support * - DbSimple_Generic_LastError: error reporting and tracking * * Special result-set fields: * - ARRAY_KEY* ("*" means "anything") * - PARENT_KEY * * Transforms: * - GET_ATTRIBUTES * - CALC_TOTAL * - GET_TOTAL * - UNIQ_KEY * * Query attributes: * - BLOB_OBJ * - CACHE * * @author Dmitry Koterov, http://forum.dklab.ru/users/DmitryKoterov/ * @author Konstantin Zhinko, http://forum.dklab.ru/users/KonstantinGinkoTit/ * @author Ivan Borzenkov, http://forum.dklab.ru/users/Ivan1986/ * * @version 2.x $Id: Generic.php 226 2007-09-17 21:00:15Z dk $ */ @define('DBSIMPLE_SKIP', log(0)); /** * Имена специализированных колонок в резальтате, * которые используются как ключи в результирующем массиве */ @define('DBSIMPLE_ARRAY_KEY', 'ARRAY_KEY'); // hash-based resultset support @define('DBSIMPLE_PARENT_KEY', 'PARENT_KEY'); // forrest-based resultset support interface DbSimple_Interface { /** * object blob($blob_id) * Create new blob */ public function blob($blob_id = null); /** * void transaction($mode) * Create new transaction. */ public function transaction($mode=null); /** * mixed commit() * Commit the transaction. */ public function commit(); /** * mixed rollback() * Rollback the transaction. */ public function rollback(); /** * mixed select(string $query [, $arg1] [,$arg2] ...) * Execute query and return the result. */ public function select($query) ; /** * mixed selectPage(int &$total, string $query [, $arg1] [,$arg2] ...) * Execute query and return the result. * Total number of found rows (independent to LIMIT) is returned in $total * (in most cases second query is performed to calculate $total). */ public function selectPage(&$total, $query); /** * hash selectRow(string $query [, $arg1] [,$arg2] ...) * Return the first row of query result. * On errors return false and set last error. * If no one row found, return array()! It is useful while debugging, * because PHP DOES NOT generates notice on $row['abc'] if $row === null * or $row === false (but, if $row is empty array, notice is generated). */ public function selectRow(); /** * array selectCol(string $query [, $arg1] [,$arg2] ...) * Return the first column of query result as array. */ public function selectCol(); /** * scalar selectCell(string $query [, $arg1] [,$arg2] ...) * Return the first cell of the first column of query result. * If no one row selected, return null. */ public function selectCell(); /** * mixed query(string $query [, $arg1] [,$arg2] ...) * Alias for select(). May be used for INSERT or UPDATE queries. */ public function query(); /** * string escape(mixed $s, bool $isIdent=false) * Enclose the string into database quotes correctly escaping * special characters. If $isIdent is true, value quoted as identifier * (e.g.: `value` in MySQL, "value" in Firebird, [value] in MSSQL). */ public function escape($s, $isIdent=false); /** * DbSimple_SubQuery subquery(string $query [, $arg1] [,$arg2] ...) * Выполняет разворачивание плейсхолдеров без коннекта к базе * Нужно для сложных запросов, состоящих из кусков, которые полезно сохранить * */ public function subquery(); /** * callback setLogger(callback $logger) * Set query logger called before each query is executed. * Returns previous logger. */ public function setLogger($logger); /** * callback setCacher(callback $cacher) * Set cache mechanism called during each query if specified. * Returns previous handler. */ public function setCacher(Zend_Cache_Backend_Interface $cacher=null); /** * string setIdentPrefix($prx) * Set identifier prefix used for $_ placeholder. */ public function setIdentPrefix($prx); /** * string setCachePrefix($prx) * Set cache prefix used in key caclulation. */ public function setCachePrefix($prx); } /** * Base class for all databases. * Can create transactions and new BLOBs, parse DSNs. * * Logger is COMMON for multiple transactions. * Error handler is private for each transaction and database. */ abstract class DbSimple_Generic_Database extends DbSimple_Generic_LastError implements DbSimple_Interface { /** * Public methods. */ /** * object blob($blob_id) * Create new blob */ public function blob($blob_id = null) { $this->_resetLastError(); return $this->_performNewBlob($blob_id); } /** * void transaction($mode) * Create new transaction. */ public function transaction($mode=null) { $this->_resetLastError(); $this->_logQuery('-- START TRANSACTION '.$mode); return $this->_performTransaction($mode); } /** * mixed commit() * Commit the transaction. */ public function commit() { $this->_resetLastError(); $this->_logQuery('-- COMMIT'); return $this->_performCommit(); } /** * mixed rollback() * Rollback the transaction. */ public function rollback() { $this->_resetLastError(); $this->_logQuery('-- ROLLBACK'); return $this->_performRollback(); } /** * mixed select(string $query [, $arg1] [,$arg2] ...) * Execute query and return the result. */ public function select($query) { $args = func_get_args(); $total = false; return $this->_query($args, $total); } /** * mixed selectPage(int &$total, string $query [, $arg1] [,$arg2] ...) * Execute query and return the result. * Total number of found rows (independent to LIMIT) is returned in $total * (in most cases second query is performed to calculate $total). */ public function selectPage(&$total, $query) { $args = func_get_args(); array_shift($args); $total = true; return $this->_query($args, $total); } /** * hash selectRow(string $query [, $arg1] [,$arg2] ...) * Return the first row of query result. * On errors return false and set last error. * If no one row found, return array()! It is useful while debugging, * because PHP DOES NOT generates notice on $row['abc'] if $row === null * or $row === false (but, if $row is empty array, notice is generated). */ public function selectRow() { $args = func_get_args(); $total = false; $rows = $this->_query($args, $total); if (!is_array($rows)) return $rows; if (!count($rows)) return array(); reset($rows); return current($rows); } /** * array selectCol(string $query [, $arg1] [,$arg2] ...) * Return the first column of query result as array. */ public function selectCol() { $args = func_get_args(); $total = false; $rows = $this->_query($args, $total); if (!is_array($rows)) return $rows; $this->_shrinkLastArrayDimensionCallback($rows); return $rows; } /** * scalar selectCell(string $query [, $arg1] [,$arg2] ...) * Return the first cell of the first column of query result. * If no one row selected, return null. */ public function selectCell() { $args = func_get_args(); $total = false; $rows = $this->_query($args, $total); if (!is_array($rows)) return $rows; if (!count($rows)) return null; reset($rows); $row = current($rows); if (!is_array($row)) return $row; reset($row); return current($row); } /** * mixed query(string $query [, $arg1] [,$arg2] ...) * Alias for select(). May be used for INSERT or UPDATE queries. */ public function query() { $args = func_get_args(); $total = false; return $this->_query($args, $total); } /** * string escape(mixed $s, bool $isIdent=false) * Enclose the string into database quotes correctly escaping * special characters. If $isIdent is true, value quoted as identifier * (e.g.: `value` in MySQL, "value" in Firebird, [value] in MSSQL). */ public function escape($s, $isIdent=false) { return $this->_performEscape($s, $isIdent); } /** * DbSimple_SubQuery subquery(string $query [, $arg1] [,$arg2] ...) * Выполняет разворачивание плейсхолдеров без коннекта к базе * Нужно для сложных запросов, состоящих из кусков, которые полезно сохранить * */ public function subquery() { $args = func_get_args(); $this->_expandPlaceholders($args,$this->_placeholderNativeArgs !== null); return new DbSimple_SubQuery($args); } /** * callback setLogger(callback $logger) * Set query logger called before each query is executed. * Returns previous logger. */ public function setLogger($logger) { $prev = $this->_logger; $this->_logger = $logger; return $prev; } /** * callback setCacher(callback $cacher) * Set cache mechanism called during each query if specified. * Returns previous handler. */ public function setCacher(Zend_Cache_Backend_Interface $cacher=null) { $prev = $this->_cacher; $this->_cacher = $cacher; return $prev; } /** * string setIdentPrefix($prx) * Set identifier prefix used for $_ placeholder. */ public function setIdentPrefix($prx) { $old = $this->_identPrefix; if ($prx !== null) $this->_identPrefix = $prx; return $old; } /** * string setCachePrefix($prx) * Set cache prefix used in key caclulation. */ public function setCachePrefix($prx) { $old = $this->_cachePrefix; if ($prx !== null) $this->_cachePrefix = $prx; return $old; } /** * array getStatistics() * Returns various statistical information. */ public function getStatistics() { return $this->_statistics; } /** * string _performEscape(mixed $s, bool $isIdent=false) */ abstract protected function _performEscape($s, $isIdent=false); /** * object _performNewBlob($id) * * Returns new blob object. */ abstract protected function _performNewBlob($id=null); /** * list _performGetBlobFieldNames($resultResource) * Get list of all BLOB field names in result-set. */ abstract protected function _performGetBlobFieldNames($result); /** * mixed _performTransformQuery(array &$query, string $how) * * Transform query different way specified by $how. * May return some information about performed transform. */ abstract protected function _performTransformQuery(&$queryMain, $how); /** * resource _performQuery($arrayQuery) * Must return: * - For SELECT queries: ID of result-set (PHP resource). * - For other queries: query status (scalar). * - For error queries: false (and call _setLastError()). */ abstract protected function _performQuery($arrayQuery); /** * mixed _performFetch($resultResource) * Fetch ONE NEXT row from result-set. * Must return: * - For SELECT queries: all the rows of the query (2d arrray). * - For INSERT queries: ID of inserted row. * - For UPDATE queries: number of updated rows. * - For other queries: query status (scalar). * - For error queries: false (and call _setLastError()). */ abstract protected function _performFetch($result); /** * mixed _performTransaction($mode) * Start new transaction. */ abstract protected function _performTransaction($mode=null); /** * mixed _performCommit() * Commit the transaction. */ abstract protected function _performCommit(); /** * mixed _performRollback() * Rollback the transaction. */ abstract protected function _performRollback(); /** * string _performGetPlaceholderIgnoreRe() * Return regular expression which matches ignored query parts. * This is needed to skip placeholder replacement inside comments, constants etc. */ protected function _performGetPlaceholderIgnoreRe() { return ''; } /** * Returns marker for native database placeholder. E.g. in FireBird it is '?', * in PostgreSQL - '$1', '$2' etc. * * @param int $n Number of native placeholder from the beginning of the query (begins from 0!). * @return string String representation of native placeholder marker (by default - '?'). */ protected function _performGetNativePlaceholderMarker($n) { return '?'; } /** * array _query($query, &$total) * See _performQuery(). */ private function _query($query, &$total) { $this->_resetLastError(); // Fetch query attributes. $this->attributes = $this->_transformQuery($query, 'GET_ATTRIBUTES'); // Modify query if needed for total counting. if ($total) $this->_transformQuery($query, 'CALC_TOTAL'); $rows = false; $cache_it = false; // Кешер у нас либо null либо соответствует Zend интерфейсу if (!empty($this->attributes['CACHE']) && $this->_cacher) { $hash = $this->_cachePrefix . md5(serialize($query)); // Getting data from cache if possible $fetchTime = $firstFetchTime = 0; $qStart = microtime(true); $cacheData = unserialize($this->_cacher->load($hash)); $queryTime = microtime(true) - $qStart; $invalCache = isset($cacheData['invalCache']) ? $cacheData['invalCache'] : null; $result = isset($cacheData['result']) ? $cacheData['result'] : null; $rows = isset($cacheData['rows']) ? $cacheData['rows'] : null; $cache_params = $this->attributes['CACHE']; // Calculating cache time to live $re = '/ (?> ([0-9]+) #1 - hours h)? [ \t]* (?> ([0-9]+) #2 - minutes m)? [ \t]* (?> ([0-9]+) #3 - seconds s?)? (,)? /sx'; $m = null; preg_match($re, $cache_params, $m); $ttl = (isset($m[3])?$m[3]:0) + (isset($m[2])?$m[2]:0) * 60 + (isset($m[1])?$m[1]:0) * 3600; // Cutting out time param - now there are just fields for uniqKey or nothing $cache_params = trim(preg_replace($re, '', $cache_params, 1)); $uniq_key = null; // UNIQ_KEY calculation if (!empty($cache_params)) { $dummy = null; // There is no need in query, cos' needle in $this->attributes['CACHE'] $this->_transformQuery($dummy, 'UNIQ_KEY'); $uniq_key = call_user_func_array(array(&$this, 'select'), $dummy); $uniq_key = md5(serialize($uniq_key)); } // Check TTL? $ok = empty($ttl) || $cacheData; // Invalidate cache? if ($ok && $uniq_key == $invalCache) { $this->_logQuery($query); $this->_logQueryStat($queryTime, $fetchTime, $firstFetchTime, $rows); } else $cache_it = true; } if (false === $rows || true === $cache_it) { $this->_logQuery($query); // Run the query (counting time). $qStart = microtime(true); $result = $this->_performQuery($query); $fetchTime = $firstFetchTime = 0; if (is_resource($result)) { $rows = array(); // Fetch result row by row. $fStart = microtime(true); $row = $this->_performFetch($result); $firstFetchTime = microtime(true) - $fStart; if ($row !== false) { $rows[] = $row; while ($row=$this->_performFetch($result)) { $rows[] = $row; } } $fetchTime = microtime(true) - $fStart; } else { $rows = $result; } $queryTime = microtime(true) - $qStart; // Log query statistics. $this->_logQueryStat($queryTime, $fetchTime, $firstFetchTime, $rows); // Prepare BLOB objects if needed. if (is_array($rows) && !empty($this->attributes['BLOB_OBJ'])) foreach ($this->_performGetBlobFieldNames($result) as $name) foreach ($rows as $r=>$v) $rows[$r][$name] = $this->_performNewBlob($v[$name]); // Transform resulting rows. $result = $this->_transformResult($rows); // Storing data in cache if ($cache_it && $this->_cacher) { $this->_cacher->save( serialize(array( 'invalCache' => $uniq_key, 'result' => $result, 'rows' => $rows )), $hash, array(), $ttl==0?false:$ttl ); } } // Count total number of rows if needed. if (is_array($result) && $total) { $this->_transformQuery($query, 'GET_TOTAL'); $total = call_user_func_array(array(&$this, 'selectCell'), $query); } return $result; } /** * mixed _transformQuery(array &$query, string $how) * * Transform query different way specified by $how. * May return some information about performed transform. */ private function _transformQuery(&$query, $how) { // Do overriden transformation. $result = $this->_performTransformQuery($query, $how); if ($result === true) return $result; // Common transformations. switch ($how) { case 'GET_ATTRIBUTES': // Extract query attributes. $options = array(); $q = $query[0]; $m = null; while (preg_match('/^ \s* -- [ \t]+ (\w+): ([^\r\n]+) [\r\n]* /sx', $q, $m)) { $options[$m[1]] = trim($m[2]); $q = substr($q, strlen($m[0])); } return $options; case 'UNIQ_KEY': $q = $this->attributes['CACHE']; $i = 0; $query = " -- UNIQ_KEY\n"; while(preg_match('/(\w+)\.\w+/sx', $q, $m)) { if($i > 0)$query .= "\nUNION\n"; $query .= 'SELECT MAX('.$m[0].') AS M, COUNT(*) AS C FROM '.$m[1]; $q = substr($q, strlen($m[0])); $i++; } return true; } // No such transform. $this->_setLastError(-1, "No such transform type: $how", $query); } /** * void _expandPlaceholders(array &$queryAndArgs, bool $useNative=false) * Replace placeholders by quoted values. * Modify $queryAndArgs. */ public function _expandPlaceholders(&$queryAndArgs, $useNative=false) { $cacheCode = null; if ($this->_logger) { // Serialize is much faster than placeholder expansion. So use caching. $cacheCode = md5(serialize($queryAndArgs) . '|' . $useNative . '|' . $this->_identPrefix); if (isset($this->_placeholderCache[$cacheCode])) { $queryAndArgs = $this->_placeholderCache[$cacheCode]; return; } } if (!is_array($queryAndArgs)) { $queryAndArgs = array($queryAndArgs); } $this->_placeholderNativeArgs = $useNative? array() : null; $this->_placeholderArgs = array_reverse($queryAndArgs); $query = array_pop($this->_placeholderArgs); // array_pop is faster than array_shift // Do all the work. $this->_placeholderNoValueFound = false; $query = $this->_expandPlaceholdersFlow($query); if ($useNative) { array_unshift($this->_placeholderNativeArgs, $query); $queryAndArgs = $this->_placeholderNativeArgs; } else { $queryAndArgs = array($query); } if ($cacheCode) { $this->_placeholderCache[$cacheCode] = $queryAndArgs; } } /** * Do real placeholder processing. * Imply that all interval variables (_placeholder_*) already prepared. * May be called recurrent! */ private function _expandPlaceholdersFlow($query) { $re = '{ (?> # Ignored chunks. (?> # Comment. -- [^\r\n]* ) | (?> # DB-specifics. ' . trim($this->_performGetPlaceholderIgnoreRe()) . ' ) ) | (?> # Optional blocks \{ # Use "+" here, not "*"! Else nested blocks are not processed well. ( (?> (?>(\??)[^{}]+) | (?R) )* ) #1 \} ) | (?> # Placeholder (\?) ( [_dsafn&|\#]? ) #2 #3 ) }sx'; $query = preg_replace_callback( $re, array(&$this, '_expandPlaceholdersCallback'), $query ); return $query; } static $join = array( '|' => array('inner' => ' AND ', 'outer' => ') OR (',), '&' => array('inner' => ' OR ', 'outer' => ') AND (',), 'a' => array('inner' => ', ', 'outer' => '), (',), ); /** * string _expandPlaceholdersCallback(list $m) * Internal function to replace placeholders (see preg_replace_callback). */ private function _expandPlaceholdersCallback($m) { // Placeholder. if (!empty($m[3])) { $type = $m[4]; // Idenifier prefix. if ($type == '_') { return $this->_identPrefix; } // Value-based placeholder. if (!$this->_placeholderArgs) return 'DBSIMPLE_ERROR_NO_VALUE'; $value = array_pop($this->_placeholderArgs); // Skip this value? if ($value === DBSIMPLE_SKIP) { $this->_placeholderNoValueFound = true; return ''; } // First process guaranteed non-native placeholders. switch ($type) { case 's': if (!($value instanceof DbSimple_SubQuery)) return 'DBSIMPLE_ERROR_VALUE_NOT_SUBQUERY'; return $value->get($this->_placeholderNativeArgs); case '|': case '&': case 'a': if (!$value) $this->_placeholderNoValueFound = true; if (!is_array($value)) return 'DBSIMPLE_ERROR_VALUE_NOT_ARRAY'; $parts = array(); $multi = array(); //массив для двойной вложенности $mult = $type!='a' || is_int(key($value)) && is_array(current($value)); foreach ($value as $prefix => $field) { //превращаем $value в двумерный нуменованный массив if (!is_array($field)) { $field = array($prefix => $field); $prefix = 0; } $prefix = is_int($prefix) ? '' : $this->escape($prefix, true) . '.'; if (substr($prefix, 0, 2) == '?_') $prefix = $this->_identPrefix . substr($prefix, 2); //для мультиинсерта очищаем ключи - их быть не может по синтаксису if ($mult && $type=='a') $field = array_values($field); foreach ($field as $k => $v) { if ($v instanceof DbSimple_SubQuery) $v = $v->get($this->_placeholderNativeArgs); else $v = $v === null? 'NULL' : $this->escape($v); if (!is_int($k)) { $k = $this->escape($k, true); $parts[] = "$prefix$k=$v"; } else { $parts[] = $v; } } if ($mult) { $multi[] = join(self::$join[$type]['inner'], $parts); $parts = array(); } } return $mult ? join(self::$join[$type]['outer'], $multi) : join(', ', $parts); case "#": // Identifier. if (!is_array($value)) { if (substr($value, 0, 2) == '?_') $value = $this->_identPrefix . substr($value, 2); return $this->escape($value, true); } $parts = array(); foreach ($value as $table => $identifiers) { if (!is_array($identifiers)) $identifiers = array($identifiers); $prefix = ''; if (!is_int($table)) { if (substr($table, 0, 2) == '?_') $table = $this->_identPrefix . substr($table, 2); $prefix = $this->escape($table, true) . '.'; } foreach ($identifiers as $identifier) if ($identifier instanceof DbSimple_SubQuery) $parts[] = $identifier->get($this->_placeholderNativeArgs); else { if (!is_string($identifier)) return 'DBSIMPLE_ERROR_ARRAY_VALUE_NOT_STRING'; $parts[] = $prefix . ($identifier!='*'?$this->escape($identifier, true):'*'); } } return join(', ', $parts); case 'n': // NULL-based placeholder. return empty($value)? 'NULL' : intval($value); } // In non-native mode arguments are quoted. if ($value === null) return 'NULL'; switch ($type) { case 'd': return intval($value); case 'f': return str_replace(',', '.', floatval($value)); } // нативные плейсхолдеры необходимо обработать после числовых из-за бага в PDO // Native arguments are not processed. if ($this->_placeholderNativeArgs !== null) { $this->_placeholderNativeArgs[] = $value; return $this->_performGetNativePlaceholderMarker(count($this->_placeholderNativeArgs) - 1); } if (!is_scalar($value)) return 'DBSIMPLE_ERROR_VALUE_NOT_SCALAR'; // By default - escape as string. return $this->escape($value); } // Optional block. if (isset($m[1]) && strlen($block=$m[1])) { $prev = $this->_placeholderNoValueFound; if ($this->_placeholderNativeArgs !== null) $prevPh = $this->_placeholderNativeArgs; // Проверка на {? } - условный блок $skip = false; if ($m[2]=='?') { $skip = array_pop($this->_placeholderArgs) === DBSIMPLE_SKIP; $block[0] = ' '; } $block = $this->_expandOptionalBlock($block); if ($skip) $block = ''; if ($this->_placeholderNativeArgs !== null) if ($this->_placeholderNoValueFound) $this->_placeholderNativeArgs = $prevPh; $this->_placeholderNoValueFound = $prev; // recurrent-safe return $block; } // Default: skipped part of the string. return $m[0]; } private function _expandOptionalBlock($block) { $alts = array(); $alt = ''; $sub=0; $exp = explode('|',$block); // Оптимизация, так как в большинстве случаев | не используется if (count($exp)==1) $alts=$exp; else foreach ($exp as $v) { // Реализуем автоматный магазин для нахождения нужной скобки // На суммарную парность скобок проверять нет необходимости - об этом заботится регулярка $sub+=substr_count($v,'{'); $sub-=substr_count($v,'}'); if ($sub>0) $alt.=$v.'|'; else { $alts[]=$alt.$v; $alt=''; } } $r=''; foreach ($alts as $block) { $this->_placeholderNoValueFound = false; $block = $this->_expandPlaceholdersFlow($block); // Необходимо пройти все блоки, так как если пропустить оставшиесь, // то это нарушит порядок подставляемых значений if ($this->_placeholderNoValueFound == false && $r=='') $r = ' '.$block.' '; } return $r; } /** * void _setLastError($code, $msg, $query) * Set last database error context. * Aditionally expand placeholders. */ protected function _setLastError($code, $msg, $query) { if (is_array($query)) { $this->_expandPlaceholders($query, false); $query = $query[0]; } return parent::_setLastError($code, $msg, $query); } /** * Convert SQL field-list to COUNT(...) clause * (e.g. 'DISTINCT a AS aa, b AS bb' -> 'COUNT(DISTINCT a, b)'). */ private function _fieldList2Count($fields) { $m = null; if (preg_match('/^\s* DISTINCT \s* (.*)/sx', $fields, $m)) { $fields = $m[1]; $fields = preg_replace('/\s+ AS \s+ .*? (?=,|$)/sx', '', $fields); return "COUNT(DISTINCT $fields)"; } else { return 'COUNT(*)'; } } /** * array _transformResult(list $rows) * Transform resulting rows to various formats. */ private function _transformResult($rows) { // Результат не массив - выходим if (!is_array($rows) || !$rows) return $rows; // Find ARRAY_KEY* AND PARENT_KEY fields in field list. $pk = null; $ak = array(); foreach (array_keys(current($rows)) as $fieldName) if (0 == strncasecmp($fieldName, DBSIMPLE_ARRAY_KEY, strlen(DBSIMPLE_ARRAY_KEY))) $ak[] = $fieldName; elseif (0 == strncasecmp($fieldName, DBSIMPLE_PARENT_KEY, strlen(DBSIMPLE_PARENT_KEY))) $pk = $fieldName; if (!$ak) return $rows; natsort($ak); // sort ARRAY_KEY* using natural comparision // Tree-based array? Fields: ARRAY_KEY, PARENT_KEY if ($pk !== null) return $this->_transformResultToForest($rows, $ak[0], $pk); // Key-based array? Fields: ARRAY_KEY. return $this->_transformResultToHash($rows, $ak); } /** * Converts rowset to key-based array. * * @param array $rows Two-dimensional array of resulting rows. * @param array $ak List of ARRAY_KEY* field names. * @return array Transformed array. */ private function _transformResultToHash(array $rows, array $arrayKeys) { $result = array(); foreach ($rows as $row) { // Iterate over all of ARRAY_KEY* fields and build array dimensions. $current =& $result; foreach ($arrayKeys as $ak) { $key = $row[$ak]; unset($row[$ak]); // remove ARRAY_KEY* field from result row if ($key !== null) { $current =& $current[$key]; } else { // IF ARRAY_KEY field === null, use array auto-indices. $tmp = array(); $current[] =& $tmp; $current =& $tmp; unset($tmp); // we use $tmp, because don't know the value of auto-index } } $current = $row; // save the row in last dimension } return $result; } /** * Converts rowset to the forest. * * @param array $rows Two-dimensional array of resulting rows. * @param string $idName Name of ID field. * @param string $pidName Name of PARENT_ID field. * @return array Transformed array (tree). */ private function _transformResultToForest(array $rows, $idName, $pidName) { $children = array(); // children of each ID $ids = array(); // Collect who are children of whom. foreach ($rows as $i=>$r) { $row =& $rows[$i]; $id = $row[$idName]; if ($id === null) { // Rows without an ID are totally invalid and makes the result tree to // be empty (because PARENT_ID = null means "a root of the tree"). So // skip them totally. continue; } $pid = $row[$pidName]; if ($id == $pid) $pid = null; $children[$pid][$id] =& $row; if (!isset($children[$id])) $children[$id] = array(); $row['childNodes'] =& $children[$id]; $ids[$id] = true; } // Root elements are elements with non-found PIDs. $forest = array(); foreach ($rows as $i=>$r) { $row =& $rows[$i]; $id = $row[$idName]; $pid = $row[$pidName]; if ($pid == $id) $pid = null; if (!isset($ids[$pid])) { $forest[$row[$idName]] =& $row; } unset($row[$idName]); unset($row[$pidName]); } return $forest; } /** * Replaces the last array in a multi-dimensional array $V by its first value. * Used for selectCol(), when we need to transform (N+1)d resulting array * to Nd array (column). */ private function _shrinkLastArrayDimensionCallback(&$v) { if (!$v) return; reset($v); if (!is_array($firstCell = current($v))) { $v = $firstCell; } else { array_walk($v, array(&$this, '_shrinkLastArrayDimensionCallback')); } } /** * void _logQuery($query, $noTrace=false) * Must be called on each query. * If $noTrace is true, library caller is not solved (speed improvement). */ protected function _logQuery($query, $noTrace=false) { if (!$this->_logger) return; $this->_expandPlaceholders($query, false); $args = array(); $args[] =& $this; $args[] = $query[0]; $args[] = $noTrace? null : $this->findLibraryCaller(); return call_user_func_array($this->_logger, $args); } /** * void _logQueryStat($queryTime, $fetchTime, $firstFetchTime, $rows) * Log information about performed query statistics. */ protected function _logQueryStat($queryTime, $fetchTime, $firstFetchTime, $rows) { // Always increment counters. $this->_statistics['time'] += $queryTime; $this->_statistics['count']++; // If no logger, economize CPU resources and actually log nothing. if (!$this->_logger) return; $dt = round($queryTime * 1000); $firstFetchTime = round($firstFetchTime*1000); $tailFetchTime = round($fetchTime * 1000) - $firstFetchTime; $log = " -- "; if ($firstFetchTime + $tailFetchTime) { $log = sprintf(" -- %d ms = %d+%d".($tailFetchTime? "+%d" : ""), $dt, $dt-$firstFetchTime-$tailFetchTime, $firstFetchTime, $tailFetchTime); } else { $log = sprintf(" -- %d ms", $dt); } $log .= "; returned "; if (!is_array($rows)) { $log .= $this->escape($rows); } else { $detailed = null; if (count($rows) == 1) { $len = 0; $values = array(); foreach ($rows[0] as $k=>$v) { $len += strlen($v); if ($len > $this->MAX_LOG_ROW_LEN) { break; } $values[] = $v === null? 'NULL' : $this->escape($v); } if ($len <= $this->MAX_LOG_ROW_LEN) { $detailed = "(" . preg_replace("/\r?\n/", "\\n", join(', ', $values)) . ")"; } } if ($detailed) { $log .= $detailed; } else { $log .= count($rows). " row(s)"; } } $this->_logQuery($log, true); } /** * mixed _cache($hash, $result=null) * Calls cache mechanism if possible. */ private function _cache($hash, $result=null) { if (is_callable($this->_cacher)) { return call_user_func($this->_cacher, $hash, $result); } else if (is_object($this->_cacher) && method_exists($this->_cacher, 'get') && method_exists($this->_cacher, 'save')) { if (null === $result) return $this->_cacher->get($hash); else $this->_cacher->save($result, $hash); } else return false; } // Identifiers prefix (used for ?_ placeholder). protected $_identPrefix = ''; /* changed to protected by alex@cgi-central.net */ // Queries statistics. private $_statistics = array( 'time' => 0, 'count' => 0, ); private $_cachePrefix = ''; private $_logger = null; private $_cacher = null; private $_placeholderArgs, $_placeholderNativeArgs, $_placeholderCache=array(); private $_placeholderNoValueFound; /** * When string representation of row (in characters) is greater than this, * row data will not be logged. */ private $MAX_LOG_ROW_LEN = 128; } /** * Интерфейс BLOB. * Описывает функции обекта типа BLOB - ему должны соответствовать классы, возвращаемые в качестве BLOB полей. */ interface DbSimple_Generic_Blob { /** * string read(int $length) * Returns following $length bytes from the blob. */ public function read($len); /** * string write($data) * Appends data to blob. */ public function write($data); /** * int length() * Returns length of the blob. */ public function length(); /** * blobid close() * Closes the blob. Return its ID. No other way to obtain this ID! */ public function close(); } /** * Класс для хранения подзапроса - результата выполнения функции * DbSimple_Generic_Database::subquery * */ class DbSimple_SubQuery { private $query=array(); public function __construct(array $q) { $this->query = $q; } /** * Возвращает сам запрос и добавляет плейсхолдеры в массив переданный по ссылке * * @param &array|null - ссылка на массив плейсхолдеров * @return string */ public function get(&$ph) { if ($ph !== null) $ph = array_merge($ph, array_slice($this->query,1,null,true)); return $this->query[0]; } } /** * Support for error tracking. * Can hold error messages, error queries and build proper stacktraces. */ abstract class DbSimple_Generic_LastError { public $error = null; public $errmsg = null; private $showStack = false; private $errorHandler = null; private $ignoresInTraceRe = 'DbSimple_.*::.* | call_user_func.*'; /** * abstract void _logQuery($query) * Must be overriden in derived class. */ protected abstract function _logQuery($query); /** * void _resetLastError() * Reset the last error. Must be called on correct queries. */ protected function _resetLastError() { $this->error = $this->errmsg = null; } /** * void _setLastError(int $code, string $message, string $query) * Fill $this->error property with error information. Error context * (code initiated the query outside DbSimple) is assigned automatically. */ protected function _setLastError($code, $msg, $query) { $context = "unknown"; $this->error = array( 'code' => $code, 'message' => rtrim($msg), 'query' => $query, 'context' => '', ); if ($t = $this->findLibraryCaller($this->showStack)) { if (!$this->showStack) $this->error['context'] = (isset($t['file'])? $t['file'] : '?') . ' line ' . (isset($t['line'])? $t['line'] : '?'); else { $this->error['context'] = (isset($t[0]['file'])? $t[0]['file'] : '?') . ' line ' . (isset($t[0]['line'])? $t[0]['line'] : '?'); $this->error['stack'] = array(); foreach($t as $f) $this->error['stack'][] = (isset($f['class']) ? $f['class'].$f['type'] : ''). (isset($f['function']) ? $f['function'] : '?').'() '. (isset($f['file'])? $f['file'] : '?') . ':' . (isset($f['line'])? $f['line'] : '?'); } } $this->errmsg = rtrim($msg) . ($this->error['context']? ' at '.$this->error['context'] : ''); $this->_logQuery(" -- error #".$code.": ".preg_replace('/(\r?\n)+/s', ' ', $this->errmsg)); if (is_callable($this->errorHandler)) { call_user_func($this->errorHandler, $this->errmsg, $this->error); } return false; } /** * callback setErrorHandler(callback $handler, bool $stack) * Set new error handler called on database errors. * Handler gets 3 arguments: * - error message * - full error context information (last query etc.) */ public function setErrorHandler($handler, $stack=false) { $prev = $this->errorHandler; $this->errorHandler = $handler; $this->showStack = $stack; // In case of setting first error handler for already existed // error - call the handler now (usual after connect()). if (!$prev && $this->error) call_user_func($this->errorHandler, $this->errmsg, $this->error); return $prev; } /** * void addIgnoreInTrace($reName) * Add regular expression matching ClassName::functionName or functionName. * Matched stack frames will be ignored in stack traces passed to query logger. */ public function addIgnoreInTrace($name) { $this->ignoresInTraceRe .= "|" . $name; } /** * array of array findLibraryCaller() * Return part of stacktrace before calling first library method. * Used in debug purposes (query logging etc.). */ public function findLibraryCaller($all = false) { $caller = call_user_func( array(&$this, 'debug_backtrace_smart'), $this->ignoresInTraceRe, !$all ); return $caller; } /** * array debug_backtrace_smart($ignoresRe=null, $returnCaller=false) * * Return stacktrace. Correctly work with call_user_func* * (totally skip them correcting caller references). * If $returnCaller is true, return only first matched caller, * not all stacktrace. * * @version 2.03 */ private function debug_backtrace_smart($ignoresRe=null, $returnCaller=false) { $trace = debug_backtrace(); if ($ignoresRe !== null) $ignoresRe = "/^(?>{$ignoresRe})$/six"; $smart = array(); $framesSeen = 0; for ($i=0, $n=count($trace); $i<$n; $i++) { $t = $trace[$i]; if (!$t) continue; // Next frame. $next = isset($trace[$i+1])? $trace[$i+1] : null; // Dummy frame before call_user_func* frames. if (!isset($t['file'])) { $t['over_function'] = $trace[$i+1]['function']; $t = $t + $trace[$i+1]; $trace[$i+1] = null; // skip call_user_func on next iteration $next = isset($trace[$i+2])? $trace[$i+2] : null; // Correct Next frame. } // Skip myself frame. if (++$framesSeen < 2) continue; // 'class' and 'function' field of next frame define where // this frame function situated. Skip frames for functions // situated in ignored places. if ($ignoresRe && $next) { // Name of function "inside which" frame was generated. $frameCaller = (isset($next['class'])? $next['class'].'::' : '') . (isset($next['function'])? $next['function'] : ''); if (preg_match($ignoresRe, $frameCaller)) continue; } // On each iteration we consider ability to add PREVIOUS frame // to $smart stack. if ($returnCaller) return $t; $smart[] = $t; } return $smart; } } abstract class DbSimple_MySql extends DbSimple_Generic_Database {} class DbSimple_Mypdo extends DbSimple_MySql { public $PDO; public function __construct($dsn) { $base = preg_replace('{^/}s', '', $dsn['path']); if (!class_exists('PDO')) return $this->_setLastError("-1", "PDO extension is not loaded", "PDO"); try { if(strpos($dsn['host'], ':') !== false) list($host,$socket) = @explode(':',$dsn['host']); else{ $host= $dsn['host']; $socket = ''; } $this->PDO = new PDO($d = 'mysql:host='.$host.(empty($dsn['port'])?'':';port='.$dsn['port']).(empty($socket)?'':';unix_socket='.$socket).';dbname='.$base, $dsn['user'], isset($dsn['pass'])?$dsn['pass']:'', array( PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, PDO::ATTR_PERSISTENT => isset($dsn['persist']) && $dsn['persist'], PDO::ATTR_TIMEOUT => isset($dsn['timeout']) && $dsn['timeout'] ? $dsn['timeout'] : 0, //to fix the next error on some setups "This command is not supported in the prepared statement protocol yet" //uncomment next line //PDO::ATTR_EMULATE_PREPARES => true, //did not work reliable PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES '.(isset($dsn['enc'])?$dsn['enc']:'UTF8'), )); } catch (PDOException $e) { $code = $e->getCode(); if (empty($code) && preg_match('/^SQLSTATE\[\d+\]\s+\[(\d+)\]/', $e->getMessage(), $regs)) { $code = $regs[1]; } $this->_setLastError($code , $e->getMessage(), 'new PDO'); } } /** @access private */ public function _isConnected() { return (bool)$this->PDO; } protected function _performGetPlaceholderIgnoreRe() { return ' " (?> [^"\\\\]+|\\\\"|\\\\)* " | \' (?> [^\'\\\\]+|\\\\\'|\\\\)* \' | ` (?> [^`]+ | ``)* ` | # backticks /\* .*? \*/ # comments '; } protected function _performEscape($s, $isIdent=false) { if (!$isIdent) { return $this->PDO->quote($s); } else { return "`" . str_replace('`', '``', $s) . "`"; } } protected function _performTransaction($parameters=null) { return $this->PDO->beginTransaction(); } protected function _performCommit() { return $this->PDO->commit(); } protected function _performRollback() { return $this->PDO->rollBack(); } protected function _performQuery($queryMain, $resultOnly = false) { $this->_lastQuery = $queryMain; $this->_expandPlaceholders($queryMain, false); if (!$this->PDO) throw new Exception("Trying to run query, but not connected to database"); $p = $this->PDO->query($queryMain[0]); if (!$p) return $this->_setDbError($p,$queryMain[0]); if ($p->errorCode()!=0) return $this->_setDbError($p,$queryMain[0]); if ($resultOnly) return $p; if (preg_match('/^\s* INSERT \s+/six', $queryMain[0])) return $this->PDO->lastInsertId(); if ($p->columnCount()==0) return $p->rowCount(); //Если у нас в запросе есть хотя-бы одна колонка - это по любому будет select $p->setFetchMode(PDO::FETCH_ASSOC); $res = $p->fetchAll(); $p->closeCursor(); return $res; } protected function _performTransformQuery(&$queryMain, $how) { // If we also need to calculate total number of found rows... switch ($how) { // Prepare total calculation (if possible) case 'CALC_TOTAL': $m = null; if (preg_match('/^(\s* SELECT)(.*)/six', $queryMain[0], $m)) $queryMain[0] = $m[1] . ' SQL_CALC_FOUND_ROWS' . $m[2]; return true; // Perform total calculation. case 'GET_TOTAL': // Built-in calculation available? $queryMain = array('SELECT FOUND_ROWS()'); return true; } return false; } protected function _setDbError($obj,$q) { $info=$obj?$obj->errorInfo():$this->PDO->errorInfo(); return $this->_setLastError($info[1], $info[2], $q); } protected function _performNewBlob($id=null) { } protected function _performGetBlobFieldNames($result) { return array(); } protected function _performFetch($result) { return $result; } /* added by alex@cgi-central.net */ /** @return PDOStatement */ public function queryResultOnly($sql) { $query = func_get_args(); $this->_logQuery($query); // Run the query (counting time). $qStart = microtime(true); $result = $this->_performQuery($query, true); $queryTime = microtime(true) - $qStart; // Log query statistics. $this->_logQueryStat($queryTime, null, null, null); return $result; } /* added by alex@cgi-central.net */ /** * Runs a query without any additional overal with no substitution * @return PDOStatement */ public function queryQuick($sql) { $p = $this->PDO->query($sql); if (!$p) return $this->_setDbError($p, $sql); if ($p->errorCode()!=0) return $this->_setDbError($p, $sql); return $p; } public function fetchRow($st) { return $st->fetch(PDO::FETCH_ASSOC); } public function fetchRows($st) { return $st->fetchAll(PDO::FETCH_ASSOC); } public function fetchArray($st) { return $st->fetch(PDO::FETCH_ASSOC); } public function freeResult($st) { $st->closeCursor(); } public function getPrefix() { return @$this->_identPrefix; } public function expandPlaceholders($queryAndArgs) { if (is_array($queryAndArgs)) { if (count($queryAndArgs)>1) $this->_expandPlaceholders($queryAndArgs); return $queryAndArgs[0]; } else { return (string)$queryAndArgs; } } function escapeWithPlaceholders($s, $_){ $queryAndArgs = func_get_args(); $this->_expandPlaceholders($queryAndArgs); return $queryAndArgs[0]; } public function quote($s) { return trim($this->PDO->quote($s), "'"); } public function __sleep() { return array(); } } class DbSimple_Mypdo_Blob implements DbSimple_Generic_Blob { // MySQL does not support separate BLOB fetching. private $blobdata = null; private $curSeek = 0; public function __construct(&$database, $blobdata=null) { $this->blobdata = $blobdata; $this->curSeek = 0; } public function read($len) { $p = $this->curSeek; $this->curSeek = min($this->curSeek + $len, strlen($this->blobdata)); return substr($this->blobdata, $this->curSeek, $len); } public function write($data) { $this->blobdata .= $data; } public function close() { return $this->blobdata; } public function length() { return strlen($this->blobdata); } } library/PasswordHash.php000064400000015256152101576050011342 0ustar00 in 2004-2006 and placed in # the public domain. Revised in subsequent years, still public domain. # # There's absolutely no warranty. # # The homepage URL for this framework is: # # http://www.openwall.com/phpass/ # # Please be sure to update the Version line if you edit this file in any way. # It is suggested that you leave the main version number intact, but indicate # your project name (after the slash) and add your own revision information. # # Please do not change the "private" password hashing method implemented in # here, thereby making your hashes incompatible. However, if you must, please # change the hash type identifier (the "$P$") to something different. # # Obviously, since this code is in the public domain, the above are not # requirements (there can be none), but merely suggestions. # class PasswordHash { var $itoa64; var $iteration_count_log2; var $portable_hashes; var $random_state; function __construct($iteration_count_log2, $portable_hashes) { $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) $iteration_count_log2 = 8; $this->iteration_count_log2 = $iteration_count_log2; $this->portable_hashes = $portable_hashes; $this->random_state = microtime(); if (function_exists('getmypid')) $this->random_state .= getmypid(); } function get_random_bytes($count) { $output = ''; if (@is_readable('/dev/urandom') && ($fh = @fopen('/dev/urandom', 'rb'))) { $output = fread($fh, $count); fclose($fh); } if (strlen($output) < $count) { $output = ''; for ($i = 0; $i < $count; $i += 16) { $this->random_state = md5(microtime() . $this->random_state); $output .= pack('H*', md5($this->random_state)); } $output = substr($output, 0, $count); } return $output; } function encode64($input, $count) { $output = ''; $i = 0; do { $value = ord($input[$i++]); $output .= $this->itoa64[$value & 0x3f]; if ($i < $count) $value |= ord($input[$i]) << 8; $output .= $this->itoa64[($value >> 6) & 0x3f]; if ($i++ >= $count) break; if ($i < $count) $value |= ord($input[$i]) << 16; $output .= $this->itoa64[($value >> 12) & 0x3f]; if ($i++ >= $count) break; $output .= $this->itoa64[($value >> 18) & 0x3f]; } while ($i < $count); return $output; } function gensalt_private($input) { $output = '$P$'; $output .= $this->itoa64[min($this->iteration_count_log2 + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; $output .= $this->encode64($input, 6); return $output; } function crypt_private($password, $setting) { $output = '*0'; if (substr($setting, 0, 2) == $output) $output = '*1'; $id = substr($setting, 0, 3); # We use "$P$", phpBB3 uses "$H$" for the same thing if ($id != '$P$' && $id != '$H$') return $output; $count_log2 = strpos($this->itoa64, $setting[3]); if ($count_log2 < 7 || $count_log2 > 30) return $output; $count = 1 << $count_log2; $salt = substr($setting, 4, 8); if (strlen($salt) != 8) return $output; # We're kind of forced to use MD5 here since it's the only # cryptographic primitive available in all versions of PHP # currently in use. To implement our own low-level crypto # in PHP would result in much worse performance and # consequently in lower iteration counts and hashes that are # quicker to crack (by non-PHP code). if (PHP_VERSION >= '5') { $hash = md5($salt . $password, TRUE); do { $hash = md5($hash . $password, TRUE); } while (--$count); } else { $hash = pack('H*', md5($salt . $password)); do { $hash = pack('H*', md5($hash . $password)); } while (--$count); } $output = substr($setting, 0, 12); $output .= $this->encode64($hash, 16); return $output; } function gensalt_extended($input) { $count_log2 = min($this->iteration_count_log2 + 8, 24); # This should be odd to not reveal weak DES keys, and the # maximum valid value is (2**24 - 1) which is odd anyway. $count = (1 << $count_log2) - 1; $output = '_'; $output .= $this->itoa64[$count & 0x3f]; $output .= $this->itoa64[($count >> 6) & 0x3f]; $output .= $this->itoa64[($count >> 12) & 0x3f]; $output .= $this->itoa64[($count >> 18) & 0x3f]; $output .= $this->encode64($input, 3); return $output; } function gensalt_blowfish($input) { # This one needs to use a different order of characters and a # different encoding scheme from the one in encode64() above. # We care because the last character in our encoded string will # only represent 2 bits. While two known implementations of # bcrypt will happily accept and correct a salt string which # has the 4 unused bits set to non-zero, we do not want to take # chances and we also do not want to waste an additional byte # of entropy. $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $output = '$2a$'; $output .= chr(ord('0') + $this->iteration_count_log2 / 10); $output .= chr(ord('0') + $this->iteration_count_log2 % 10); $output .= '$'; $i = 0; do { $c1 = ord($input[$i++]); $output .= $itoa64[$c1 >> 2]; $c1 = ($c1 & 0x03) << 4; if ($i >= 16) { $output .= $itoa64[$c1]; break; } $c2 = ord($input[$i++]); $c1 |= $c2 >> 4; $output .= $itoa64[$c1]; $c1 = ($c2 & 0x0f) << 2; $c2 = ord($input[$i++]); $c1 |= $c2 >> 6; $output .= $itoa64[$c1]; $output .= $itoa64[$c2 & 0x3f]; } while (1); return $output; } function HashPassword($password) { $random = ''; if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { $random = $this->get_random_bytes(16); $hash = crypt($password, $this->gensalt_blowfish($random)); if (strlen($hash) == 60) return $hash; } if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { if (strlen($random) < 3) $random = $this->get_random_bytes(3); $hash = crypt($password, $this->gensalt_extended($random)); if (strlen($hash) == 20) return $hash; } if (strlen($random) < 6) $random = $this->get_random_bytes(6); $hash = $this->crypt_private($password, $this->gensalt_private($random)); if (strlen($hash) == 34) return $hash; # Returning '*' on error is safe here, but would _not_ be safe # in a crypt(3)-like function used _both_ for generating new # hashes and for validating passwords against existing hashes. return '*'; } function CheckPassword($password, $stored_hash) { $hash = $this->crypt_private($password, $stored_hash); if ($hash[0] == '*') $hash = crypt($password, $stored_hash); return $hash == $stored_hash; } } ?> library/pear/HTML/Common2.php000064400000035134152101576050011736 0ustar00 * * 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 names of the authors 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 HTML * @package HTML_Common2 * @author Alexey Borzov * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version CVS: $Id: Common2.php 304516 2010-10-19 18:43:58Z avb $ * @link http://pear.php.net/package/HTML_Common2 */ /** * Base class for HTML classes * * Implements methods for working with HTML attributes, parsing and generating * attribute strings. Port of HTML_Common class for PHP4 originally written by * Adam Daniel with contributions from numerous other developers. * * @category HTML * @package HTML_Common2 * @author Alexey Borzov * @version Release: 2.0.0 */ abstract class HTML_Common2 { /** * Associative array of attributes * @var array */ protected $attributes = array(); /** * List of attribites changes to which will be announced via onAttributeChange() * method rather than performed by HTML_Common2 class itself * @var array * @see onAttributeChange() */ protected $watchedAttributes = array(); /** * Indentation level of the element * @var int */ private $_indentLevel = 0; /** * Comment associated with the element * @var string */ private $_comment = null; /** * Global options for all elements generated by subclasses of HTML_Common2 * * Preset options are * - 'charset': charset parameter used in htmlspecialchars() calls, * defaults to 'ISO-8859-1' * - 'indent': string used to indent HTML elements, defaults to "\11" * - 'linebreak': string used to indicate linebreak, defaults to "\12" * * @var array */ private static $_options = array( 'charset' => 'ISO-8859-1', 'indent' => "\11", 'linebreak' => "\12" ); /** * Sets global option(s) * * @param string|array Option name or array ('option name' => 'option value') * @param mixed Option value, if first argument is not an array */ public static function setOption($nameOrOptions, $value = null) { if (is_array($nameOrOptions)) { foreach ($nameOrOptions as $k => $v) { self::setOption($k, $v); } } else { $linebreaks = array('win' => "\15\12", 'unix' => "\12", 'mac' => "\15"); if ('linebreak' == $nameOrOptions && isset($linebreaks[$value])) { $value = $linebreaks[$value]; } self::$_options[$nameOrOptions] = $value; } } /** * Returns global option(s) * * @param string Option name * @return mixed Option value, null if option does not exist, * array of all options if $name is not given */ public static function getOption($name = null) { if (null === $name) { return self::$_options; } else { return isset(self::$_options[$name])? self::$_options[$name]: null; } } /** * Parses the HTML attributes given as string * * @param string HTML attribute string * @return array An associative aray of attributes */ protected static function parseAttributes($attrString) { $attributes = array(); if (preg_match_all( "/(([A-Za-z_:]|[^\\x00-\\x7F])([A-Za-z0-9_:.-]|[^\\x00-\\x7F])*)" . "([ \\n\\t\\r]+)?(=([ \\n\\t\\r]+)?(\"[^\"]*\"|'[^']*'|[^ \\n\\t\\r]*))?/", $attrString, $regs )) { for ($i = 0; $i < count($regs[1]); $i++) { $name = trim($regs[1][$i]); $check = trim($regs[0][$i]); $value = trim($regs[7][$i]); if ($name == $check) { $attributes[strtolower($name)] = strtolower($name); } else { if (!empty($value) && ($value[0] == '\'' || $value[0] == '"')) { $value = substr($value, 1, -1); } $attributes[strtolower($name)] = $value; } } } return $attributes; } /** * Creates a valid attribute array from either a string or an array * * @param mixed Array of attributes or HTML attribute string * @return array An associative aray of attributes */ protected static function prepareAttributes($attributes) { $prepared = array(); if (is_string($attributes)) { return self::parseAttributes($attributes); } elseif (is_array($attributes)) { foreach ($attributes as $key => $value) { if (is_int($key)) { $key = strtolower($value); $prepared[$key] = $key; } else { $prepared[strtolower($key)] = (string)$value; } } } return $prepared; } /** * Removes an attribute from an attribute array * * @param array Attribute array * @param string Name of attribute to remove */ protected static function removeAttributeArray(array &$attributes, $name) { unset($attributes[strtolower($name)]); } /** * Creates HTML attribute string from array * * @param array Attribute array * @return string Attribute string */ protected static function getAttributesString(array $attributes) { $str = ''; $charset = self::getOption('charset'); foreach ($attributes as $key => $value) { $str .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES, $charset) . '"'; } return $str; } /** * Class constructor, sets default attributes * * @param mixed Array of attribute 'name' => 'value' pairs or HTML attribute string */ public function __construct($attributes = null) { $this->mergeAttributes($attributes); } /** * Sets the value of the attribute * * @param string Attribute name * @param string Attribute value (will be set to $name if omitted) * @return HTML_Common2 */ public function setAttribute($name, $value = null) { $name = strtolower($name); if (is_null($value)) { $value = $name; } if (in_array($name, $this->watchedAttributes)) { $this->onAttributeChange($name, $value); } else { $this->attributes[$name] = (string)$value; } return $this; } /** * Returns the value of an attribute * * @param string Attribute name * @return string Attribute value, null if attribute does not exist */ public function getAttribute($name) { $name = strtolower($name); return isset($this->attributes[$name])? $this->attributes[$name]: null; } /** * Sets the attributes * * @param mixed Array of attribute 'name' => 'value' pairs or HTML attribute string * @return HTML_Common2 */ public function setAttributes($attributes) { $attributes = self::prepareAttributes($attributes); $watched = array(); foreach ($this->watchedAttributes as $watchedKey) { if (isset($attributes[$watchedKey])) { $this->setAttribute($watchedKey, $attributes[$watchedKey]); unset($attributes[$watchedKey]); } else { $this->removeAttribute($watchedKey); } if (isset($this->attributes[$watchedKey])) { $watched[$watchedKey] = $this->attributes[$watchedKey]; } } $this->attributes = array_merge($watched, $attributes); return $this; } /** * Returns the attribute array or string * * @param bool Whether to return attributes as string * @return mixed Either an array or string of attributes */ public function getAttributes($asString = false) { if ($asString) { return self::getAttributesString($this->attributes); } else { return $this->attributes; } } /** * Merges the existing attributes with the new ones * * @param mixed Array of attribute 'name' => 'value' pairs or HTML attribute string * @return HTML_Common2 */ public function mergeAttributes($attributes) { $attributes = self::prepareAttributes($attributes); foreach ($this->watchedAttributes as $watchedKey) { if (isset($attributes[$watchedKey])) { $this->onAttributeChange($watchedKey, $attributes[$watchedKey]); unset($attributes[$watchedKey]); } } $this->attributes = array_merge($this->attributes, $attributes); return $this; } /** * Removes an attribute * * @param string Name of attribute to remove * @return HTML_Common2 */ public function removeAttribute($attribute) { if (in_array(strtolower($attribute), $this->watchedAttributes)) { $this->onAttributeChange(strtolower($attribute), null); } else { self::removeAttributeArray($this->attributes, $attribute); } return $this; } /** * Sets the indentation level * * @param int * @return HTML_Common2 */ public function setIndentLevel($level) { $level = intval($level); if (0 <= $level) { $this->_indentLevel = $level; } return $this; } /** * Gets the indentation level * * @return int */ public function getIndentLevel() { return $this->_indentLevel; } /** * Returns the string to indent the element * * @return string */ protected function getIndent() { return str_repeat(self::getOption('indent'), $this->getIndentLevel()); } /** * Sets the comment for the element * * @param string * @return HTML_Common2 */ public function setComment($comment) { $this->_comment = $comment; return $this; } /** * Returns the comment associated with the element * * @return string */ public function getComment() { return $this->_comment; } /** * Checks whether the element has given CSS class * * @param string Class name * @return bool */ public function hasClass($class) { $regex = '/(^|\s)' . preg_quote($class, '/') . '(\s|$)/'; return (bool)preg_match($regex, $this->getAttribute('class')); } /** * Adds the given CSS class(es) to the element * * @param string|array Class name, multiple class names separated by * whitespace, array of class names * @return HTML_Common2 */ public function addClass($class) { if (!is_array($class)) { $class = preg_split('/\s+/', $class, null, PREG_SPLIT_NO_EMPTY); } $curClass = preg_split('/\s+/', $this->getAttribute('class'), null, PREG_SPLIT_NO_EMPTY); foreach ($class as $c) { if (!in_array($c, $curClass)) { $curClass[] = $c; } } $this->setAttribute('class', implode(' ', $curClass)); return $this; } /** * Removes the given CSS class(es) from the element * * @param string|array Class name, multiple class names separated by * whitespace, array of class names * @return HTML_Common2 */ public function removeClass($class) { if (!is_array($class)) { $class = preg_split('/\s+/', $class, null, PREG_SPLIT_NO_EMPTY); } $curClass = array_diff( preg_split('/\s+/', $this->getAttribute('class'), null, PREG_SPLIT_NO_EMPTY), $class ); if (0 == count($curClass)) { $this->removeAttribute('class'); } else { $this->setAttribute('class', implode(' ', $curClass)); } return $this; } /** * Returns the HTML representation of the element * * This magic method allows using the instances of HTML_Common2 in string * contexts * * @return string */ abstract public function __toString(); /** * Called if trying to change an attribute with name in $watchedAttributes * * This method is called for each attribute whose name is in the * $watchedAttributes array and which is being changed by setAttribute(), * setAttributes() or mergeAttributes() or removed via removeAttribute(). * Note that the operation for the attribute is not carried on after calling * this method, it is the responsibility of this method to change or remove * (or not) the attribute. * * @param string Attribute name * @param string Attribute value, null if attribute is being removed */ protected function onAttributeChange($name, $value = null) { } } ?> library/pear/HTML/QuickForm2/DataSource/Array.php000064400000006747152101576050015526 0ustar00, * Bertrand Mansion * 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 names of the authors 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 HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Array.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Interface for data sources used by HTML_QuickForm2 objects */ require_once 'HTML/QuickForm2/DataSource.php'; /** * Array-based data source for HTML_QuickForm2 objects * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_DataSource_Array implements HTML_QuickForm2_DataSource { /** * Array containing elements' values * @var array */ protected $values; /** * Class constructor, initializes the values array * * @param array Array containing the elements' values */ public function __construct($values = array()) { $this->values = $values; } public function getValue($name) { if (empty($this->values)) { return null; } if (strpos($name, '[')) { $tokens = explode('[', str_replace(']', '', $name)); $value = $this->values; do { $token = array_shift($tokens); if (!is_array($value) || !isset($value[$token])) { return null; } $value = $value[$token]; } while (!empty($tokens)); return $value; } elseif (isset($this->values[$name])) { return $this->values[$name]; } else { return null; } } } ?> library/pear/HTML/QuickForm2/DataSource/Session.php000064400000006145152101576050016063 0ustar00, * Bertrand Mansion * 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 names of the authors 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 HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Session.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** Interface for data sources containing submitted values */ require_once 'HTML/QuickForm2/DataSource/Submit.php'; /** Array-based data source for HTML_QuickForm2 objects */ require_once 'HTML/QuickForm2/DataSource/Array.php'; /** * Class presenting the values stored in session by Controller as submitted ones * * This is a less hackish implementation of loadValues() method in old * HTML_QuickForm_Controller. The values need to be presented as submitted so * that elements like checkboxes and multiselects do not try to use default * values from subsequent datasources. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_DataSource_Session extends HTML_QuickForm2_DataSource_Array implements HTML_QuickForm2_DataSource_Submit { /** * File upload data is not stored in the session */ public function getUpload($name) { return null; } } ?> library/pear/HTML/QuickForm2/DataSource/Submit.php000064400000005774152101576050015712 0ustar00, * Bertrand Mansion * 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 names of the authors 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 HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Submit.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Interface for data sources used by HTML_QuickForm2 objects */ require_once 'HTML/QuickForm2/DataSource.php'; /** * Interface for data sources containing submitted values * * This interface provides method for getting information on uploaded files. * Additionally some elements will only consider getting their values from data * sources implementing this interface. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ interface HTML_QuickForm2_DataSource_Submit extends HTML_QuickForm2_DataSource { /** * Returns the information about uploaded file * * If data source doesn't such information it should return null * * @param string Name of file upload field * @return array|null Information on uploaded file, from $_FILES array */ public function getUpload($name); } ?> library/pear/HTML/QuickForm2/DataSource/SuperGlobal.php000064400000013701152101576050016653 0ustar00, * Bertrand Mansion * 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 names of the authors 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 HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: SuperGlobal.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Interface for data sources containing submitted values */ require_once 'HTML/QuickForm2/DataSource/Submit.php'; /** * Array-based data source for HTML_QuickForm2 objects */ require_once 'HTML/QuickForm2/DataSource/Array.php'; /** * Data source for HTML_QuickForm2 objects based on superglobal arrays * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_DataSource_SuperGlobal extends HTML_QuickForm2_DataSource_Array implements HTML_QuickForm2_DataSource_Submit { /** * Information on file uploads (from $_FILES) * @var array */ protected $files = array(); /** * Keys present in the $_FILES array * @var array */ private static $_fileKeys = array('name', 'type', 'size', 'tmp_name', 'error'); /** * Class constructor, intializes the internal arrays from superglobals * * @param string Request method (GET or POST) * @param bool Whether magic_quotes_gpc directive is on */ public function __construct($requestMethod = 'POST', $magicQuotesGPC = false) { if (!$magicQuotesGPC) { if ('GET' == strtoupper($requestMethod)) { $this->values = $_GET; } else { $this->values = $_POST; $this->files = $_FILES; } } else { if ('GET' == strtoupper($requestMethod)) { $this->values = $this->arrayMapRecursive('stripslashes', $_GET); } else { $this->values = $this->arrayMapRecursive('stripslashes', $_POST); foreach ($_FILES as $key1 => $val1) { foreach ($val1 as $key2 => $val2) { if ('name' == $key2) { $this->files[$key1][$key2] = $this->arrayMapRecursive( 'stripslashes', $val2 ); } else { $this->files[$key1][$key2] = $val2; } } } } } } /** * A recursive version of array_map() function * * @param callback Callback function to apply * @param mixed Input array * @return array with callback applied */ protected function arrayMapRecursive($callback, $arr) { if (!is_array($arr)) { return call_user_func($callback, $arr); } $mapped = array(); foreach ($arr as $k => $v) { $mapped[$k] = is_array($v)? $this->arrayMapRecursive($callback, $v): call_user_func($callback, $v); } return $mapped; } public function getUpload($name) { if (empty($this->files)) { return null; } if (false !== ($pos = strpos($name, '['))) { $tokens = explode('[', str_replace(']', '', $name)); $base = array_shift($tokens); $value = array(); if (!isset($this->files[$base]['name'])) { return null; } foreach (self::$_fileKeys as $key) { $value[$key] = $this->files[$base][$key]; } do { $token = array_shift($tokens); if (!isset($value['name'][$token])) { return null; } foreach (self::$_fileKeys as $key) { $value[$key] = $value[$key][$token]; } } while (!empty($tokens)); return $value; } elseif(isset($this->files[$name])) { return $this->files[$name]; } else { return null; } } } ?> library/pear/HTML/QuickForm2/Rule.php000064400000033775152101576050013326 0ustar00, * Bertrand Mansion * 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 names of the authors 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 HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Rule.php 310525 2011-04-26 18:42:03Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Abstract base class for HTML_QuickForm2 rules * * This class provides methods that allow chaining several rules together. * Its validate() method executes the whole rule chain starting from this rule. * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ abstract class HTML_QuickForm2_Rule { /** * Constant showing that validation should be run server-side * @see HTML_QuickForm2_Node::addRule() */ const SERVER = 1; /** * Constant showing that validation should be run client-side (on form submit) * @see HTML_QuickForm2_Node::addRule() */ const CLIENT = 2; /** * Constant showing that validation should be run client-side (on form submit and on leaving the field) * @see HTML_QuickForm2_Node::addRule() */ const ONBLUR_CLIENT = 6; /** * A combination of SERVER and CLIENT constants * @see HTML_QuickForm2_Node::addRule() */ const CLIENT_SERVER = 3; /** * A combination of SERVER and ONBLUR_CLIENT constants * @see HTML_QuickForm2_Node::addRule() */ const ONBLUR_CLIENT_SERVER = 7; /** * An element whose value will be validated by this rule * @var HTML_QuickForm2_Node */ protected $owner; /** * An error message to display if validation fails * @var string */ protected $message; /** * Configuration data for the rule * @var mixed */ protected $config; /** * Rules chained to this via "and" and "or" operators * * The contents can be described as "disjunctive normal form", where an outer * array represents a disjunction of conjunctive clauses represented by inner * arrays. * * @var array */ protected $chainedRules = array(array()); /** * Class constructor * * @param HTML_QuickForm2_Node Element to validate * @param string Error message to display if validation fails * @param mixed Configuration data for the rule */ public function __construct(HTML_QuickForm2_Node $owner, $message = '', $config = null) { $this->setOwner($owner); $this->setMessage($message); $this->setConfig($config); } /** * Merges local configuration with that provided for registerRule() * * Default behaviour is for global config to override local one, different * Rules may implement more complex merging behaviours. * * @param mixed Local configuration * @param mixed Global configuration, usually provided to {@link HTML_QuickForm2_Factory::registerRule()} * @return mixed Merged configuration */ public static function mergeConfig($localConfig, $globalConfig) { return is_null($globalConfig)? $localConfig: $globalConfig; } /** * Sets configuration data for the rule * * @param mixed Rule configuration data (specific for a Rule) * @return HTML_QuickForm2_Rule * @throws HTML_QuickForm2_InvalidArgumentException in case of invalid * configuration data */ public function setConfig($config) { $this->config = $config; return $this; } /** * Returns the rule's configuration data * * @return mixed Configuration data (specific for a Rule) */ public function getConfig() { return $this->config; } /** * Sets the error message output by the rule * * @param string Error message to display if validation fails * @return HTML_QuickForm2_Rule */ public function setMessage($message) { $this->message = (string)$message; return $this; } /** * Returns the error message output by the rule * * @return string Error message */ public function getMessage() { return $this->message; } /** * Sets the element that will be validated by this rule * * @param HTML_QuickForm2_Node Element to validate * @throws HTML_QuickForm2_InvalidArgumentException if trying to set * an instance of HTML_QuickForm2_Element_Static as rule owner */ public function setOwner(HTML_QuickForm2_Node $owner) { // Very little sense to validate static elements as they're, well, static. // If someone comes up with a validation rule for these, he can override // setOwner() there... if ($owner instanceof HTML_QuickForm2_Element_Static) { throw new HTML_QuickForm2_InvalidArgumentException( get_class($this) . ' cannot validate Static elements' ); } if (null !== $this->owner) { $this->owner->removeRule($this); } $this->owner = $owner; } /** * Adds a rule to the chain with an "and" operator * * Evaluation is short-circuited, next rule will not be evaluated if the * previous one returns false. The method is named this way because "and" is * a reserved word in PHP. * * @param HTML_QuickForm2_Rule * @return HTML_QuickForm2_Rule first rule in the chain (i.e. $this) * @throws HTML_QuickForm2_InvalidArgumentException when trying to add * a "required" rule to the chain */ public function and_(HTML_QuickForm2_Rule $next) { if ($next instanceof HTML_QuickForm2_Rule_Required) { throw new HTML_QuickForm2_InvalidArgumentException( 'and_(): Cannot add a "required" rule' ); } $this->chainedRules[count($this->chainedRules) - 1][] = $next; return $this; } /** * Adds a rule to the chain with an "or" operator * * Evaluation is short-circuited, next rule will not be evaluated if the * previous one returns true. The method is named this way because "or" is * a reserved word in PHP. * * @param HTML_QuickForm2_Rule * @return HTML_QuickForm2_Rule first rule in the chain (i.e. $this) * @throws HTML_QuickForm2_InvalidArgumentException when trying to add * a "required" rule to the chain */ public function or_(HTML_QuickForm2_Rule $next) { if ($next instanceof HTML_QuickForm2_Rule_Required) { throw new HTML_QuickForm2_InvalidArgumentException( 'or_(): Cannot add a "required" rule' ); } $this->chainedRules[] = array($next); return $this; } /** * Performs validation * * The whole rule chain is executed. Note that the side effect of this * method is setting the error message on element if validation fails * * @return boolean Whether the element is valid */ public function validate() { $globalValid = false; $localValid = $this->validateOwner(); foreach ($this->chainedRules as $item) { foreach ($item as $multiplier) { if (!($localValid = $localValid && $multiplier->validate())) { break; } } if ($globalValid = $globalValid || $localValid) { break; } $localValid = true; } $globalValid or $this->setOwnerError(); return $globalValid; } /** * Validates the owner element * * @return bool Whether owner element is valid according to the rule */ abstract protected function validateOwner(); /** * Sets the error message on the owner element */ protected function setOwnerError() { if (strlen($this->getMessage()) && !$this->owner->getError()) { $this->owner->setError($this->getMessage()); } } /** * Returns the client-side validation callback * * This essentially builds a Javascript version of validateOwner() method, * with element ID and Rule configuration hardcoded. * * @return string Javascript function to validate the element's value * @throws HTML_QuickForm2_Exception if Rule can only be run server-side */ protected function getJavascriptCallback() { throw new HTML_QuickForm2_Exception( get_class($this) . ' does not implement javascript validation' ); } /** * Returns IDs of form fields that should trigger "live" Javascript validation * * This returns IDs that are linked to the rule itself. * * @return array */ protected function getOwnJavascriptTriggers() { return $this->owner->getJavascriptTriggers(); } /** * Returns IDs of form fields that should trigger "live" Javascript validation * * This returns IDs that are linked to the rule itself and its chained * rules. Live validation will be be triggered by 'blur' or 'change' event * on any of the elements whose IDs are returned by this method. * * @return array */ protected function getJavascriptTriggers() { $triggers = array_flip($this->getOwnJavascriptTriggers()); foreach ($this->chainedRules as $item) { foreach ($item as $multiplier) { foreach ($multiplier->getJavascriptTriggers() as $trigger) { $triggers[$trigger] = true; } } } return array_keys($triggers); } /** * Returns the client-side representation of the Rule * * The Javascript object returned contains the following fields: * - callback: {@see getJavascriptCallback()} * - elementId: element ID to set error for if validation fails * - errorMessage: error message to set if validation fails * - chained: chained rules, array of arrays like in $chainedRules property * * @return string * @throws HTML_QuickForm2_Exception if Rule or its chained Rules can only * be run server-side */ public function getJavascript($outputTriggers = true) { HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_JavascriptBuilder'); $js = $this->getJavascriptCallback() . ",\n\t'" . $this->owner->getId() . "', " . HTML_QuickForm2_JavascriptBuilder::encode($this->getMessage()); $js = $outputTriggers && count($triggers = $this->getJavascriptTriggers()) ? 'new qf.LiveRule(' . $js . ', ' . HTML_QuickForm2_JavascriptBuilder::encode($triggers) : 'new qf.Rule(' . $js; if (count($this->chainedRules) > 1 || count($this->chainedRules[0]) > 0) { $chained = array(); foreach ($this->chainedRules as $item) { $multipliers = array(); foreach ($item as $multiplier) { $multipliers[] = $multiplier->getJavascript(false); } $chained[] = '[' . implode(",\n", $multipliers) . ']'; } $js .= ",\n\t [" . implode(",\n", $chained) . "]"; } return $js . ')'; } /** * (( defined by alex@cgi-central.net - it is too wordy to build * rules without such easy chaining )) * Adds a validation rule to the owner of this validation rule * * @param HTML_QuickForm2_Rule|string Validation rule or rule type * @param string Message to display if validation * @param mixed Additional data for the rule * @return HTML_QuickForm2_Rule The added rule * @throws HTML_QuickForm2_InvalidArgumentException if $rule is of a * wrong type or rule name isn't registered with Factory * @throws HTML_QuickForm2_NotFoundException if class for a given rule * name cannot be found */ public function addRule($rule, $message = '', $options = null) { return $this->owner->addRule($rule, $message, $options); } /** @return HTML_QuickForm2_Node */ public function getOwner() { return $this->owner; } } library/pear/HTML/QuickForm2/Element/InputImage.php000064400000013103152101576050016031 0ustar00 elements * * PHP version 5 * * LICENSE: * * Copyright (c) 2006-2011, Alexey Borzov , * Bertrand Mansion * 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 names of the authors 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 HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: InputImage.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Base class for elements */ require_once 'HTML/QuickForm2/Element/Input.php'; /** * Class for elements * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_Element_InputImage extends HTML_QuickForm2_Element_Input { protected $attributes = array('type' => 'image'); /** * Coordinates of user click within the image, array contains keys 'x' and 'y' * @var array */ protected $coordinates = null; /** * Image buttons can not be frozen * * @param bool Whether element should be frozen or editable. This * parameter is ignored in case of image elements * @return bool Always returns false */ public function toggleFrozen($freeze = null) { return false; } /** * Image button's value cannot be set via this method * * @param mixed Element's value, this parameter is ignored * @return HTML_QuickForm2_Element_InputImage */ public function setValue($value) { return $this; } /** * Returns the element's value * * The value is only returned if the form was actually submitted and this * image button was clicked. Returns null in all other cases. * * @return array|null An array with keys 'x' and 'y' containing the * coordinates of user click if the image was clicked, * null otherwise */ public function getRawValue() { return $this->getAttribute('disabled')? null: $this->coordinates; } /** * Returns the HTML representation of the element * * The method changes the element's name to foo[bar][] if it was foo[bar] * originally. If it is not done, then one of the click coordinates will be * lost, see {@link http://bugs.php.net/bug.php?id=745} * * @return string */ public function __toString() { if (false === strpos($this->attributes['name'], '[') || '[]' == substr($this->attributes['name'], -2)) { return parent::__toString(); } else { $this->attributes['name'] .= '[]'; $html = parent::__toString(); $this->attributes['name'] = substr($this->attributes['name'], 0, -2); return $html; } } protected function updateValue() { foreach ($this->getDataSources() as $ds) { if ($ds instanceof HTML_QuickForm2_DataSource_Submit) { $name = $this->getName(); if (false === strpos($name, '[') && null !== ($value = $ds->getValue($name . '_x'))) { $this->coordinates = array( 'x' => $value, 'y' => $ds->getValue($name . '_y') ); return; } elseif (false !== strpos($name, '[')) { if ('[]' == substr($name, -2)) { $name = substr($name, 0, -2); } if (null !== ($value = $ds->getValue($name))) { $this->coordinates = array( 'x' => $value[0], 'y' => $value[1] ); return; } } } } $this->coordinates = null; } } ?> library/pear/HTML/QuickForm2/Element/Static.php000064400000021234152101576050015222 0ustar00, * Bertrand Mansion * 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 names of the authors 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 HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Static.php 311477 2011-05-27 09:01:44Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Base class for simple HTML_QuickForm2 elements (not Containers) */ require_once 'HTML/QuickForm2/Element.php'; /** * Class for static elements that only contain text or markup * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_Element_Static extends HTML_QuickForm2_Element { /** * Name of the tag to wrap around static element's content * @var string */ protected $tagName = null; /** * Whether to output closing tag when $tagName is set and element's content is empty * @var bool */ protected $forceClosingTag = true; /** * Contains options and data used for the element creation * - content: Content of the static element * @var array */ protected $data = array('content' => ''); /** * Class constructor * * Static element can understand the following keys in $data parameter: * - 'content': content of the static element, e.g. text or markup * - 'tagName': name of the tag to wrap around content, e.g. 'div'. * Using tag names corresponding to form elements will cause an Exception * - 'forceClosingTag': whether to output closing tag in case of empty * content, <foo></foo> vs. <foo /> * * @param string Element name * @param mixed Attributes (either a string or an array) * @param array Additional element data */ public function __construct($name = null, $attributes = null, array $data = array()) { if (!empty($data['tagName'])) { $this->setTagName( $data['tagName'], !array_key_exists('forceClosingTag', $data) || $data['forceClosingTag'] ); } unset($data['tagName'], $data['forceClosingTag']); parent::__construct($name, $attributes, $data); } /** * Intercepts setting 'name' and 'id' attributes * * Overrides parent method to allow removal of 'name' attribute on Static * elements * * @param string Attribute name * @param string Attribute value, null if attribute is being removed * @throws HTML_QuickForm2_InvalidArgumentException if trying to * remove a required attribute */ protected function onAttributeChange($name, $value = null) { if ('name' == $name && null === $value) { unset($this->attributes['name']); } else { parent::onAttributeChange($name, $value); } } /** * Sets the element's name * * Passing null here will remove the name attribute * * @param string|null * @return HTML_QuickForm2_Element_Static */ public function setName($name) { if (null !== $name) { return parent::setName($name); } else { return $this->removeAttribute('name'); } } public function getType() { return 'static'; } /** * Static element can not be frozen * * @param bool Whether element should be frozen or editable. This * parameter is ignored in case of static elements * @return bool Always returns false */ public function toggleFrozen($freeze = null) { return false; } /** * Sets the contents of the static element * * @param string Static content * @return HTML_QuickForm2_Element_Static */ function setContent($content) { $this->data['content'] = $content; return $this; } /** * Returns the contents of the static element * * @return string */ function getContent() { return $this->data['content']; } /** * Static element's content can also be set via this method * * @param mixed Element's value, this parameter is ignored * @return HTML_QuickForm2_Element_Static */ public function setValue($value) { $this->setContent($value); return $this; } /** * Static elements have no value * * @return null */ public function getRawValue() { return null; } public function __toString() { $prefix = $this->getIndent(); if ($comment = $this->getComment()) { $prefix .= '' . HTML_Common2::getOption('linebreak') . $this->getIndent(); } if (!$this->tagName) { return $prefix . $this->getContent(); } elseif ('' != $this->getContent()) { return $prefix . '<' . $this->tagName . $this->getAttributes(true) . '>' . $this->getContent() . 'tagName . '>'; } else { return $prefix . '<' . $this->tagName . $this->getAttributes(true) . ($this->forceClosingTag ? '>tagName . '>' : ' />'); } } public function getJavascriptValue($inContainer = false) { return ''; } public function getJavascriptTriggers() { return array(); } /** * Called when the element needs to update its value from form's data sources * * Static elements content can be updated with default form values. */ protected function updateValue() { foreach ($this->getDataSources() as $ds) { if (!$ds instanceof HTML_QuickForm2_DataSource_Submit && null !== ($value = $ds->getValue($this->getName()))) { $this->setContent($value); return; } } } /** * Sets the name of the HTML tag to wrap around static element's content * * @param string tag name * @param bool whether to output closing tag in case of empty contents * @throws HTML_QuickForm2_InvalidArgumentException when trying to set a tag * name corresponding to a form element * @return HTML_QuickForm2_Element_Static */ public function setTagName($name, $forceClosing = true) { // Prevent people shooting themselves in the proverbial foot if (in_array(strtolower($name), array('form', 'fieldset', 'button', 'input', 'select', 'textarea')) ) { throw new HTML_QuickForm2_InvalidArgumentException( "Do not use tag name '{$name}' with Static element, use proper element class" ); } $this->tagName = (string)$name; $this->forceClosingTag = (bool)$forceClosing; return $this; } } ?>library/pear/HTML/QuickForm2/Element/InputHidden.php000064400000006053152101576050016210 0ustar00 elements * * PHP version 5 * * LICENSE: * * Copyright (c) 2006-2011, Alexey Borzov , * Bertrand Mansion * 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 names of the authors 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 HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: InputHidden.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Base class for elements */ require_once 'HTML/QuickForm2/Element/Input.php'; /** * Class for elements * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_Element_InputHidden extends HTML_QuickForm2_Element_Input { protected $attributes = array('type' => 'hidden'); /** * Hidden elements can not be frozen * * @param bool Whether element should be frozen or editable. This * parameter is ignored in case of hidden elements * @return bool Always returns false */ public function toggleFrozen($freeze = null) { return false; } public function render(HTML_QuickForm2_Renderer $renderer) { $renderer->renderHidden($this); $this->renderClientRules($renderer->getJavascriptBuilder()); return $renderer; } } ?> library/pear/HTML/QuickForm2/Element/InputRadio.php000064400000005250152101576050016051 0ustar00 elements * * PHP version 5 * * LICENSE: * * Copyright (c) 2006-2011, Alexey Borzov , * Bertrand Mansion * 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 names of the authors 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 HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: InputRadio.php 309777 2011-03-28 10:41:11Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Base class for checkboxes and radios */ require_once 'HTML/QuickForm2/Element/InputCheckable.php'; /** * Class for elements * * @category HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @version Release: @package_version@ */ class HTML_QuickForm2_Element_InputRadio extends HTML_QuickForm2_Element_InputCheckable { protected $attributes = array('type' => 'radio', 'value' => 'on'); protected $frozenHtml = array( 'checked' => '(x)', 'unchecked' => '( )' ); } ?> library/pear/HTML/QuickForm2/Element/Select.php000064400000046205152101576050015217 0ustar00 elements * * PHP version 5 * * LICENSE: * * Copyright (c) 2006-2011, Alexey Borzov , * Bertrand Mansion * 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 names of the authors 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 HTML * @package HTML_QuickForm2 * @author Alexey Borzov * @author Bertrand Mansion * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version SVN: $Id: Select.php 311435 2011-05-26 10:30:06Z avb $ * @link http://pear.php.net/package/HTML_QuickForm2 */ /** * Base class for simple HTML_QuickForm2 elements */ require_once 'HTML/QuickForm2/Element.php'; /** * Collection of s * * This class handles the output of