├── .htaccess ├── README.md ├── data ├── .gitignore └── .htaccess ├── favicon.ico ├── index.php ├── public ├── css │ └── readr.css ├── img │ ├── icons.png │ ├── loading.png │ ├── readr-icon-114.png │ ├── readr-icon-144.png │ ├── readr-tile-144.png │ └── search.png └── js │ ├── backbone-min.js │ ├── jquery-ui-1.10.3.custom.min.js │ ├── jquery.hammer.min.js │ ├── jquery.min.js │ ├── jquery.tagsinput.min.js │ ├── moment.min.js │ ├── readr.js │ ├── underscore-min.js │ └── underscore.string.min.js ├── readr ├── .htaccess ├── src │ └── Readr │ │ ├── App.php │ │ ├── Controller │ │ ├── AbstractController.php │ │ ├── ApiController.php │ │ ├── IndexController.php │ │ ├── LoginController.php │ │ ├── SettingsController.php │ │ └── UpdateController.php │ │ ├── Helper │ │ └── FlashMessenger.php │ │ ├── Model │ │ ├── AbstractModel.php │ │ ├── Entries.php │ │ ├── Feeds.php │ │ ├── Settings.php │ │ └── Tags.php │ │ ├── Opml.php │ │ ├── ServiceManager.php │ │ ├── Updater.php │ │ └── View.php └── views │ ├── index │ └── index.phtml │ ├── layout.phtml │ ├── login │ └── index.phtml │ └── settings │ └── index.phtml └── vendor ├── .htaccess ├── SplClassLoader.php ├── password_compat └── password.php └── simplepie ├── autoloader.php └── library ├── SimplePie.php └── SimplePie ├── Author.php ├── Cache.php ├── Cache ├── Base.php ├── DB.php ├── File.php ├── Memcache.php └── MySQL.php ├── Caption.php ├── Category.php ├── Content └── Type │ └── Sniffer.php ├── Copyright.php ├── Core.php ├── Credit.php ├── Decode └── HTML │ └── Entities.php ├── Enclosure.php ├── Exception.php ├── File.php ├── HTTP └── Parser.php ├── IRI.php ├── Item.php ├── Locator.php ├── Misc.php ├── Net └── IPv6.php ├── Parse └── Date.php ├── Parser.php ├── Rating.php ├── Registry.php ├── Restriction.php ├── Sanitize.php ├── Source.php ├── XML └── Declaration │ └── Parser.php └── gzdecode.php /.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteCond %{REQUEST_FILENAME} -s [OR] 3 | RewriteCond %{REQUEST_FILENAME} -l [OR] 4 | RewriteCond %{REQUEST_FILENAME} -d 5 | RewriteRule ^.*$ - [NC,L] 6 | RewriteRule ^.*$ index.php [NC,L] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Readr 2 | 3 | Readr is a clean & simple, self-hosted RSS reader. 4 | 5 | It is currently under active development, please report any issues you have. 6 | 7 | Readr aims to be as simple as possible to set up and use with no external dependencies and no configuration file to customize. 8 | 9 | Licensed under the GPLv3 license. 10 | 11 | ![Screenshot](http://readr.pabloprieto.net/screenshot.png) 12 | 13 | ## Requirements 14 | 15 | * PHP 5.3.7+ with sqlite enabled 16 | * mod_rewrite Apache module enabled 17 | 18 | ## Installation 19 | 20 | 1. Upload all files to your server. Don't forget the .htaccess file. 21 | 2. Make the directory `/data` writeable 22 | 3. Create a cronjob to update your feeds automatically via curl or wget and point it to http://yourdomain/update 23 | 24 | ## Shortcuts 25 | 26 | * `space` : next entry 27 | * `shift` + `space` : previous entry 28 | * `r` or `m` : mark as read/unread 29 | * `f` or `s` : toggle favorite 30 | * `v` : open the original source in a new window 31 | 32 | ## Credits 33 | 34 | Readr makes use of the following libraries: 35 | 36 | * SimplePie: http://simplepie.org 37 | * password_compat: https://github.com/ircmaxell/password_compat 38 | * jQuery: http://jquery.com 39 | * Backbone.js: http://backbonejs.org 40 | * Moment.js: http://momentjs.com 41 | * Hammer.js: http://eightmedia.github.io/hammer.js/ 42 | 43 | ## Contact 44 | 45 | Pablo Prieto, [pabloprieto.net](http://pabloprieto.net/) 46 | 47 | ## Changelog 48 | 49 | * **v0.9** 50 | - New search box 51 | - New option to auto delete oldest entries 52 | * **v0.8** 53 | - New autocomplete UI for tags 54 | - Export subscriptions as OPML file 55 | * **v0.7** 56 | - 'Emulate HTTP' option added for compatibility with servers that don't support default REST/HTTP approach 57 | - Remember expanded/collapsed state in the feeds menu 58 | * **v0.6** 59 | - Responsive design 60 | - Direct urls to feeds, tags and entries 61 | - Added keyboards shortcuts 62 | * **v0.5.1** 63 | - Security fix 64 | * **v0.5** 65 | - Initial version 66 | 67 | 68 | -------------------------------------------------------------------------------- /data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !.htaccess -------------------------------------------------------------------------------- /data/.htaccess: -------------------------------------------------------------------------------- 1 | Deny from all -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pabloprieto/Readr/55457f6172b2de0b7c12b56d953b76b9c9b66d00/favicon.ico -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | register(); 11 | 12 | $app = new \Readr\App; 13 | $app->run(); -------------------------------------------------------------------------------- /public/img/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pabloprieto/Readr/55457f6172b2de0b7c12b56d953b76b9c9b66d00/public/img/icons.png -------------------------------------------------------------------------------- /public/img/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pabloprieto/Readr/55457f6172b2de0b7c12b56d953b76b9c9b66d00/public/img/loading.png -------------------------------------------------------------------------------- /public/img/readr-icon-114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pabloprieto/Readr/55457f6172b2de0b7c12b56d953b76b9c9b66d00/public/img/readr-icon-114.png -------------------------------------------------------------------------------- /public/img/readr-icon-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pabloprieto/Readr/55457f6172b2de0b7c12b56d953b76b9c9b66d00/public/img/readr-icon-144.png -------------------------------------------------------------------------------- /public/img/readr-tile-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pabloprieto/Readr/55457f6172b2de0b7c12b56d953b76b9c9b66d00/public/img/readr-tile-144.png -------------------------------------------------------------------------------- /public/img/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pabloprieto/Readr/55457f6172b2de0b7c12b56d953b76b9c9b66d00/public/img/search.png -------------------------------------------------------------------------------- /public/js/jquery.tagsinput.min.js: -------------------------------------------------------------------------------- 1 | (function(a){var b=new Array;var c=new Array;a.fn.doAutosize=function(b){var c=a(this).data("minwidth"),d=a(this).data("maxwidth"),e="",f=a(this),g=a("#"+a(this).data("tester_id"));if(e===(e=f.val())){return}var h=e.replace(/&/g,"&").replace(/\s/g," ").replace(//g,">");g.html(h);var i=g.width(),j=i+b.comfortZone>=c?i+b.comfortZone:c,k=f.width(),l=j=c||j>c&&j").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:f.css("fontSize"),fontFamily:f.css("fontFamily"),fontWeight:f.css("fontWeight"),letterSpacing:f.css("letterSpacing"),whiteSpace:"nowrap"}),h=a(this).attr("id")+"_autosize_tester";if(!a("#"+h).length>0){g.attr("id",h);g.appendTo("body")}f.data("minwidth",c);f.data("maxwidth",d);f.data("tester_id",h);f.css("width",c)};a.fn.addTag=function(d,e){e=jQuery.extend({focus:false,callback:true},e);this.each(function(){var f=a(this).attr("id");var g=a(this).val().split(b[f]);if(g[0]==""){g=new Array}d=jQuery.trim(d);if(e.unique){var h=a(g).tagExist(d);if(h==true){a("#"+f+"_tag").addClass("not_valid")}}else{var h=false}if(d!=""&&h!=true){a("").addClass("tag").append(a("").text(d).append("  "),a("",{href:"#",title:"Removing tag",text:"x"}).click(function(){return a("#"+f).removeTag(escape(d))})).insertBefore("#"+f+"_addTag");g.push(d);a("#"+f+"_tag").val("");if(e.focus){a("#"+f+"_tag").focus()}else{a("#"+f+"_tag").blur()}a.fn.tagsInput.updateTagsField(this,g);if(e.callback&&c[f]&&c[f]["onAddTag"]){var i=c[f]["onAddTag"];i.call(this,d)}if(c[f]&&c[f]["onChange"]){var j=g.length;var i=c[f]["onChange"];i.call(this,a(this),g[j-1])}}});return false};a.fn.removeTag=function(d){d=unescape(d);this.each(function(){var e=a(this).attr("id");var f=a(this).val().split(b[e]);a("#"+e+"_tagsinput .tag").remove();str="";for(i=0;i=0};a.fn.importTags=function(b){id=a(this).attr("id");a("#"+id+"_tagsinput .tag").remove();a.fn.tagsInput.importTags(this,b)};a.fn.tagsInput=function(d){var e=jQuery.extend({interactive:true,defaultText:"add a tag",minChars:0,width:"300px",height:"100px",autocomplete:{selectFirst:false},hide:true,delimiter:",",unique:true,removeWithBackspace:true,placeholderColor:"#666666",autosize:true,comfortZone:20,inputPadding:6*2},d);this.each(function(){if(e.hide){a(this).hide()}var d=a(this).attr("id");if(!d||b[a(this).attr("id")]){d=a(this).attr("id","tags"+(new Date).getTime()).attr("id")}var f=jQuery.extend({pid:d,real_input:"#"+d,holder:"#"+d+"_tagsinput",input_wrapper:"#"+d+"_addTag",fake_input:"#"+d+"_tag"},e);b[d]=f.delimiter;if(e.onAddTag||e.onRemoveTag||e.onChange){c[d]=new Array;c[d]["onAddTag"]=e.onAddTag;c[d]["onRemoveTag"]=e.onRemoveTag;c[d]["onChange"]=e.onChange}var g='
';if(e.interactive){g=g+''}g=g+'
';a(g).insertAfter(this);a(f.holder).css("width",e.width);a(f.holder).css("height",e.height);if(a(f.real_input).val()!=""){a.fn.tagsInput.importTags(a(f.real_input),a(f.real_input).val())}if(e.interactive){a(f.fake_input).val(a(f.fake_input).attr("data-default"));a(f.fake_input).css("color",e.placeholderColor);a(f.fake_input).resetAutosize(e);a(f.holder).bind("click",f,function(b){a(b.data.fake_input).focus()});a(f.fake_input).bind("focus",f,function(b){if(a(b.data.fake_input).val()==a(b.data.fake_input).attr("data-default")){a(b.data.fake_input).val("")}a(b.data.fake_input).css("color","#000000")});if(e.autocomplete_url!=undefined){autocomplete_options={source:e.autocomplete_url};for(attrname in e.autocomplete){autocomplete_options[attrname]=e.autocomplete[attrname]}if(jQuery.Autocompleter!==undefined){a(f.fake_input).autocomplete(e.autocomplete_url,e.autocomplete);a(f.fake_input).bind("result",f,function(b,c,f){if(c){a("#"+d).addTag(c[0]+"",{focus:true,unique:e.unique})}})}else if(jQuery.ui.autocomplete!==undefined){a(f.fake_input).autocomplete(autocomplete_options);a(f.fake_input).bind("autocompleteselect",f,function(b,c){a(b.data.real_input).addTag(c.item.value,{focus:true,unique:e.unique});return false})}}else{a(f.fake_input).bind("blur",f,function(b){var c=a(this).attr("data-default");if(a(b.data.fake_input).val()!=""&&a(b.data.fake_input).val()!=c){if(b.data.minChars<=a(b.data.fake_input).val().length&&(!b.data.maxChars||b.data.maxChars>=a(b.data.fake_input).val().length))a(b.data.real_input).addTag(a(b.data.fake_input).val(),{focus:true,unique:e.unique})}else{a(b.data.fake_input).val(a(b.data.fake_input).attr("data-default"));a(b.data.fake_input).css("color",e.placeholderColor)}return false})}a(f.fake_input).bind("keypress",f,function(b){if(b.which==b.data.delimiter.charCodeAt(0)||b.which==13){b.preventDefault();if(b.data.minChars<=a(b.data.fake_input).val().length&&(!b.data.maxChars||b.data.maxChars>=a(b.data.fake_input).val().length))a(b.data.real_input).addTag(a(b.data.fake_input).val(),{focus:true,unique:e.unique});a(b.data.fake_input).resetAutosize(e);return false}else if(b.data.autosize){a(b.data.fake_input).doAutosize(e)}});f.removeWithBackspace&&a(f.fake_input).bind("keydown",function(b){if(b.keyCode==8&&a(this).val()==""){b.preventDefault();var c=a(this).closest(".tagsinput").find(".tag:last").text();var d=a(this).attr("id").replace(/_tag$/,"");c=c.replace(/[\s]+x$/,"");a("#"+d).removeTag(escape(c));a(this).trigger("focus")}});a(f.fake_input).blur();if(f.unique){a(f.fake_input).keydown(function(b){if(b.keyCode==8||String.fromCharCode(b.which).match(/\w+|[áéíóúÁÉÍÓÚñÑ,/]+/)){a(this).removeClass("not_valid")}})}}});return this};a.fn.tagsInput.updateTagsField=function(c,d){var e=a(c).attr("id");a(c).val(d.join(b[e]))};a.fn.tagsInput.importTags=function(d,e){a(d).val("");var f=a(d).attr("id");var g=e.split(b[f]);for(i=0;i0)t&1&&(n+=e),t>>=1,e+=e;return n},u=[].slice,a=function(e){return e==null?"\\s":e.source?e.source:"["+p.escapeRegExp(e)+"]"},f={lt:"<",gt:">",quot:'"',apos:"'",amp:"&"},l={};for(var c in f)l[f[c]]=c;var h=function(){function e(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}var n=o,r=function(){return r.cache.hasOwnProperty(arguments[0])||(r.cache[arguments[0]]=r.parse(arguments[0])),r.format.call(null,r.cache[arguments[0]],arguments)};return r.format=function(r,i){var s=1,o=r.length,u="",a,f=[],l,c,p,d,v,m;for(l=0;l=0?"+"+a:a,v=p[4]?p[4]=="0"?"0":p[4].charAt(1):" ",m=p[6]-t(a).length,d=p[6]?n(v,m):"",f.push(p[5]?a+d:d+a)}}return f.join("")},r.cache={},r.parse=function(e){var t=e,n=[],r=[],i=0;while(t){if((n=/^[^\x25]+/.exec(t))!==null)r.push(n[0]);else if((n=/^\x25{2}/.exec(t))!==null)r.push("%");else{if((n=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(t))===null)throw new Error("[_.sprintf] huh?");if(n[2]){i|=1;var s=[],o=n[2],u=[];if((u=/^([a-z_][a-z_\d]*)/i.exec(o))===null)throw new Error("[_.sprintf] huh?");s.push(u[1]);while((o=o.substring(u[0].length))!=="")if((u=/^\.([a-z_][a-z_\d]*)/i.exec(o))!==null)s.push(u[1]);else{if((u=/^\[(\d+)\]/.exec(o))===null)throw new Error("[_.sprintf] huh?");s.push(u[1])}n[2]=s}else i|=2;if(i===3)throw new Error("[_.sprintf] mixing positional and named placeholders is not (yet) supported");r.push(n)}t=t.substring(n[0].length)}return r},r}(),p={VERSION:"2.3.0",isBlank:function(e){return e==null&&(e=""),/^\s*$/.test(e)},stripTags:function(e){return e==null?"":t(e).replace(/<\/?[^>]+>/g,"")},capitalize:function(e){return e=e==null?"":t(e),e.charAt(0).toUpperCase()+e.slice(1)},chop:function(e,n){return e==null?[]:(e=t(e),n=~~n,n>0?e.match(new RegExp(".{1,"+n+"}","g")):[e])},clean:function(e){return p.strip(e).replace(/\s+/g," ")},count:function(e,n){return e==null||n==null?0:t(e).split(n).length-1},chars:function(e){return e==null?[]:t(e).split("")},swapCase:function(e){return e==null?"":t(e).replace(/\S/g,function(e){return e===e.toUpperCase()?e.toLowerCase():e.toUpperCase()})},escapeHTML:function(e){return e==null?"":t(e).replace(/[&<>"']/g,function(e){return"&"+l[e]+";"})},unescapeHTML:function(e){return e==null?"":t(e).replace(/\&([^;]+);/g,function(e,n){var r;return n in f?f[n]:(r=n.match(/^#x([\da-fA-F]+)$/))?t.fromCharCode(parseInt(r[1],16)):(r=n.match(/^#(\d+)$/))?t.fromCharCode(~~r[1]):e})},escapeRegExp:function(e){return e==null?"":t(e).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")},splice:function(e,t,n,r){var i=p.chars(e);return i.splice(~~t,~~n,r),i.join("")},insert:function(e,t,n){return p.splice(e,t,0,n)},include:function(e,n){return n===""?!0:e==null?!1:t(e).indexOf(n)!==-1},join:function(){var e=u.call(arguments),t=e.shift();return t==null&&(t=""),e.join(t)},lines:function(e){return e==null?[]:t(e).split("\n")},reverse:function(e){return p.chars(e).reverse().join("")},startsWith:function(e,n){return n===""?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(0,n.length)===n)},endsWith:function(e,n){return n===""?!0:e==null||n==null?!1:(e=t(e),n=t(n),e.length>=n.length&&e.slice(e.length-n.length)===n)},succ:function(e){return e==null?"":(e=t(e),e.slice(0,-1)+t.fromCharCode(e.charCodeAt(e.length-1)+1))},titleize:function(e){return e==null?"":t(e).replace(/(?:^|\s)\S/g,function(e){return e.toUpperCase()})},camelize:function(e){return p.trim(e).replace(/[-_\s]+(.)?/g,function(e,t){return t.toUpperCase()})},underscored:function(e){return p.trim(e).replace(/([a-z\d])([A-Z]+)/g,"$1_$2").replace(/[-\s]+/g,"_").toLowerCase()},dasherize:function(e){return p.trim(e).replace(/([A-Z])/g,"-$1").replace(/[-_\s]+/g,"-").toLowerCase()},classify:function(e){return p.titleize(t(e).replace(/_/g," ")).replace(/\s/g,"")},humanize:function(e){return p.capitalize(p.underscored(e).replace(/_id$/,"").replace(/_/g," "))},trim:function(e,r){return e==null?"":!r&&n?n.call(e):(r=a(r),t(e).replace(new RegExp("^"+r+"+|"+r+"+$","g"),""))},ltrim:function(e,n){return e==null?"":!n&&i?i.call(e):(n=a(n),t(e).replace(new RegExp("^"+n+"+"),""))},rtrim:function(e,n){return e==null?"":!n&&r?r.call(e):(n=a(n),t(e).replace(new RegExp(n+"+$"),""))},truncate:function(e,n,r){return e==null?"":(e=t(e),r=r||"...",n=~~n,e.length>n?e.slice(0,n)+r:e)},prune:function(e,n,r){if(e==null)return"";e=t(e),n=~~n,r=r!=null?t(r):"...";if(e.length<=n)return e;var i=function(e){return e.toUpperCase()!==e.toLowerCase()?"A":" "},s=e.slice(0,n+1).replace(/.(?=\W*\w*$)/g,i);return s.slice(s.length-2).match(/\w\w/)?s=s.replace(/\s*\S+$/,""):s=p.rtrim(s.slice(0,s.length-1)),(s+r).length>e.length?e:e.slice(0,s.length)+r},words:function(e,t){return p.isBlank(e)?[]:p.trim(e,t).split(t||/\s+/)},pad:function(e,n,r,i){e=e==null?"":t(e),n=~~n;var s=0;r?r.length>1&&(r=r.charAt(0)):r=" ";switch(i){case"right":return s=n-e.length,e+o(r,s);case"both":return s=n-e.length,o(r,Math.ceil(s/2))+e+o(r,Math.floor(s/2));default:return s=n-e.length,o(r,s)+e}},lpad:function(e,t,n){return p.pad(e,t,n)},rpad:function(e,t,n){return p.pad(e,t,n,"right")},lrpad:function(e,t,n){return p.pad(e,t,n,"both")},sprintf:h,vsprintf:function(e,t){return t.unshift(e),h.apply(null,t)},toNumber:function(e,n){if(e==null||e=="")return 0;e=t(e);var r=s(s(e).toFixed(~~n));return r===0&&!e.match(/^0+$/)?Number.NaN:r},numberFormat:function(e,t,n,r){if(isNaN(e)||e==null)return"";e=e.toFixed(~~t),r=r||",";var i=e.split("."),s=i[0],o=i[1]?(n||".")+i[1]:"";return s.replace(/(\d)(?=(?:\d{3})+$)/g,"$1"+r)+o},strRight:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strRightBack:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.lastIndexOf(n):-1;return~r?e.slice(r+n.length,e.length):e},strLeft:function(e,n){if(e==null)return"";e=t(e),n=n!=null?t(n):n;var r=n?e.indexOf(n):-1;return~r?e.slice(0,r):e},strLeftBack:function(e,t){if(e==null)return"";e+="",t=t!=null?""+t:t;var n=e.lastIndexOf(t);return~n?e.slice(0,n):e},toSentence:function(e,t,n,r){t=t||", ",n=n||" and ";var i=e.slice(),s=i.pop();return e.length>2&&r&&(n=p.rtrim(t)+n),i.length?i.join(t)+n+s:s},toSentenceSerial:function(){var e=u.call(arguments);return e[3]=!0,p.toSentence.apply(p,e)},slugify:function(e){if(e==null)return"";var n="ąàáäâãåæćęèéëêìíïîłńòóöôõøùúüûñçżź",r="aaaaaaaaceeeeeiiiilnoooooouuuunczz",i=new RegExp(a(n),"g");return e=t(e).toLowerCase().replace(i,function(e){var t=n.indexOf(e);return r.charAt(t)||"-"}),p.dasherize(e.replace(/[^\w\s-]/g,""))},surround:function(e,t){return[t,e,t].join("")},quote:function(e){return p.surround(e,'"')},exports:function(){var e={};for(var t in this){if(!this.hasOwnProperty(t)||t.match(/^(?:include|contains|reverse)$/))continue;e[t]=this[t]}return e},repeat:function(e,n,r){if(e==null)return"";n=~~n;if(r==null)return o(t(e),n);for(var i=[];n>0;i[--n]=e);return i.join(r)},levenshtein:function(e,n){if(e==null&&n==null)return 0;if(e==null)return t(n).length;if(n==null)return t(e).length;e=t(e),n=t(n);var r=[],i,s;for(var o=0;o<=n.length;o++)for(var u=0;u<=e.length;u++)o&&u?e.charAt(u-1)===n.charAt(o-1)?s=i:s=Math.min(r[u],r[u-1],i)+1:s=o+u,i=r[u],r[u]=s;return r.pop()}};p.strip=p.trim,p.lstrip=p.ltrim,p.rstrip=p.rtrim,p.center=p.lrpad,p.rjust=p.lpad,p.ljust=p.rpad,p.contains=p.include,p.q=p.quote,typeof exports!="undefined"?(typeof module!="undefined"&&module.exports&&(module.exports=p),exports._s=p):typeof define=="function"&&define.amd?define("underscore.string",[],function(){return p}):(e._=e._||{},e._.string=e._.str=p)}(this,String); -------------------------------------------------------------------------------- /readr/.htaccess: -------------------------------------------------------------------------------- 1 | Deny from all -------------------------------------------------------------------------------- /readr/src/Readr/App.php: -------------------------------------------------------------------------------- 1 | checkInstall(); 34 | $this->checkVersion(); 35 | 36 | try { 37 | 38 | $response = $this->route(); 39 | echo $response; 40 | 41 | } catch (\Exception $e) { 42 | 43 | header($_SERVER['SERVER_PROTOCOL'] . ' ' . $e->getCode()); 44 | die($e->getMessage()); 45 | 46 | } 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public static function getBasePath() 53 | { 54 | if (!self::$basePath) { 55 | $path = dirname($_SERVER['SCRIPT_NAME']); 56 | self::$basePath = rtrim($path, '/') . '/'; 57 | } 58 | 59 | return self::$basePath; 60 | } 61 | 62 | /** 63 | * @return string 64 | */ 65 | public static function getRelease() 66 | { 67 | return array(0,9,1); 68 | } 69 | 70 | /** 71 | * @return int 72 | */ 73 | public static function getVersion() 74 | { 75 | return 2013110901; 76 | } 77 | 78 | /** 79 | * @return string|false 80 | * @throws \RuntimeException 81 | */ 82 | protected function route() 83 | { 84 | $path = $this->getPathInfo(); 85 | 86 | if (empty($path)) { 87 | 88 | $controllerName = 'index'; 89 | $actionName = 'index'; 90 | $args = array(); 91 | 92 | } else { 93 | 94 | $segments = explode('/', $path); 95 | 96 | $controllerName = $segments[0]; 97 | $actionName = isset($segments[1]) ? $segments[1] : 'index'; 98 | $args = filter_var_array(array_slice($segments, 2), FILTER_SANITIZE_STRING); 99 | 100 | } 101 | 102 | $class = '\\Readr\\Controller\\' . ucfirst($controllerName) . 'Controller'; 103 | 104 | if (!class_exists($class)) { 105 | throw new \Exception("Page not found", 404); 106 | } 107 | 108 | $controller = new $class($this->getServiceManager()); 109 | $method = $actionName . 'Action'; 110 | 111 | if (!method_exists($controller, $method)) { 112 | throw new \Exception("Page not found", 404); 113 | } 114 | 115 | $response = call_user_func_array(array($controller, $method), $args); 116 | 117 | if (is_string($response)) { 118 | 119 | return $response; 120 | 121 | } elseif (is_array($response) || is_null($response)) { 122 | 123 | $template = 'readr/views/' . strtolower($controllerName) . '/' . strtolower($actionName) . '.phtml'; 124 | $view = new View($template, $response); 125 | 126 | $layout = new View('readr/views/layout.phtml', array( 127 | 'title' => 'Readr', 128 | 'content' => $view->render() 129 | )); 130 | 131 | return $layout->render(); 132 | 133 | } 134 | 135 | return false; 136 | } 137 | 138 | /** 139 | * @return bool 140 | */ 141 | protected function checkInstall() 142 | { 143 | $sm = $this->getServiceManager(); 144 | $db = $sm->get('db'); 145 | 146 | $statement = $db->query("SELECT COUNT(*) FROM sqlite_master WHERE type='table'"); 147 | $count = (int) $statement->fetchColumn(0); 148 | 149 | if ($count == 0) { 150 | 151 | $db->exec( 152 | 'CREATE TABLE "feeds" ( 153 | "id" INTEGER PRIMARY KEY AUTOINCREMENT, 154 | "title" TEXT NOT NULL, 155 | "url" TEXT NOT NULL UNIQUE, 156 | "link" TEXT, 157 | "last_update" INTEGER, 158 | "last_error" TEXT)' 159 | ); 160 | 161 | $db->exec( 162 | 'CREATE TABLE "tags" ( 163 | "name" TEXT NOT NULL, 164 | "feed_id" INTEGER NOT NULL REFERENCES "feeds"("id") ON DELETE CASCADE, 165 | PRIMARY KEY ("name", "feed_id"))' 166 | ); 167 | 168 | $db->exec( 169 | 'CREATE TABLE "entries" ( 170 | "id" INTEGER PRIMARY KEY AUTOINCREMENT, 171 | "feed_id" INTEGER NOT NULL REFERENCES "feeds"("id") ON DELETE CASCADE, 172 | "title" TEXT NOT NULL, 173 | "content" TEXT NOT NULL, 174 | "author" TEXT, 175 | "link" TEXT NOT NULL UNIQUE, 176 | "date" INTEGER NOT NULL, 177 | "read" INTEGER NOT NULL DEFAULT 0, 178 | "favorite" INTEGER NOT NULL DEFAULT 0)' 179 | ); 180 | 181 | $db->exec( 182 | 'CREATE TABLE "settings" ( 183 | "name" TEXT PRIMARY KEY, 184 | "value" TEXT)' 185 | ); 186 | 187 | $settings = $sm->get('settings'); 188 | $settings->set('version', self::getVersion()); 189 | 190 | return false; 191 | 192 | } 193 | 194 | return true; 195 | } 196 | 197 | /** 198 | * @return void 199 | */ 200 | protected function checkVersion() 201 | { 202 | $settings = $this->getServiceManager()->get('settings'); 203 | $version = intval($settings->get('version')); 204 | 205 | if ($version < self::getVersion()) { 206 | // TODO: run migrations if necessary 207 | $settings->set('version', self::getVersion()); 208 | } 209 | } 210 | 211 | /** 212 | * @return ServiceManager 213 | */ 214 | protected function getServiceManager() 215 | { 216 | if (!$this->serviceManager) { 217 | $this->serviceManager = new ServiceManager(array( 218 | 219 | 'db' => function($sm) { 220 | $db = new PDO('sqlite:data/reader.db'); 221 | $db->exec('PRAGMA foreign_keys=ON'); 222 | return $db; 223 | }, 224 | 225 | 'settings' => function($sm) { 226 | $db = $sm->get('db'); 227 | return new Model\Settings($db); 228 | }, 229 | 230 | 'feeds' => function($sm) { 231 | $db = $sm->get('db'); 232 | return new Model\Feeds($db); 233 | }, 234 | 235 | 'entries' => function($sm) { 236 | $db = $sm->get('db'); 237 | return new Model\Entries($db); 238 | }, 239 | 240 | 'tags' => function($sm) { 241 | $db = $sm->get('db'); 242 | return new Model\Tags($db); 243 | } 244 | 245 | )); 246 | } 247 | 248 | return $this->serviceManager; 249 | } 250 | 251 | /** 252 | * @return string 253 | */ 254 | protected function getPathInfo() 255 | { 256 | if (isset($_SERVER['PATH_INFO'])) { 257 | return ltrim($_SERVER['PATH_INFO'], '/'); 258 | } 259 | 260 | $basePath = self::getBasePath(); 261 | $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); 262 | 263 | return (string) substr($uri, strpos($uri, $basePath) + strlen($basePath)); 264 | } 265 | 266 | } -------------------------------------------------------------------------------- /readr/src/Readr/Controller/AbstractController.php: -------------------------------------------------------------------------------- 1 | serviceManager = $serviceManager; 30 | $this->init(); 31 | } 32 | 33 | public function init(){} 34 | 35 | /** 36 | * @return bool 37 | */ 38 | protected function checkAuth() 39 | { 40 | $settings = $this->getServiceManager()->get('settings'); 41 | $username = $settings->get('username'); 42 | 43 | if (!$username) { 44 | return true; 45 | } 46 | 47 | session_start(); 48 | 49 | if (array_key_exists('username', $_SESSION) && $username == $_SESSION['username']) { 50 | return true; 51 | } 52 | 53 | return false; 54 | } 55 | 56 | /** 57 | * @return ServiceManager 58 | */ 59 | protected function getServiceManager() 60 | { 61 | return $this->serviceManager; 62 | } 63 | 64 | /** 65 | * @param string $name 66 | * @param string $default (default: null) 67 | * @return string|null 68 | */ 69 | protected function getParam($name, $default = null) 70 | { 71 | if (!isset($_REQUEST[$name])) { 72 | return $default; 73 | } 74 | 75 | return filter_var($_REQUEST[$name], FILTER_SANITIZE_STRING); 76 | } 77 | 78 | /** 79 | * @param string $name 80 | * @return array|null 81 | */ 82 | protected function getFile($name) 83 | { 84 | if (isset($_FILES[$name])) { 85 | return $_FILES[$name]; 86 | } 87 | 88 | return null; 89 | } 90 | 91 | /** 92 | * @return array 93 | */ 94 | protected function getQueryData() 95 | { 96 | return filter_var_array($_GET, FILTER_SANITIZE_STRING); 97 | } 98 | 99 | /** 100 | * @return array 101 | */ 102 | protected function getPostData() 103 | { 104 | return filter_var_array($_POST, FILTER_SANITIZE_STRING); 105 | } 106 | 107 | /** 108 | * @return array 109 | */ 110 | protected function getInputData() 111 | { 112 | $content = file_get_contents("php://input"); 113 | return json_decode($content, true); 114 | } 115 | 116 | /** 117 | * @return string 118 | */ 119 | protected function getHttpMethod() 120 | { 121 | if (array_key_exists('HTTP_X_HTTP_METHOD_OVERRIDE', $_SERVER)) { 122 | return strtolower($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); 123 | } 124 | 125 | return strtolower($_SERVER['REQUEST_METHOD']); 126 | } 127 | 128 | /** 129 | * @param string $url (default: '') 130 | * @return void 131 | */ 132 | protected function redirect($url = '') 133 | { 134 | $url = App::getBasePath() . $url; 135 | header('Location: ' . $url); 136 | exit; 137 | } 138 | 139 | } -------------------------------------------------------------------------------- /readr/src/Readr/Controller/ApiController.php: -------------------------------------------------------------------------------- 1 | checkAuth()) { 20 | throw new \Exception(json_encode(array( 21 | 'error' => 'Authentication required' 22 | )), 403); 23 | }; 24 | } 25 | 26 | public function feedsAction($id = null) 27 | { 28 | header('Content-type: application/json'); 29 | 30 | $method = $this->getHttpMethod(); 31 | $model = $this->getServiceManager()->get('feeds'); 32 | 33 | if ($id) { 34 | $feed = $model->fetch($id); 35 | 36 | if (!$feed) { 37 | throw new \Exception(json_encode(array( 38 | 'error' => 'Feed not found' 39 | )), 404); 40 | } 41 | } 42 | 43 | $tagsModel = $this->getServiceManager()->get('tags'); 44 | 45 | switch ($method) { 46 | 47 | case 'put': 48 | case 'patch': 49 | if ($id) { 50 | $data = $this->getInputData(); 51 | $result = $model->update( 52 | $id, 53 | $data['title'], 54 | $data['url'] 55 | ); 56 | 57 | $tagsModel->remove($id); 58 | 59 | if ($data['tags']) { 60 | $tags = explode(',', $data['tags']); 61 | foreach ($tags as $tag) { 62 | $tagsModel->insert($tag, $id); 63 | } 64 | } 65 | } 66 | 67 | break; 68 | 69 | case 'post': 70 | $data = $this->getInputData(); 71 | 72 | $simplePie = new SimplePie(); 73 | $simplePie->enable_cache(false); 74 | $simplePie->set_feed_url($data['url']); 75 | $result = $simplePie->init(); 76 | 77 | if (!$result) { 78 | throw new \Exception(json_encode(array( 79 | 'error' => $simplePie->error() 80 | )), 400); 81 | } 82 | 83 | if ($feeds = $simplePie->get_all_discovered_feeds()) { 84 | if (is_array($feeds)) $feed = $feeds[0]; 85 | else $feed = $feeds; 86 | 87 | $simplePie->set_file($feed); 88 | $simplePie->init(); 89 | 90 | $data['url'] = $feed->url; 91 | } 92 | 93 | $data['title'] = $simplePie->get_title(); 94 | $data['link'] = $simplePie->get_permalink(); 95 | 96 | $result = $model->insert( 97 | $data['title'], 98 | $data['url'], 99 | $data['link'] 100 | ); 101 | 102 | if (!$result) { 103 | throw new \Exception(json_encode(array( 104 | 'error' => 'A feed with the same url is already registered' 105 | )), 400); 106 | } 107 | 108 | $id = $model->lastInsertId(); 109 | 110 | if ($data['tags']) { 111 | $tags = explode(',', $data['tags']); 112 | foreach ($tags as $tag) { 113 | $tagsModel->insert($tag, $id); 114 | } 115 | } 116 | 117 | $items = $simplePie->get_items(); 118 | 119 | foreach ($items as $item) { 120 | if (!$item) continue; 121 | 122 | $author = $item->get_author(); 123 | $entriesModel = $this->getServiceManager()->get('entries'); 124 | $entriesModel->insert( 125 | $id, 126 | $item->get_title(), 127 | $item->get_content(), 128 | $author ? $author->get_name() : null, 129 | $item->get_permalink(), 130 | $item->get_date('U') 131 | ); 132 | } 133 | 134 | $model->setUpdateData( 135 | $id, 136 | time() 137 | ); 138 | 139 | $feed = $model->fetch($id); 140 | return json_encode($feed); 141 | 142 | case 'delete': 143 | if ($id) { 144 | $result = $model->delete($id); 145 | } 146 | 147 | break; 148 | 149 | case 'get': 150 | default: 151 | if ($id) { 152 | return json_encode($feed); 153 | } else { 154 | $feeds = $model->fetchAll(); 155 | return json_encode($feeds); 156 | } 157 | 158 | } 159 | 160 | return false; 161 | } 162 | 163 | public function tagsAction($name = null) 164 | { 165 | header('Content-type: application/json'); 166 | 167 | $method = $this->getHttpMethod(); 168 | $model = $this->getServiceManager()->get('tags'); 169 | 170 | if ($name) { 171 | $name = urldecode($name); 172 | $tag = $model->fetch($name); 173 | 174 | if (!$tag) { 175 | throw new \Exception(json_encode(array( 176 | 'error' => 'Tag not found' 177 | )), 404); 178 | } 179 | } 180 | 181 | switch ($method) { 182 | 183 | case 'put': 184 | if ($name) { 185 | $name = urldecode($name); 186 | $data = $this->getInputData(); 187 | $model->update($name, $data['name']); 188 | } 189 | 190 | break; 191 | 192 | case 'delete': 193 | if ($name) { 194 | $name = urldecode($name); 195 | $model->delete($name); 196 | } 197 | 198 | break; 199 | 200 | case 'get': 201 | default: 202 | if ($name) { 203 | return json_encode($tag); 204 | } elseif ($term = $this->getParam('term')) { 205 | $tags = $model->search($term); 206 | } else { 207 | $tags = $model->fetchAll(); 208 | } 209 | 210 | return json_encode($tags); 211 | 212 | } 213 | 214 | return false; 215 | } 216 | 217 | public function entriesAction($id = null) 218 | { 219 | header('Content-type: application/json'); 220 | 221 | $method = $this->getHttpMethod(); 222 | $model = $this->getServiceManager()->get('entries'); 223 | 224 | if ($id) { 225 | $entry = $model->fetch($id); 226 | 227 | if (!$entry) { 228 | throw new \Exception(json_encode(array( 229 | 'error' => 'Entry not found' 230 | )), 404); 231 | } 232 | } 233 | 234 | switch ($method) { 235 | 236 | case 'put': 237 | case 'patch': 238 | 239 | $data = $this->getInputData(); 240 | 241 | if (isset($data['read'])) { 242 | if ($id) { 243 | $model->updateReadStatus($data['read'], $id); 244 | } elseif (isset($data['feed_id'])) { 245 | $model->updateReadStatus($data['read'], null, $data['feed_id']); 246 | } elseif (isset($data['tag'])) { 247 | $model->updateReadStatus($data['read'], null, null, $data['tag']); 248 | } else { 249 | $model->updateReadStatus($data['read']); 250 | } 251 | } elseif ($id && isset($data['favorite'])) { 252 | $model->updateFavoriteStatus($data['favorite'], $id); 253 | } 254 | 255 | break; 256 | 257 | case 'get': 258 | default: 259 | 260 | if ($id) { 261 | return json_encode($entry); 262 | } else { 263 | $offset = $this->getParam('offset', 0); 264 | $limit = $this->getParam('limit', 50); 265 | 266 | $params = $this->getQueryData(); 267 | unset($params['offset']); 268 | unset($params['limit']); 269 | 270 | $entries = $model->fetchAll($limit, $offset, $params); 271 | return json_encode($entries); 272 | } 273 | 274 | } 275 | 276 | return false; 277 | } 278 | 279 | public function faviconAction($feed_id) 280 | { 281 | $model = $this->getServiceManager()->get('feeds'); 282 | $feed = $model->fetch($feed_id); 283 | 284 | if (!$feed) { 285 | throw new \Exception(json_encode(array( 286 | 'error' => 'Feed not found' 287 | )), 404); 288 | } 289 | 290 | $domain = parse_url($feed['link'], PHP_URL_HOST); 291 | 292 | header('HTTP/1.1 301'); 293 | header('Location: https://plus.google.com/_/favicon?domain=' . $domain); 294 | exit(); 295 | } 296 | 297 | } -------------------------------------------------------------------------------- /readr/src/Readr/Controller/IndexController.php: -------------------------------------------------------------------------------- 1 | checkAuth()) { 18 | $this->redirect('login'); 19 | } 20 | } 21 | 22 | public function indexAction() 23 | { 24 | $settings = $this->getServiceManager()->get('settings'); 25 | $feeds = $this->getServiceManager()->get('feeds'); 26 | 27 | return array( 28 | 'feeds' => $feeds->fetchAll(), 29 | 'username' => $settings->get('username'), 30 | 'emulateHTTP' => $settings->get('emulateHTTP', 0), 31 | 'collapsed' => $settings->get('collapsed', '{}') 32 | ); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /readr/src/Readr/Controller/LoginController.php: -------------------------------------------------------------------------------- 1 | getPostData(); 18 | $errors = array(); 19 | 20 | if (!empty($data)) { 21 | 22 | $auth = $this->checkCredentials( 23 | $data['username'], 24 | $data['password'] 25 | ); 26 | 27 | if ($auth) { 28 | return $this->redirect(); 29 | } else { 30 | $errors[] = 'Wrong username or password.'; 31 | } 32 | 33 | } 34 | 35 | return array( 36 | 'errors' => $errors 37 | ); 38 | } 39 | 40 | public function signoutAction() 41 | { 42 | session_start(); 43 | session_destroy(); 44 | $this->redirect(); 45 | } 46 | 47 | /** 48 | * @param string $username 49 | * @param string $password 50 | * @return bool 51 | */ 52 | protected function checkCredentials($username, $password) 53 | { 54 | $settings = $this->getServiceManager()->get('settings'); 55 | 56 | if ( 57 | $username == $settings->get('username') && 58 | password_verify($password, $settings->get('password')) 59 | ) { 60 | session_start(); 61 | session_set_cookie_params(86400*30); 62 | session_regenerate_id(true); 63 | $_SESSION['username'] = $username; 64 | return true; 65 | } 66 | 67 | return false; 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /readr/src/Readr/Controller/SettingsController.php: -------------------------------------------------------------------------------- 1 | checkAuth()) { 23 | $this->redirect('login'); 24 | } 25 | } 26 | 27 | public function indexAction() 28 | { 29 | $settings = $this->getServiceManager()->get('settings'); 30 | $messenger = new FlashMessenger(); 31 | 32 | return array( 33 | 'messenger' => $messenger, 34 | 'username' => $settings->get('username'), 35 | 'emulateHTTP' => $settings->get('emulateHTTP', 0), 36 | 'deleteAfter' => $settings->get('deleteAfter', null), 37 | 'release' => implode('.', App::getRelease()), 38 | 'version' => App::getVersion() 39 | ); 40 | } 41 | 42 | public function authAction() 43 | { 44 | $settings = $this->getServiceManager()->get('settings'); 45 | $data = $this->getPostData(); 46 | 47 | if (isset($data['username']) && !empty($data['username'])) { 48 | 49 | $messenger = new FlashMessenger(); 50 | 51 | if (empty($data['password'])) { 52 | 53 | $messenger->add('Password is empty', 'auth-error'); 54 | 55 | } elseif ($data['password'] == $data['password_confirm']) { 56 | 57 | $hash = password_hash($data['password'], PASSWORD_DEFAULT); 58 | 59 | $settings->set('username', $data['username']); 60 | $settings->set('password', $hash); 61 | 62 | } else { 63 | 64 | $messenger->add('Password and confirmation do not match.', 'auth-error'); 65 | 66 | } 67 | 68 | } else { 69 | 70 | if (isset($_SESSION)) { 71 | unset($_SESSION['username']); 72 | } 73 | 74 | $settings->delete('username'); 75 | $settings->delete('password'); 76 | 77 | } 78 | 79 | $this->redirect('settings'); 80 | } 81 | 82 | public function miscAction() 83 | { 84 | $settings = $this->getServiceManager()->get('settings'); 85 | $data = $this->getPostData(); 86 | 87 | if (isset($data['emulateHTTP'])) { 88 | $settings->set('emulateHTTP', 1); 89 | } else { 90 | $settings->delete('emulateHTTP'); 91 | } 92 | 93 | $deleteAfter = intval($data['deleteAfter']); 94 | if ($deleteAfter > 0) { 95 | $settings->set('deleteAfter', $deleteAfter); 96 | } else { 97 | $settings->delete('deleteAfter'); 98 | } 99 | 100 | $this->redirect('settings'); 101 | } 102 | 103 | public function collapsedAction() 104 | { 105 | $data = $this->getPostData(); 106 | 107 | if (isset($data['name'])) { 108 | 109 | $settings = $this->getServiceManager()->get('settings'); 110 | $collapsed = json_decode($settings->get('collapsed', '{}')); 111 | 112 | if (!$data['collapsed']) { 113 | unset($collapsed->$data['name']); 114 | } else { 115 | $collapsed->$data['name'] = 1; 116 | } 117 | 118 | $settings->set('collapsed', json_encode($collapsed)); 119 | 120 | } 121 | 122 | return false; 123 | } 124 | 125 | public function importAction() 126 | { 127 | $file = $this->getFile('file'); 128 | $messenger = new FlashMessenger(); 129 | 130 | if (!$file || $file['error'] > 0) { 131 | $messenger->add(sprintf('File \'%s\' has not been correctly uploaded.', $file['name']), 'import-error'); 132 | $this->redirect('settings'); 133 | } 134 | 135 | $subscriptions = @simplexml_load_file($file['tmp_name']); 136 | if (!$subscriptions || !$subscriptions->body) { 137 | $messenger->add(sprintf('\'%s\' is not a valid OPML file.', $file['name']), 'import-error'); 138 | $this->redirect('settings'); 139 | } 140 | 141 | $feeds = $this->getServiceManager()->get('feeds'); 142 | $tags = $this->getServiceManager()->get('tags'); 143 | $entries = $this->getServiceManager()->get('entries'); 144 | 145 | $opml = new Opml(); 146 | $opml->process($subscriptions->body, $feeds, $tags); 147 | 148 | $updater = new Updater( 149 | $feeds, 150 | $entries 151 | ); 152 | 153 | $updater->update(); 154 | 155 | return $this->redirect(''); 156 | } 157 | 158 | public function exportAction() 159 | { 160 | $feeds = $this->getServiceManager()->get('feeds'); 161 | 162 | $opml = new Opml(); 163 | $xml = $opml->create($feeds); 164 | 165 | header('Content-Type: application/xml'); 166 | header('Content-Disposition: attachment; filename="subscriptions.xml"'); 167 | 168 | return $xml; 169 | } 170 | 171 | } -------------------------------------------------------------------------------- /readr/src/Readr/Controller/UpdateController.php: -------------------------------------------------------------------------------- 1 | insertNewEntries(); 20 | $this->deleteOldEntries(); 21 | 22 | return "Done." . PHP_EOL; 23 | } 24 | 25 | protected function insertNewEntries() 26 | { 27 | $feedsModel = $this->getServiceManager()->get('feeds'); 28 | $entriesModel = $this->getServiceManager()->get('entries'); 29 | 30 | $updater = new Updater( 31 | $feedsModel, 32 | $entriesModel 33 | ); 34 | 35 | $updater->update(1000); 36 | } 37 | 38 | protected function deleteOldEntries() 39 | { 40 | $settings = $this->getServiceManager()->get('settings'); 41 | $deleteAfter = intval($settings->get('deleteAfter', 0)); 42 | 43 | if ($deleteAfter > 0) { 44 | $timestamp = time() - $deleteAfter * 86400; 45 | 46 | $entriesModel = $this->getServiceManager()->get('entries'); 47 | $entriesModel->deleteAll($timestamp); 48 | } 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /readr/src/Readr/Helper/FlashMessenger.php: -------------------------------------------------------------------------------- 1 | prefix . $namespace; 32 | 33 | if (!isset($_SESSION[$key])) { 34 | $_SESSION[$key] = array(); 35 | } 36 | 37 | $_SESSION[$key][] = $message; 38 | 39 | return $this; 40 | } 41 | 42 | /** 43 | * @param string $namespace (default: 'default') 44 | * @return bool 45 | */ 46 | public function hasMessages($namespace = 'default') 47 | { 48 | $key = $this->prefix . $namespace; 49 | return isset($_SESSION[$key]) && count($_SESSION[$key]) > 0; 50 | } 51 | 52 | /** 53 | * @param string $namespace (default: 'default') 54 | * @param bool $clear (default: true) 55 | * @return array|null 56 | */ 57 | public function getMessages($namespace = 'default', $clear = true) 58 | { 59 | $key = $this->prefix . $namespace; 60 | 61 | if (!isset($_SESSION[$key])) { 62 | return null; 63 | } 64 | 65 | $messages = $_SESSION[$key]; 66 | 67 | if ($clear) { 68 | unset($_SESSION[$key]); 69 | } 70 | 71 | return $messages; 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /readr/src/Readr/Model/AbstractModel.php: -------------------------------------------------------------------------------- 1 | db = $db; 28 | } 29 | 30 | /** 31 | * @return string 32 | */ 33 | public function lastInsertId() 34 | { 35 | return $this->getDb()->lastInsertId(); 36 | } 37 | 38 | /** 39 | * @return array 40 | */ 41 | public function errorInfo() 42 | { 43 | return $this->getDb()->errorInfo(); 44 | } 45 | 46 | /** 47 | * @return PDO 48 | */ 49 | protected function getDb() 50 | { 51 | return $this->db; 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /readr/src/Readr/Model/Entries.php: -------------------------------------------------------------------------------- 1 | getDb()->prepare($sql); 24 | $statement->execute(array( 25 | ':id' => $id 26 | )); 27 | 28 | return $statement->fetch(PDO::FETCH_ASSOC); 29 | } 30 | 31 | public function fetchAll($limit = 50, $offset = 0, $filters = array()) 32 | { 33 | $columns = array( 34 | 'entries.id', 35 | 'entries.feed_id', 36 | 'entries.title', 37 | 'entries.date', 38 | 'entries.read', 39 | 'entries.favorite', 40 | 'feeds.title AS feed_title', 41 | 'GROUP_CONCAT(tags.name,\',\') AS tags' 42 | ); 43 | 44 | $where = array(); 45 | 46 | if (isset($filters['feed_id'])) { 47 | $filters['entries.feed_id'] = $filters['feed_id']; 48 | unset($filters['feed_id']); 49 | } 50 | 51 | if (isset($filters['tag'])) { 52 | $filters['tags.name'] = $filters['tag']; 53 | unset($filters['tag']); 54 | } 55 | 56 | if (isset($filters['q'])) { 57 | $value = $this->getDb()->quote('%' . $filters['q'] . '%'); 58 | $where[] = sprintf("(entries.title LIKE %s OR entries.content LIKE %s)", $value, $value); 59 | unset($filters['q']); 60 | } 61 | 62 | foreach ($filters as $name => $value) { 63 | $where[] = sprintf("$name = %s", $this->getDb()->quote($value)); 64 | } 65 | 66 | $sql = "SELECT " . implode(', ', $columns) . " FROM entries"; 67 | $sql .= " JOIN feeds ON feeds.id = entries.feed_id"; 68 | $sql .= " LEFT JOIN tags ON feeds.id = tags.feed_id"; 69 | 70 | if (count($where)) { 71 | $sql .= " WHERE " . implode(' AND ', $where); 72 | } 73 | 74 | $sql .= " GROUP BY entries.id ORDER BY date DESC LIMIT :limit OFFSET :offset"; 75 | 76 | $statement = $this->getDb()->prepare($sql); 77 | $statement->execute(array( 78 | ':limit' => $limit, 79 | ':offset' => $offset 80 | )); 81 | 82 | return $statement->fetchAll(PDO::FETCH_ASSOC); 83 | } 84 | 85 | public function insert($feed_id, $title, $content, $author, $link, $date) 86 | { 87 | $sql = "INSERT INTO entries (feed_id, title, content, author, link, date) 88 | VALUES (:feed_id, :title, :content, :author, :link, :date)"; 89 | 90 | $statement = $this->getDb()->prepare($sql); 91 | $statement->execute(array( 92 | ':feed_id' => $feed_id, 93 | ':title' => $title, 94 | ':content' => $content, 95 | ':author' => $author, 96 | ':link' => $link, 97 | ':date' => $date 98 | )); 99 | 100 | return $statement->rowCount(); 101 | } 102 | 103 | public function updateReadStatus($read, $id = null, $feed_id = null, $tag_name = null) 104 | { 105 | $sql = "UPDATE entries SET read = :read"; 106 | $params = array(':read' => $read ? 1 : 0); 107 | 108 | if ($id) { 109 | $sql .= " WHERE id = :id"; 110 | $params[':id'] = $id; 111 | } elseif ($feed_id) { 112 | $sql .= " WHERE feed_id = :feed_id"; 113 | $params[':feed_id'] = $feed_id; 114 | } elseif ($tag_name) { 115 | $sql .= " WHERE feed_id IN (SELECT feed_id FROM tags WHERE name = :tag_name)"; 116 | $params[':tag_name'] = $tag_name; 117 | } 118 | 119 | $statement = $this->getDb()->prepare($sql); 120 | $statement->execute($params); 121 | 122 | return $statement->rowCount(); 123 | } 124 | 125 | public function updateFavoriteStatus($favorite, $id) 126 | { 127 | $statement = $this->getDb()->prepare("UPDATE entries SET favorite = :favorite WHERE id = :id"); 128 | $statement->execute(array( 129 | ':favorite' => $favorite, 130 | ':id' => $id 131 | )); 132 | 133 | return $statement->rowCount(); 134 | } 135 | 136 | public function deleteAll($date) 137 | { 138 | $statement = $this->getDb()->prepare("DELETE FROM entries WHERE date < :date"); 139 | $statement->execute(array( 140 | ':date' => $date, 141 | )); 142 | 143 | return $statement->rowCount(); 144 | } 145 | 146 | } -------------------------------------------------------------------------------- /readr/src/Readr/Model/Feeds.php: -------------------------------------------------------------------------------- 1 | getDb()->prepare($sql); 24 | $statement->execute(array( 25 | ':id' => $id 26 | )); 27 | 28 | return $statement->fetch(PDO::FETCH_ASSOC); 29 | } 30 | 31 | public function fetchAll($limit = -1, $offset = 0, $order = 'title ASC') 32 | { 33 | $sql = "SELECT 34 | feeds.*, 35 | (SELECT GROUP_CONCAT(tags.name, ',') FROM tags WHERE tags.feed_id = feeds.id) AS tags, 36 | COUNT(entries.id) AS entries_count, 37 | SUM(CASE WHEN entries.read = 0 THEN 1 ELSE 0 END) AS unread_count 38 | FROM feeds 39 | LEFT JOIN entries ON entries.feed_id = feeds.id 40 | GROUP BY feeds.id 41 | ORDER BY {$order} LIMIT :limit OFFSET :offset"; 42 | 43 | $statement = $this->getDb()->prepare($sql); 44 | $statement->execute(array( 45 | ':limit' => $limit, 46 | ':offset' => $offset 47 | )); 48 | 49 | return $statement->fetchAll(PDO::FETCH_ASSOC); 50 | } 51 | 52 | public function insert($title, $url, $link) 53 | { 54 | $statement = $this->getDb()->prepare("INSERT INTO feeds (title,url,link) VALUES (:title,:url,:link)"); 55 | $statement->execute(array( 56 | ':title' => trim($title), 57 | ':url' => trim($url), 58 | ':link' => trim($link) 59 | )); 60 | 61 | return $statement->rowCount(); 62 | } 63 | 64 | public function update($id, $title, $url) 65 | { 66 | $statement = $this->getDb()->prepare("UPDATE feeds SET title = :title, url = :url WHERE id = :id"); 67 | $statement->execute(array( 68 | ':title' => trim($title), 69 | ':url' => trim($url), 70 | ':id' => $id 71 | )); 72 | 73 | return $statement->rowCount(); 74 | } 75 | 76 | public function setUpdateData($id, $timestamp, $error = null) 77 | { 78 | $statement = $this->getDb()->prepare("UPDATE feeds SET last_update = :timestamp, last_error = :error WHERE id = :id"); 79 | $statement->execute(array( 80 | ':id' => $id, 81 | ':timestamp' => $timestamp, 82 | ':error' => $error 83 | )); 84 | 85 | return $statement->rowCount(); 86 | } 87 | 88 | public function delete($id) 89 | { 90 | $statement = $this->getDb()->prepare("DELETE FROM feeds WHERE id = :id"); 91 | $statement->execute(array( 92 | ':id' => $id 93 | )); 94 | 95 | return $statement->rowCount(); 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /readr/src/Readr/Model/Settings.php: -------------------------------------------------------------------------------- 1 | getDb()->prepare($sql); 22 | $statement->execute(array( 23 | ':name' => $name 24 | )); 25 | 26 | $row = $statement->fetch(PDO::FETCH_ASSOC); 27 | return empty($row) ? $default : $row['value']; 28 | } 29 | 30 | public function set($name, $value) 31 | { 32 | $sql = "INSERT OR REPLACE INTO settings (name,value) VALUES (:name,:value)"; 33 | 34 | $statement = $this->getDb()->prepare($sql); 35 | $statement->execute(array( 36 | ':name' => trim($name), 37 | ':value' => trim($value) 38 | )); 39 | 40 | return $statement->rowCount(); 41 | } 42 | 43 | public function delete($name) 44 | { 45 | $sql = "DELETE FROM settings WHERE name = :name"; 46 | 47 | $statement = $this->getDb()->prepare($sql); 48 | $statement->execute(array( 49 | ':name' => trim($name) 50 | )); 51 | 52 | return $statement->rowCount(); 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /readr/src/Readr/Model/Tags.php: -------------------------------------------------------------------------------- 1 | getDb()->prepare("SELECT name, COUNT(feed_id) AS feeds_count FROM tags WHERE name = :name GROUP BY name"); 20 | $statement->execute(array( 21 | ':name' => trim($name) 22 | )); 23 | 24 | return $statement->fetch(PDO::FETCH_ASSOC); 25 | } 26 | 27 | public function fetchAll() 28 | { 29 | $statement = $this->getDb()->prepare("SELECT name, COUNT(feed_id) AS feeds_count FROM tags GROUP BY name"); 30 | $statement->execute(); 31 | return $statement->fetchAll(PDO::FETCH_ASSOC); 32 | } 33 | 34 | public function search($value) 35 | { 36 | $statement = $this->getDb()->prepare("SELECT DISTINCT name AS label FROM tags WHERE name LIKE :value"); 37 | $statement->execute(array('value' => $value . '%')); 38 | return $statement->fetchAll(PDO::FETCH_ASSOC); 39 | } 40 | 41 | public function insert($name, $feed_id) 42 | { 43 | $statement = $this->getDb()->prepare("INSERT INTO tags (name,feed_id) VALUES (:name,:feed_id)"); 44 | $statement->execute(array( 45 | ':name' => trim($name), 46 | ':feed_id' => $feed_id 47 | )); 48 | 49 | return $statement->rowCount(); 50 | } 51 | 52 | public function update($name, $newName) 53 | { 54 | $statement = $this->getDb()->prepare("UPDATE tags SET name = :newName WHERE name = :name"); 55 | $statement->execute(array( 56 | ':name' => trim($name), 57 | ':newName' => trim($newName) 58 | )); 59 | 60 | return $statement->rowCount(); 61 | } 62 | 63 | public function remove($feed_id) 64 | { 65 | $statement = $this->getDb()->prepare("DELETE FROM tags WHERE feed_id = :feed_id"); 66 | $statement->execute(array( 67 | ':feed_id' => $feed_id 68 | )); 69 | 70 | return $statement->rowCount(); 71 | } 72 | 73 | public function delete($name) 74 | { 75 | $statement = $this->getDb()->prepare("DELETE FROM tags WHERE name = :name"); 76 | $statement->execute(array( 77 | ':name' => trim($name) 78 | )); 79 | 80 | return $statement->rowCount(); 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /readr/src/Readr/Opml.php: -------------------------------------------------------------------------------- 1 | attributes()->text; 28 | 29 | foreach ($xml->outline as $outline) { 30 | 31 | $type = (string) $outline->attributes()->type; 32 | 33 | if ($type == 'rss') { 34 | 35 | $result = $feeds->insert( 36 | (string) $outline->attributes()->text, 37 | (string) $outline->attributes()->xmlUrl, 38 | (string) $outline->attributes()->htmlUrl 39 | ); 40 | 41 | if ($result && $title) { 42 | $tags->insert( 43 | $title, 44 | $feeds->lastInsertId() 45 | ); 46 | } 47 | 48 | } else { 49 | 50 | $this->process($outline, $feeds, $tags); 51 | 52 | } 53 | 54 | } 55 | } 56 | 57 | /** 58 | * @param Feeds $feeds 59 | * @return string 60 | */ 61 | public function create(Feeds $feeds) 62 | { 63 | $items = $feeds->fetchAll(); 64 | $tags = array(); 65 | $unclassified = array(); 66 | 67 | foreach ($items as $item) { 68 | $t = array_filter(explode(',', $item['tags']), 'strlen'); 69 | 70 | if (!empty($t)) { 71 | foreach ($t as $tag) { 72 | if (!isset($tags[$tag])) $tags[$tag] = array(); 73 | $tags[$tag][] = $item; 74 | } 75 | } else { 76 | $unclassified[] = $item; 77 | } 78 | } 79 | 80 | ksort($tags); 81 | 82 | $xml = '' . PHP_EOL; 83 | $xml .= '' . PHP_EOL; 84 | $xml .= 'Readr subscriptions' . PHP_EOL; 85 | $xml .= '' . PHP_EOL; 86 | 87 | foreach ($tags as $tag => $items) { 88 | 89 | $tag = htmlspecialchars($tag); 90 | $xml .= sprintf('', $tag, $tag) . PHP_EOL; 91 | 92 | foreach ($items as $item) { 93 | $title = htmlspecialchars($item['title']); 94 | $xml .= sprintf( 95 | ' ', 96 | $title, 97 | $title, 98 | $item['url'], 99 | $item['link'] 100 | ) . PHP_EOL; 101 | } 102 | 103 | $xml .= '' . PHP_EOL; 104 | 105 | } 106 | 107 | foreach ($unclassified as $item) { 108 | 109 | $title = htmlspecialchars($item['title']); 110 | $xml .= sprintf( 111 | '', 112 | $title, 113 | $title, 114 | $item['url'], 115 | $item['link'] 116 | ) . PHP_EOL; 117 | 118 | } 119 | 120 | $xml .= '' . PHP_EOL; 121 | $xml .= ''; 122 | 123 | return $xml; 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /readr/src/Readr/ServiceManager.php: -------------------------------------------------------------------------------- 1 | factories = $factories; 33 | $this->instances = array(); 34 | } 35 | 36 | /** 37 | * @param string $name 38 | * @param callable $factory 39 | * @return void 40 | */ 41 | public function add($name, $factory) 42 | { 43 | $this->factories[$name] = $factory; 44 | } 45 | 46 | /** 47 | * @param string $name 48 | * @return mixed 49 | */ 50 | public function get($name) 51 | { 52 | if (!array_key_exists($name, $this->instances)) { 53 | $this->create($name); 54 | } 55 | 56 | return $this->instances[$name]; 57 | } 58 | 59 | /** 60 | * @param string $name 61 | * @return bool 62 | */ 63 | public function has($name) 64 | { 65 | return array_key_exists($name, $this->factories); 66 | } 67 | 68 | /** 69 | * @param string $name 70 | * @return void 71 | * @throws RuntimeException 72 | */ 73 | protected function create($name) 74 | { 75 | if (!$this->has($name)) { 76 | throw new RuntimeException(sprintf( 77 | "%s: Invalid service name '%s'.", 78 | get_class($this), 79 | $name 80 | )); 81 | } 82 | 83 | if (!is_callable($this->factories[$name])) { 84 | throw new RuntimeException(sprintf( 85 | "%s: Can't create service '%s', factory is not callable.", 86 | get_class($this), 87 | $name 88 | )); 89 | } 90 | 91 | $this->instances[$name] = $this->factories[$name]($this); 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /readr/src/Readr/Updater.php: -------------------------------------------------------------------------------- 1 | feedsModel = $feedsModel; 36 | $this->entriesModel = $entriesModel; 37 | } 38 | 39 | /** 40 | * @param int $limit (default: 1000) 41 | * @return void 42 | */ 43 | public function update($limit = 1000) 44 | { 45 | @set_time_limit(600); 46 | @error_reporting(E_ERROR); 47 | 48 | $feeds = $this->feedsModel->fetchAll($limit, 0, 'last_update ASC'); 49 | 50 | $simplePie = new SimplePie(); 51 | $simplePie->enable_cache(false); 52 | 53 | foreach ($feeds as $feed) { 54 | 55 | $simplePie->set_feed_url($feed['url']); 56 | $result = $simplePie->init(); 57 | 58 | if ($result) { 59 | 60 | $items = $simplePie->get_items(); 61 | 62 | foreach ($items as $item) { 63 | if (!$item) continue; 64 | 65 | $author = $item->get_author(); 66 | 67 | $this->entriesModel->insert( 68 | $feed['id'], 69 | $item->get_title(), 70 | $item->get_content(), 71 | $author ? $author->get_name() : null, 72 | $item->get_permalink(), 73 | $item->get_date('U') 74 | ); 75 | } 76 | 77 | } 78 | 79 | $this->feedsModel->setUpdateData( 80 | $feed['id'], 81 | time(), 82 | $result ? null : $simplePie->error() 83 | ); 84 | 85 | } 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /readr/src/Readr/View.php: -------------------------------------------------------------------------------- 1 | path = $path; 32 | $this->data = $data; 33 | } 34 | 35 | public function __get($name) 36 | { 37 | return isset($this->data[$name]) ? $this->data[$name] : null; 38 | } 39 | 40 | public function __set($name, $value) 41 | { 42 | if (!$this->data) $this->data = array(); 43 | $this->data[$name] = $value; 44 | } 45 | 46 | /** 47 | * @return string 48 | * @throws \RuntimeException 49 | */ 50 | public function render() 51 | { 52 | if(!file_exists($this->path)){ 53 | throw new \RuntimeException(sprintf( 54 | "Can't render template '%s', file does not exists.", 55 | $this->path 56 | )); 57 | } 58 | 59 | ob_start(); 60 | include $this->path; 61 | return ob_get_clean(); 62 | } 63 | 64 | /** 65 | * @param string $value 66 | * @return string 67 | */ 68 | public function escape($value) 69 | { 70 | return htmlspecialchars($value); 71 | } 72 | 73 | /** 74 | * @param string $file (default: '') 75 | * @return string 76 | */ 77 | protected function basePath($file = '') 78 | { 79 | $basePath = App::getBasePath(); 80 | return $basePath . ltrim($file, '/'); 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /readr/views/index/index.phtml: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 9 |

10 | Readr 11 |

12 |

13 | 14 |

15 |
16 | 17 |
18 | 19 | 22 | 23 |
24 | 25 | 31 | 32 | 35 | 36 | 42 | 43 |
44 | 45 |
46 | 47 |
48 | 49 |
50 | 51 | 64 | 65 | 74 | 75 |
76 | 77 |
78 | 79 |
80 | 81 | 82 | 83 | 84 | 85 | 111 | 112 | 128 | 129 | 143 | 144 | 183 | 184 | 206 | 207 | 218 | 219 | 244 | 245 | -------------------------------------------------------------------------------- /readr/views/layout.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <?= $this->title ?> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | content ?> 40 | 41 | 42 | -------------------------------------------------------------------------------- /readr/views/login/index.phtml: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |

6 | Readr 7 |

8 | 9 |
10 | 11 |
12 | 13 | 43 | 44 |
45 | 46 |
-------------------------------------------------------------------------------- /readr/views/settings/index.phtml: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 | 8 | 9 |

10 | Readr 11 |

12 |
13 | 14 | username): ?> 15 | 18 | 19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |

Authentication

27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 | Leave blank to disable authentication 35 |
36 |
37 | 38 |
39 | 40 |
41 | 42 |
43 |
44 | 45 |
46 | 47 |
48 | 49 |
50 |
51 | 52 | messenger->hasMessages('auth-error')): ?> 53 |
54 |
55 | ', $this->messenger->getMessages('auth-error')) ?> 56 |
57 |
58 | 59 | 60 |
61 |
62 |
63 | 64 |
65 |
66 | 67 |
68 | 69 |
70 | 71 |
72 | 73 |

Import/Export OPML File

74 | 75 |
76 | 77 |
78 | 81 |
82 | 83 |
84 | 85 |
86 | 87 |
88 |
89 | 90 | messenger->hasMessages('import-error')): ?> 91 |
92 |
93 | ', $this->messenger->getMessages('import-error')) ?> 94 |
95 |
96 | 97 | 98 |
99 |
100 | 101 |
102 |
103 | 104 |
105 | 106 |
107 | 108 |
109 | 110 |

Misc

111 | 112 |
113 | 114 |
115 |
116 | 120 | 121 | Check this if read/favorite status are not properly saved 122 | 123 |
124 |
125 | 126 |
127 |
128 | 132 | 133 | If empty, entries are never deleted. 134 | 135 |
136 |
137 | 138 |
139 |
140 | 141 |
142 |
143 | 144 |
145 | 146 |
147 | 148 |
149 | 150 |

Version

151 | 152 |
    153 |
  • 154 | Current version: 155 | release ?> 156 | (version ?>) 157 |
  • 158 |
159 | 160 |
161 | 162 |
163 | 164 |
165 | 166 | -------------------------------------------------------------------------------- /vendor/.htaccess: -------------------------------------------------------------------------------- 1 | Deny from all -------------------------------------------------------------------------------- /vendor/SplClassLoader.php: -------------------------------------------------------------------------------- 1 | register(); 13 | * 14 | * @author Jonathan H. Wage 15 | * @author Roman S. Borschel 16 | * @author Matthew Weier O'Phinney 17 | * @author Kris Wallsmith 18 | * @author Fabien Potencier 19 | */ 20 | class SplClassLoader 21 | { 22 | private $_fileExtension = '.php'; 23 | private $_namespace; 24 | private $_includePath; 25 | private $_namespaceSeparator = '\\'; 26 | 27 | /** 28 | * Creates a new SplClassLoader that loads classes of the 29 | * specified namespace. 30 | * 31 | * @param string $ns The namespace to use. 32 | */ 33 | public function __construct($ns = null, $includePath = null) 34 | { 35 | $this->_namespace = $ns; 36 | $this->_includePath = $includePath; 37 | } 38 | 39 | /** 40 | * Sets the namespace separator used by classes in the namespace of this class loader. 41 | * 42 | * @param string $sep The separator to use. 43 | */ 44 | public function setNamespaceSeparator($sep) 45 | { 46 | $this->_namespaceSeparator = $sep; 47 | } 48 | 49 | /** 50 | * Gets the namespace seperator used by classes in the namespace of this class loader. 51 | * 52 | * @return void 53 | */ 54 | public function getNamespaceSeparator() 55 | { 56 | return $this->_namespaceSeparator; 57 | } 58 | 59 | /** 60 | * Sets the base include path for all class files in the namespace of this class loader. 61 | * 62 | * @param string $includePath 63 | */ 64 | public function setIncludePath($includePath) 65 | { 66 | $this->_includePath = $includePath; 67 | } 68 | 69 | /** 70 | * Gets the base include path for all class files in the namespace of this class loader. 71 | * 72 | * @return string $includePath 73 | */ 74 | public function getIncludePath() 75 | { 76 | return $this->_includePath; 77 | } 78 | 79 | /** 80 | * Sets the file extension of class files in the namespace of this class loader. 81 | * 82 | * @param string $fileExtension 83 | */ 84 | public function setFileExtension($fileExtension) 85 | { 86 | $this->_fileExtension = $fileExtension; 87 | } 88 | 89 | /** 90 | * Gets the file extension of class files in the namespace of this class loader. 91 | * 92 | * @return string $fileExtension 93 | */ 94 | public function getFileExtension() 95 | { 96 | return $this->_fileExtension; 97 | } 98 | 99 | /** 100 | * Installs this class loader on the SPL autoload stack. 101 | */ 102 | public function register() 103 | { 104 | spl_autoload_register(array($this, 'loadClass')); 105 | } 106 | 107 | /** 108 | * Uninstalls this class loader from the SPL autoloader stack. 109 | */ 110 | public function unregister() 111 | { 112 | spl_autoload_unregister(array($this, 'loadClass')); 113 | } 114 | 115 | /** 116 | * Loads the given class or interface. 117 | * 118 | * @param string $className The name of the class to load. 119 | * @return void 120 | */ 121 | public function loadClass($className) 122 | { 123 | if (null === $this->_namespace || $this->_namespace.$this->_namespaceSeparator === substr($className, 0, strlen($this->_namespace.$this->_namespaceSeparator))) { 124 | $fileName = ''; 125 | $namespace = ''; 126 | if (false !== ($lastNsPos = strripos($className, $this->_namespaceSeparator))) { 127 | $namespace = substr($className, 0, $lastNsPos); 128 | $className = substr($className, $lastNsPos + 1); 129 | $fileName = str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; 130 | } 131 | $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . $this->_fileExtension; 132 | 133 | require ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') . $fileName; 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /vendor/password_compat/password.php: -------------------------------------------------------------------------------- 1 | 6 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 7 | * @copyright 2012 The Authors 8 | */ 9 | 10 | if (!defined('PASSWORD_DEFAULT')) { 11 | 12 | define('PASSWORD_BCRYPT', 1); 13 | define('PASSWORD_DEFAULT', PASSWORD_BCRYPT); 14 | 15 | /** 16 | * Hash the password using the specified algorithm 17 | * 18 | * @param string $password The password to hash 19 | * @param int $algo The algorithm to use (Defined by PASSWORD_* constants) 20 | * @param array $options The options for the algorithm to use 21 | * 22 | * @return string|false The hashed password, or false on error. 23 | */ 24 | function password_hash($password, $algo, array $options = array()) { 25 | if (!function_exists('crypt')) { 26 | trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING); 27 | return null; 28 | } 29 | if (!is_string($password)) { 30 | trigger_error("password_hash(): Password must be a string", E_USER_WARNING); 31 | return null; 32 | } 33 | if (!is_int($algo)) { 34 | trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING); 35 | return null; 36 | } 37 | switch ($algo) { 38 | case PASSWORD_BCRYPT: 39 | // Note that this is a C constant, but not exposed to PHP, so we don't define it here. 40 | $cost = 10; 41 | if (isset($options['cost'])) { 42 | $cost = $options['cost']; 43 | if ($cost < 4 || $cost > 31) { 44 | trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING); 45 | return null; 46 | } 47 | } 48 | // The length of salt to generate 49 | $raw_salt_len = 16; 50 | // The length required in the final serialization 51 | $required_salt_len = 22; 52 | $hash_format = sprintf("$2y$%02d$", $cost); 53 | break; 54 | default: 55 | trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING); 56 | return null; 57 | } 58 | if (isset($options['salt'])) { 59 | switch (gettype($options['salt'])) { 60 | case 'NULL': 61 | case 'boolean': 62 | case 'integer': 63 | case 'double': 64 | case 'string': 65 | $salt = (string) $options['salt']; 66 | break; 67 | case 'object': 68 | if (method_exists($options['salt'], '__tostring')) { 69 | $salt = (string) $options['salt']; 70 | break; 71 | } 72 | case 'array': 73 | case 'resource': 74 | default: 75 | trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING); 76 | return null; 77 | } 78 | if (strlen($salt) < $required_salt_len) { 79 | trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING); 80 | return null; 81 | } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) { 82 | $salt = str_replace('+', '.', base64_encode($salt)); 83 | } 84 | } else { 85 | $buffer = ''; 86 | $buffer_valid = false; 87 | if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) { 88 | $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM); 89 | if ($buffer) { 90 | $buffer_valid = true; 91 | } 92 | } 93 | if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) { 94 | $buffer = openssl_random_pseudo_bytes($raw_salt_len); 95 | if ($buffer) { 96 | $buffer_valid = true; 97 | } 98 | } 99 | if (!$buffer_valid && is_readable('/dev/urandom')) { 100 | $f = fopen('/dev/urandom', 'r'); 101 | $read = strlen($buffer); 102 | while ($read < $raw_salt_len) { 103 | $buffer .= fread($f, $raw_salt_len - $read); 104 | $read = strlen($buffer); 105 | } 106 | fclose($f); 107 | if ($read >= $raw_salt_len) { 108 | $buffer_valid = true; 109 | } 110 | } 111 | if (!$buffer_valid || strlen($buffer) < $raw_salt_len) { 112 | $bl = strlen($buffer); 113 | for ($i = 0; $i < $raw_salt_len; $i++) { 114 | if ($i < $bl) { 115 | $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255)); 116 | } else { 117 | $buffer .= chr(mt_rand(0, 255)); 118 | } 119 | } 120 | } 121 | $salt = str_replace('+', '.', base64_encode($buffer)); 122 | } 123 | $salt = substr($salt, 0, $required_salt_len); 124 | 125 | $hash = $hash_format . $salt; 126 | 127 | $ret = crypt($password, $hash); 128 | 129 | if (!is_string($ret) || strlen($ret) <= 13) { 130 | return false; 131 | } 132 | 133 | return $ret; 134 | } 135 | 136 | /** 137 | * Get information about the password hash. Returns an array of the information 138 | * that was used to generate the password hash. 139 | * 140 | * array( 141 | * 'algo' => 1, 142 | * 'algoName' => 'bcrypt', 143 | * 'options' => array( 144 | * 'cost' => 10, 145 | * ), 146 | * ) 147 | * 148 | * @param string $hash The password hash to extract info from 149 | * 150 | * @return array The array of information about the hash. 151 | */ 152 | function password_get_info($hash) { 153 | $return = array( 154 | 'algo' => 0, 155 | 'algoName' => 'unknown', 156 | 'options' => array(), 157 | ); 158 | if (substr($hash, 0, 4) == '$2y$' && strlen($hash) == 60) { 159 | $return['algo'] = PASSWORD_BCRYPT; 160 | $return['algoName'] = 'bcrypt'; 161 | list($cost) = sscanf($hash, "$2y$%d$"); 162 | $return['options']['cost'] = $cost; 163 | } 164 | return $return; 165 | } 166 | 167 | /** 168 | * Determine if the password hash needs to be rehashed according to the options provided 169 | * 170 | * If the answer is true, after validating the password using password_verify, rehash it. 171 | * 172 | * @param string $hash The hash to test 173 | * @param int $algo The algorithm used for new password hashes 174 | * @param array $options The options array passed to password_hash 175 | * 176 | * @return boolean True if the password needs to be rehashed. 177 | */ 178 | function password_needs_rehash($hash, $algo, array $options = array()) { 179 | $info = password_get_info($hash); 180 | if ($info['algo'] != $algo) { 181 | return true; 182 | } 183 | switch ($algo) { 184 | case PASSWORD_BCRYPT: 185 | $cost = isset($options['cost']) ? $options['cost'] : 10; 186 | if ($cost != $info['options']['cost']) { 187 | return true; 188 | } 189 | break; 190 | } 191 | return false; 192 | } 193 | 194 | /** 195 | * Verify a password against a hash using a timing attack resistant approach 196 | * 197 | * @param string $password The password to verify 198 | * @param string $hash The hash to verify against 199 | * 200 | * @return boolean If the password matches the hash 201 | */ 202 | function password_verify($password, $hash) { 203 | if (!function_exists('crypt')) { 204 | trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING); 205 | return false; 206 | } 207 | $ret = crypt($password, $hash); 208 | if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) { 209 | return false; 210 | } 211 | 212 | $status = 0; 213 | for ($i = 0; $i < strlen($ret); $i++) { 214 | $status |= (ord($ret[$i]) ^ ord($hash[$i])); 215 | } 216 | 217 | return $status === 0; 218 | } 219 | } 220 | 221 | 222 | -------------------------------------------------------------------------------- /vendor/simplepie/autoloader.php: -------------------------------------------------------------------------------- 1 | path = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'; 68 | } 69 | 70 | /** 71 | * Autoloader 72 | * 73 | * @param string $class The name of the class to attempt to load. 74 | */ 75 | public function autoload($class) 76 | { 77 | // Only load the class if it starts with "SimplePie" 78 | if (strpos($class, 'SimplePie') !== 0) 79 | { 80 | return; 81 | } 82 | 83 | $filename = $this->path . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; 84 | include $filename; 85 | } 86 | } -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Author.php: -------------------------------------------------------------------------------- 1 | name = $name; 91 | $this->link = $link; 92 | $this->email = $email; 93 | } 94 | 95 | /** 96 | * String-ified version 97 | * 98 | * @return string 99 | */ 100 | public function __toString() 101 | { 102 | // There is no $this->data here 103 | return md5(serialize($this)); 104 | } 105 | 106 | /** 107 | * Author's name 108 | * 109 | * @return string|null 110 | */ 111 | public function get_name() 112 | { 113 | if ($this->name !== null) 114 | { 115 | return $this->name; 116 | } 117 | else 118 | { 119 | return null; 120 | } 121 | } 122 | 123 | /** 124 | * Author's link 125 | * 126 | * @return string|null 127 | */ 128 | public function get_link() 129 | { 130 | if ($this->link !== null) 131 | { 132 | return $this->link; 133 | } 134 | else 135 | { 136 | return null; 137 | } 138 | } 139 | 140 | /** 141 | * Author's email address 142 | * 143 | * @return string|null 144 | */ 145 | public function get_email() 146 | { 147 | if ($this->email !== null) 148 | { 149 | return $this->email; 150 | } 151 | else 152 | { 153 | return null; 154 | } 155 | } 156 | } 157 | 158 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Cache.php: -------------------------------------------------------------------------------- 1 | 'SimplePie_Cache_MySQL', 66 | 'memcache' => 'SimplePie_Cache_Memcache', 67 | ); 68 | 69 | /** 70 | * Don't call the constructor. Please. 71 | */ 72 | private function __construct() { } 73 | 74 | /** 75 | * Create a new SimplePie_Cache object 76 | * 77 | * @param string $location URL location (scheme is used to determine handler) 78 | * @param string $filename Unique identifier for cache object 79 | * @param string $extension 'spi' or 'spc' 80 | * @return SimplePie_Cache_Base Type of object depends on scheme of `$location` 81 | */ 82 | public static function get_handler($location, $filename, $extension) 83 | { 84 | $type = explode(':', $location, 2); 85 | $type = $type[0]; 86 | if (!empty(self::$handlers[$type])) 87 | { 88 | $class = self::$handlers[$type]; 89 | return new $class($location, $filename, $extension); 90 | } 91 | 92 | return new SimplePie_Cache_File($location, $filename, $extension); 93 | } 94 | 95 | /** 96 | * Create a new SimplePie_Cache object 97 | * 98 | * @deprecated Use {@see get_handler} instead 99 | */ 100 | public function create($location, $filename, $extension) 101 | { 102 | trigger_error('Cache::create() has been replaced with Cache::get_handler(). Switch to the registry system to use this.', E_USER_DEPRECATED); 103 | return self::get_handler($location, $filename, $extension); 104 | } 105 | 106 | /** 107 | * Register a handler 108 | * 109 | * @param string $type DSN type to register for 110 | * @param string $class Name of handler class. Must implement SimplePie_Cache_Base 111 | */ 112 | public static function register($type, $class) 113 | { 114 | self::$handlers[$type] = $class; 115 | } 116 | 117 | /** 118 | * Parse a URL into an array 119 | * 120 | * @param string $url 121 | * @return array 122 | */ 123 | public static function parse_URL($url) 124 | { 125 | $params = parse_url($url); 126 | $params['extras'] = array(); 127 | if (isset($params['query'])) 128 | { 129 | parse_str($params['query'], $params['extras']); 130 | } 131 | return $params; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Cache/Base.php: -------------------------------------------------------------------------------- 1 | get_items(); 64 | $items_by_id = array(); 65 | 66 | if (!empty($items)) 67 | { 68 | foreach ($items as $item) 69 | { 70 | $items_by_id[$item->get_id()] = $item; 71 | } 72 | 73 | if (count($items_by_id) !== count($items)) 74 | { 75 | $items_by_id = array(); 76 | foreach ($items as $item) 77 | { 78 | $items_by_id[$item->get_id(true)] = $item; 79 | } 80 | } 81 | 82 | if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) 83 | { 84 | $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; 85 | } 86 | elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) 87 | { 88 | $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; 89 | } 90 | elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) 91 | { 92 | $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; 93 | } 94 | elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0])) 95 | { 96 | $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]; 97 | } 98 | else 99 | { 100 | $channel = null; 101 | } 102 | 103 | if ($channel !== null) 104 | { 105 | if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'])) 106 | { 107 | unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']); 108 | } 109 | if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry'])) 110 | { 111 | unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']); 112 | } 113 | if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])) 114 | { 115 | unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']); 116 | } 117 | if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])) 118 | { 119 | unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']); 120 | } 121 | if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item'])) 122 | { 123 | unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']); 124 | } 125 | } 126 | if (isset($data->data['items'])) 127 | { 128 | unset($data->data['items']); 129 | } 130 | if (isset($data->data['ordered_items'])) 131 | { 132 | unset($data->data['ordered_items']); 133 | } 134 | } 135 | return array(serialize($data->data), $items_by_id); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Cache/File.php: -------------------------------------------------------------------------------- 1 | location = $location; 92 | $this->filename = $name; 93 | $this->extension = $type; 94 | $this->name = "$this->location/$this->filename.$this->extension"; 95 | } 96 | 97 | /** 98 | * Save data to the cache 99 | * 100 | * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property 101 | * @return bool Successfulness 102 | */ 103 | public function save($data) 104 | { 105 | if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location)) 106 | { 107 | if ($data instanceof SimplePie) 108 | { 109 | $data = $data->data; 110 | } 111 | 112 | $data = serialize($data); 113 | return (bool) file_put_contents($this->name, $data); 114 | } 115 | return false; 116 | } 117 | 118 | /** 119 | * Retrieve the data saved to the cache 120 | * 121 | * @return array Data for SimplePie::$data 122 | */ 123 | public function load() 124 | { 125 | if (file_exists($this->name) && is_readable($this->name)) 126 | { 127 | return unserialize(file_get_contents($this->name)); 128 | } 129 | return false; 130 | } 131 | 132 | /** 133 | * Retrieve the last modified time for the cache 134 | * 135 | * @return int Timestamp 136 | */ 137 | public function mtime() 138 | { 139 | if (file_exists($this->name)) 140 | { 141 | return filemtime($this->name); 142 | } 143 | return false; 144 | } 145 | 146 | /** 147 | * Set the last modified time to the current time 148 | * 149 | * @return bool Success status 150 | */ 151 | public function touch() 152 | { 153 | if (file_exists($this->name)) 154 | { 155 | return touch($this->name); 156 | } 157 | return false; 158 | } 159 | 160 | /** 161 | * Remove the cache 162 | * 163 | * @return bool Success status 164 | */ 165 | public function unlink() 166 | { 167 | if (file_exists($this->name)) 168 | { 169 | return unlink($this->name); 170 | } 171 | return false; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Cache/Memcache.php: -------------------------------------------------------------------------------- 1 | options = array( 91 | 'host' => '127.0.0.1', 92 | 'port' => 11211, 93 | 'extras' => array( 94 | 'timeout' => 3600, // one hour 95 | 'prefix' => 'simplepie_', 96 | ), 97 | ); 98 | $parsed = SimplePie_Cache::parse_URL($location); 99 | $this->options['host'] = empty($parsed['host']) ? $this->options['host'] : $parsed['host']; 100 | $this->options['port'] = empty($parsed['port']) ? $this->options['port'] : $parsed['port']; 101 | $this->options['extras'] = array_merge($this->options['extras'], $parsed['extras']); 102 | $this->name = $this->options['extras']['prefix'] . md5("$name:$type"); 103 | 104 | $this->cache = new Memcache(); 105 | $this->cache->addServer($this->options['host'], (int) $this->options['port']); 106 | } 107 | 108 | /** 109 | * Save data to the cache 110 | * 111 | * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property 112 | * @return bool Successfulness 113 | */ 114 | public function save($data) 115 | { 116 | if ($data instanceof SimplePie) 117 | { 118 | $data = $data->data; 119 | } 120 | return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']); 121 | } 122 | 123 | /** 124 | * Retrieve the data saved to the cache 125 | * 126 | * @return array Data for SimplePie::$data 127 | */ 128 | public function load() 129 | { 130 | $data = $this->cache->get($this->name); 131 | 132 | if ($data !== false) 133 | { 134 | return unserialize($data); 135 | } 136 | return false; 137 | } 138 | 139 | /** 140 | * Retrieve the last modified time for the cache 141 | * 142 | * @return int Timestamp 143 | */ 144 | public function mtime() 145 | { 146 | $data = $this->cache->get($this->name); 147 | 148 | if ($data !== false) 149 | { 150 | // essentially ignore the mtime because Memcache expires on it's own 151 | return time(); 152 | } 153 | 154 | return false; 155 | } 156 | 157 | /** 158 | * Set the last modified time to the current time 159 | * 160 | * @return bool Success status 161 | */ 162 | public function touch() 163 | { 164 | $data = $this->cache->get($this->name); 165 | 166 | if ($data !== false) 167 | { 168 | return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->duration); 169 | } 170 | 171 | return false; 172 | } 173 | 174 | /** 175 | * Remove the cache 176 | * 177 | * @return bool Success status 178 | */ 179 | public function unlink() 180 | { 181 | return $this->cache->delete($this->name, 0); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Caption.php: -------------------------------------------------------------------------------- 1 | ` captions as defined in Media RSS. 48 | * 49 | * Used by {@see SimplePie_Enclosure::get_caption()} and {@see SimplePie_Enclosure::get_captions()} 50 | * 51 | * This class can be overloaded with {@see SimplePie::set_caption_class()} 52 | * 53 | * @package SimplePie 54 | * @subpackage API 55 | */ 56 | class SimplePie_Caption 57 | { 58 | /** 59 | * Content type 60 | * 61 | * @var string 62 | * @see get_type() 63 | */ 64 | var $type; 65 | 66 | /** 67 | * Language 68 | * 69 | * @var string 70 | * @see get_language() 71 | */ 72 | var $lang; 73 | 74 | /** 75 | * Start time 76 | * 77 | * @var string 78 | * @see get_starttime() 79 | */ 80 | var $startTime; 81 | 82 | /** 83 | * End time 84 | * 85 | * @var string 86 | * @see get_endtime() 87 | */ 88 | var $endTime; 89 | 90 | /** 91 | * Caption text 92 | * 93 | * @var string 94 | * @see get_text() 95 | */ 96 | var $text; 97 | 98 | /** 99 | * Constructor, used to input the data 100 | * 101 | * For documentation on all the parameters, see the corresponding 102 | * properties and their accessors 103 | */ 104 | public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null) 105 | { 106 | $this->type = $type; 107 | $this->lang = $lang; 108 | $this->startTime = $startTime; 109 | $this->endTime = $endTime; 110 | $this->text = $text; 111 | } 112 | 113 | /** 114 | * String-ified version 115 | * 116 | * @return string 117 | */ 118 | public function __toString() 119 | { 120 | // There is no $this->data here 121 | return md5(serialize($this)); 122 | } 123 | 124 | /** 125 | * Get the end time 126 | * 127 | * @return string|null Time in the format 'hh:mm:ss.SSS' 128 | */ 129 | public function get_endtime() 130 | { 131 | if ($this->endTime !== null) 132 | { 133 | return $this->endTime; 134 | } 135 | else 136 | { 137 | return null; 138 | } 139 | } 140 | 141 | /** 142 | * Get the language 143 | * 144 | * @link http://tools.ietf.org/html/rfc3066 145 | * @return string|null Language code as per RFC 3066 146 | */ 147 | public function get_language() 148 | { 149 | if ($this->lang !== null) 150 | { 151 | return $this->lang; 152 | } 153 | else 154 | { 155 | return null; 156 | } 157 | } 158 | 159 | /** 160 | * Get the start time 161 | * 162 | * @return string|null Time in the format 'hh:mm:ss.SSS' 163 | */ 164 | public function get_starttime() 165 | { 166 | if ($this->startTime !== null) 167 | { 168 | return $this->startTime; 169 | } 170 | else 171 | { 172 | return null; 173 | } 174 | } 175 | 176 | /** 177 | * Get the text of the caption 178 | * 179 | * @return string|null 180 | */ 181 | public function get_text() 182 | { 183 | if ($this->text !== null) 184 | { 185 | return $this->text; 186 | } 187 | else 188 | { 189 | return null; 190 | } 191 | } 192 | 193 | /** 194 | * Get the content type (not MIME type) 195 | * 196 | * @return string|null Either 'text' or 'html' 197 | */ 198 | public function get_type() 199 | { 200 | if ($this->type !== null) 201 | { 202 | return $this->type; 203 | } 204 | else 205 | { 206 | return null; 207 | } 208 | } 209 | } 210 | 211 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Category.php: -------------------------------------------------------------------------------- 1 | term = $term; 91 | $this->scheme = $scheme; 92 | $this->label = $label; 93 | } 94 | 95 | /** 96 | * String-ified version 97 | * 98 | * @return string 99 | */ 100 | public function __toString() 101 | { 102 | // There is no $this->data here 103 | return md5(serialize($this)); 104 | } 105 | 106 | /** 107 | * Get the category identifier 108 | * 109 | * @return string|null 110 | */ 111 | public function get_term() 112 | { 113 | if ($this->term !== null) 114 | { 115 | return $this->term; 116 | } 117 | else 118 | { 119 | return null; 120 | } 121 | } 122 | 123 | /** 124 | * Get the categorization scheme identifier 125 | * 126 | * @return string|null 127 | */ 128 | public function get_scheme() 129 | { 130 | if ($this->scheme !== null) 131 | { 132 | return $this->scheme; 133 | } 134 | else 135 | { 136 | return null; 137 | } 138 | } 139 | 140 | /** 141 | * Get the human readable label 142 | * 143 | * @return string|null 144 | */ 145 | public function get_label() 146 | { 147 | if ($this->label !== null) 148 | { 149 | return $this->label; 150 | } 151 | else 152 | { 153 | return $this->get_term(); 154 | } 155 | } 156 | } 157 | 158 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Content/Type/Sniffer.php: -------------------------------------------------------------------------------- 1 | file = $file; 77 | } 78 | 79 | /** 80 | * Get the Content-Type of the specified file 81 | * 82 | * @return string Actual Content-Type 83 | */ 84 | public function get_type() 85 | { 86 | if (isset($this->file->headers['content-type'])) 87 | { 88 | if (!isset($this->file->headers['content-encoding']) 89 | && ($this->file->headers['content-type'] === 'text/plain' 90 | || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1' 91 | || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1' 92 | || $this->file->headers['content-type'] === 'text/plain; charset=UTF-8')) 93 | { 94 | return $this->text_or_binary(); 95 | } 96 | 97 | if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) 98 | { 99 | $official = substr($this->file->headers['content-type'], 0, $pos); 100 | } 101 | else 102 | { 103 | $official = $this->file->headers['content-type']; 104 | } 105 | $official = trim(strtolower($official)); 106 | 107 | if ($official === 'unknown/unknown' 108 | || $official === 'application/unknown') 109 | { 110 | return $this->unknown(); 111 | } 112 | elseif (substr($official, -4) === '+xml' 113 | || $official === 'text/xml' 114 | || $official === 'application/xml') 115 | { 116 | return $official; 117 | } 118 | elseif (substr($official, 0, 6) === 'image/') 119 | { 120 | if ($return = $this->image()) 121 | { 122 | return $return; 123 | } 124 | else 125 | { 126 | return $official; 127 | } 128 | } 129 | elseif ($official === 'text/html') 130 | { 131 | return $this->feed_or_html(); 132 | } 133 | else 134 | { 135 | return $official; 136 | } 137 | } 138 | else 139 | { 140 | return $this->unknown(); 141 | } 142 | } 143 | 144 | /** 145 | * Sniff text or binary 146 | * 147 | * @return string Actual Content-Type 148 | */ 149 | public function text_or_binary() 150 | { 151 | if (substr($this->file->body, 0, 2) === "\xFE\xFF" 152 | || substr($this->file->body, 0, 2) === "\xFF\xFE" 153 | || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF" 154 | || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") 155 | { 156 | return 'text/plain'; 157 | } 158 | elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) 159 | { 160 | return 'application/octect-stream'; 161 | } 162 | else 163 | { 164 | return 'text/plain'; 165 | } 166 | } 167 | 168 | /** 169 | * Sniff unknown 170 | * 171 | * @return string Actual Content-Type 172 | */ 173 | public function unknown() 174 | { 175 | $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20"); 176 | if (strtolower(substr($this->file->body, $ws, 14)) === 'file->body, $ws, 5)) === 'file->body, $ws, 7)) === 'file->body, 0, 5) === '%PDF-') 183 | { 184 | return 'application/pdf'; 185 | } 186 | elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') 187 | { 188 | return 'application/postscript'; 189 | } 190 | elseif (substr($this->file->body, 0, 6) === 'GIF87a' 191 | || substr($this->file->body, 0, 6) === 'GIF89a') 192 | { 193 | return 'image/gif'; 194 | } 195 | elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") 196 | { 197 | return 'image/png'; 198 | } 199 | elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") 200 | { 201 | return 'image/jpeg'; 202 | } 203 | elseif (substr($this->file->body, 0, 2) === "\x42\x4D") 204 | { 205 | return 'image/bmp'; 206 | } 207 | elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") 208 | { 209 | return 'image/vnd.microsoft.icon'; 210 | } 211 | else 212 | { 213 | return $this->text_or_binary(); 214 | } 215 | } 216 | 217 | /** 218 | * Sniff images 219 | * 220 | * @return string Actual Content-Type 221 | */ 222 | public function image() 223 | { 224 | if (substr($this->file->body, 0, 6) === 'GIF87a' 225 | || substr($this->file->body, 0, 6) === 'GIF89a') 226 | { 227 | return 'image/gif'; 228 | } 229 | elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") 230 | { 231 | return 'image/png'; 232 | } 233 | elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") 234 | { 235 | return 'image/jpeg'; 236 | } 237 | elseif (substr($this->file->body, 0, 2) === "\x42\x4D") 238 | { 239 | return 'image/bmp'; 240 | } 241 | elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") 242 | { 243 | return 'image/vnd.microsoft.icon'; 244 | } 245 | else 246 | { 247 | return false; 248 | } 249 | } 250 | 251 | /** 252 | * Sniff HTML 253 | * 254 | * @return string Actual Content-Type 255 | */ 256 | public function feed_or_html() 257 | { 258 | $len = strlen($this->file->body); 259 | $pos = strspn($this->file->body, "\x09\x0A\x0D\x20"); 260 | 261 | while ($pos < $len) 262 | { 263 | switch ($this->file->body[$pos]) 264 | { 265 | case "\x09": 266 | case "\x0A": 267 | case "\x0D": 268 | case "\x20": 269 | $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos); 270 | continue 2; 271 | 272 | case '<': 273 | $pos++; 274 | break; 275 | 276 | default: 277 | return 'text/html'; 278 | } 279 | 280 | if (substr($this->file->body, $pos, 3) === '!--') 281 | { 282 | $pos += 3; 283 | if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) 284 | { 285 | $pos += 3; 286 | } 287 | else 288 | { 289 | return 'text/html'; 290 | } 291 | } 292 | elseif (substr($this->file->body, $pos, 1) === '!') 293 | { 294 | if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) 295 | { 296 | $pos++; 297 | } 298 | else 299 | { 300 | return 'text/html'; 301 | } 302 | } 303 | elseif (substr($this->file->body, $pos, 1) === '?') 304 | { 305 | if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) 306 | { 307 | $pos += 2; 308 | } 309 | else 310 | { 311 | return 'text/html'; 312 | } 313 | } 314 | elseif (substr($this->file->body, $pos, 3) === 'rss' 315 | || substr($this->file->body, $pos, 7) === 'rdf:RDF') 316 | { 317 | return 'application/rss+xml'; 318 | } 319 | elseif (substr($this->file->body, $pos, 4) === 'feed') 320 | { 321 | return 'application/atom+xml'; 322 | } 323 | else 324 | { 325 | return 'text/html'; 326 | } 327 | } 328 | 329 | return 'text/html'; 330 | } 331 | } 332 | 333 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Copyright.php: -------------------------------------------------------------------------------- 1 | ` copyright tags as defined in Media RSS 47 | * 48 | * Used by {@see SimplePie_Enclosure::get_copyright()} 49 | * 50 | * This class can be overloaded with {@see SimplePie::set_copyright_class()} 51 | * 52 | * @package SimplePie 53 | * @subpackage API 54 | */ 55 | class SimplePie_Copyright 56 | { 57 | /** 58 | * Copyright URL 59 | * 60 | * @var string 61 | * @see get_url() 62 | */ 63 | var $url; 64 | 65 | /** 66 | * Attribution 67 | * 68 | * @var string 69 | * @see get_attribution() 70 | */ 71 | var $label; 72 | 73 | /** 74 | * Constructor, used to input the data 75 | * 76 | * For documentation on all the parameters, see the corresponding 77 | * properties and their accessors 78 | */ 79 | public function __construct($url = null, $label = null) 80 | { 81 | $this->url = $url; 82 | $this->label = $label; 83 | } 84 | 85 | /** 86 | * String-ified version 87 | * 88 | * @return string 89 | */ 90 | public function __toString() 91 | { 92 | // There is no $this->data here 93 | return md5(serialize($this)); 94 | } 95 | 96 | /** 97 | * Get the copyright URL 98 | * 99 | * @return string|null URL to copyright information 100 | */ 101 | public function get_url() 102 | { 103 | if ($this->url !== null) 104 | { 105 | return $this->url; 106 | } 107 | else 108 | { 109 | return null; 110 | } 111 | } 112 | 113 | /** 114 | * Get the attribution text 115 | * 116 | * @return string|null 117 | */ 118 | public function get_attribution() 119 | { 120 | if ($this->label !== null) 121 | { 122 | return $this->label; 123 | } 124 | else 125 | { 126 | return null; 127 | } 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Core.php: -------------------------------------------------------------------------------- 1 | ` as defined in Media RSS 47 | * 48 | * Used by {@see SimplePie_Enclosure::get_credit()} and {@see SimplePie_Enclosure::get_credits()} 49 | * 50 | * This class can be overloaded with {@see SimplePie::set_credit_class()} 51 | * 52 | * @package SimplePie 53 | * @subpackage API 54 | */ 55 | class SimplePie_Credit 56 | { 57 | /** 58 | * Credited role 59 | * 60 | * @var string 61 | * @see get_role() 62 | */ 63 | var $role; 64 | 65 | /** 66 | * Organizational scheme 67 | * 68 | * @var string 69 | * @see get_scheme() 70 | */ 71 | var $scheme; 72 | 73 | /** 74 | * Credited name 75 | * 76 | * @var string 77 | * @see get_name() 78 | */ 79 | var $name; 80 | 81 | /** 82 | * Constructor, used to input the data 83 | * 84 | * For documentation on all the parameters, see the corresponding 85 | * properties and their accessors 86 | */ 87 | public function __construct($role = null, $scheme = null, $name = null) 88 | { 89 | $this->role = $role; 90 | $this->scheme = $scheme; 91 | $this->name = $name; 92 | } 93 | 94 | /** 95 | * String-ified version 96 | * 97 | * @return string 98 | */ 99 | public function __toString() 100 | { 101 | // There is no $this->data here 102 | return md5(serialize($this)); 103 | } 104 | 105 | /** 106 | * Get the role of the person receiving credit 107 | * 108 | * @return string|null 109 | */ 110 | public function get_role() 111 | { 112 | if ($this->role !== null) 113 | { 114 | return $this->role; 115 | } 116 | else 117 | { 118 | return null; 119 | } 120 | } 121 | 122 | /** 123 | * Get the organizational scheme 124 | * 125 | * @return string|null 126 | */ 127 | public function get_scheme() 128 | { 129 | if ($this->scheme !== null) 130 | { 131 | return $this->scheme; 132 | } 133 | else 134 | { 135 | return null; 136 | } 137 | } 138 | 139 | /** 140 | * Get the credited person/entity's name 141 | * 142 | * @return string|null 143 | */ 144 | public function get_name() 145 | { 146 | if ($this->name !== null) 147 | { 148 | return $this->name; 149 | } 150 | else 151 | { 152 | return null; 153 | } 154 | } 155 | } 156 | 157 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Exception.php: -------------------------------------------------------------------------------- 1 | encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); 75 | } 76 | $this->url = $url; 77 | $this->useragent = $useragent; 78 | if (preg_match('/^http(s)?:\/\//i', $url)) 79 | { 80 | if ($useragent === null) 81 | { 82 | $useragent = ini_get('user_agent'); 83 | $this->useragent = $useragent; 84 | } 85 | if (!is_array($headers)) 86 | { 87 | $headers = array(); 88 | } 89 | if (!$force_fsockopen && function_exists('curl_exec')) 90 | { 91 | $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL; 92 | $fp = curl_init(); 93 | $headers2 = array(); 94 | foreach ($headers as $key => $value) 95 | { 96 | $headers2[] = "$key: $value"; 97 | } 98 | if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>=')) 99 | { 100 | curl_setopt($fp, CURLOPT_ENCODING, ''); 101 | } 102 | curl_setopt($fp, CURLOPT_URL, $url); 103 | curl_setopt($fp, CURLOPT_HEADER, 1); 104 | curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1); 105 | curl_setopt($fp, CURLOPT_TIMEOUT, $timeout); 106 | curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout); 107 | curl_setopt($fp, CURLOPT_REFERER, $url); 108 | curl_setopt($fp, CURLOPT_USERAGENT, $useragent); 109 | curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2); 110 | if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>=')) 111 | { 112 | curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1); 113 | curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects); 114 | } 115 | 116 | $this->headers = curl_exec($fp); 117 | if (curl_errno($fp) === 23 || curl_errno($fp) === 61) 118 | { 119 | curl_setopt($fp, CURLOPT_ENCODING, 'none'); 120 | $this->headers = curl_exec($fp); 121 | } 122 | if (curl_errno($fp)) 123 | { 124 | $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp); 125 | $this->success = false; 126 | } 127 | else 128 | { 129 | $info = curl_getinfo($fp); 130 | curl_close($fp); 131 | $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1); 132 | $this->headers = array_pop($this->headers); 133 | $parser = new SimplePie_HTTP_Parser($this->headers); 134 | if ($parser->parse()) 135 | { 136 | $this->headers = $parser->headers; 137 | $this->body = $parser->body; 138 | $this->status_code = $parser->status_code; 139 | if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) 140 | { 141 | $this->redirects++; 142 | $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); 143 | return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); 144 | } 145 | } 146 | } 147 | } 148 | else 149 | { 150 | $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN; 151 | $url_parts = parse_url($url); 152 | $socket_host = $url_parts['host']; 153 | if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') 154 | { 155 | $socket_host = "ssl://$url_parts[host]"; 156 | $url_parts['port'] = 443; 157 | } 158 | if (!isset($url_parts['port'])) 159 | { 160 | $url_parts['port'] = 80; 161 | } 162 | $fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout); 163 | if (!$fp) 164 | { 165 | $this->error = 'fsockopen error: ' . $errstr; 166 | $this->success = false; 167 | } 168 | else 169 | { 170 | stream_set_timeout($fp, $timeout); 171 | if (isset($url_parts['path'])) 172 | { 173 | if (isset($url_parts['query'])) 174 | { 175 | $get = "$url_parts[path]?$url_parts[query]"; 176 | } 177 | else 178 | { 179 | $get = $url_parts['path']; 180 | } 181 | } 182 | else 183 | { 184 | $get = '/'; 185 | } 186 | $out = "GET $get HTTP/1.1\r\n"; 187 | $out .= "Host: $url_parts[host]\r\n"; 188 | $out .= "User-Agent: $useragent\r\n"; 189 | if (extension_loaded('zlib')) 190 | { 191 | $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n"; 192 | } 193 | 194 | if (isset($url_parts['user']) && isset($url_parts['pass'])) 195 | { 196 | $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n"; 197 | } 198 | foreach ($headers as $key => $value) 199 | { 200 | $out .= "$key: $value\r\n"; 201 | } 202 | $out .= "Connection: Close\r\n\r\n"; 203 | fwrite($fp, $out); 204 | 205 | $info = stream_get_meta_data($fp); 206 | 207 | $this->headers = ''; 208 | while (!$info['eof'] && !$info['timed_out']) 209 | { 210 | $this->headers .= fread($fp, 1160); 211 | $info = stream_get_meta_data($fp); 212 | } 213 | if (!$info['timed_out']) 214 | { 215 | $parser = new SimplePie_HTTP_Parser($this->headers); 216 | if ($parser->parse()) 217 | { 218 | $this->headers = $parser->headers; 219 | $this->body = $parser->body; 220 | $this->status_code = $parser->status_code; 221 | if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) 222 | { 223 | $this->redirects++; 224 | $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); 225 | return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); 226 | } 227 | if (isset($this->headers['content-encoding'])) 228 | { 229 | // Hey, we act dumb elsewhere, so let's do that here too 230 | switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20"))) 231 | { 232 | case 'gzip': 233 | case 'x-gzip': 234 | $decoder = new SimplePie_gzdecode($this->body); 235 | if (!$decoder->parse()) 236 | { 237 | $this->error = 'Unable to decode HTTP "gzip" stream'; 238 | $this->success = false; 239 | } 240 | else 241 | { 242 | $this->body = $decoder->data; 243 | } 244 | break; 245 | 246 | case 'deflate': 247 | if (($decompressed = gzinflate($this->body)) !== false) 248 | { 249 | $this->body = $decompressed; 250 | } 251 | else if (($decompressed = gzuncompress($this->body)) !== false) 252 | { 253 | $this->body = $decompressed; 254 | } 255 | else if (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false) 256 | { 257 | $this->body = $decompressed; 258 | } 259 | else 260 | { 261 | $this->error = 'Unable to decode HTTP "deflate" stream'; 262 | $this->success = false; 263 | } 264 | break; 265 | 266 | default: 267 | $this->error = 'Unknown content coding'; 268 | $this->success = false; 269 | } 270 | } 271 | } 272 | } 273 | else 274 | { 275 | $this->error = 'fsocket timed out'; 276 | $this->success = false; 277 | } 278 | fclose($fp); 279 | } 280 | } 281 | } 282 | else 283 | { 284 | $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS; 285 | if (!$this->body = file_get_contents($url)) 286 | { 287 | $this->error = 'file_get_contents could not read the file'; 288 | $this->success = false; 289 | } 290 | } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Net/IPv6.php: -------------------------------------------------------------------------------- 1 | 55 | * @author elfrink at introweb dot nl 56 | * @author Josh Peck 57 | * @author Geoffrey Sneddon 58 | */ 59 | class SimplePie_Net_IPv6 60 | { 61 | /** 62 | * Uncompresses an IPv6 address 63 | * 64 | * RFC 4291 allows you to compress concecutive zero pieces in an address to 65 | * '::'. This method expects a valid IPv6 address and expands the '::' to 66 | * the required number of zero pieces. 67 | * 68 | * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 69 | * ::1 -> 0:0:0:0:0:0:0:1 70 | * 71 | * @author Alexander Merz 72 | * @author elfrink at introweb dot nl 73 | * @author Josh Peck 74 | * @copyright 2003-2005 The PHP Group 75 | * @license http://www.opensource.org/licenses/bsd-license.php 76 | * @param string $ip An IPv6 address 77 | * @return string The uncompressed IPv6 address 78 | */ 79 | public static function uncompress($ip) 80 | { 81 | $c1 = -1; 82 | $c2 = -1; 83 | if (substr_count($ip, '::') === 1) 84 | { 85 | list($ip1, $ip2) = explode('::', $ip); 86 | if ($ip1 === '') 87 | { 88 | $c1 = -1; 89 | } 90 | else 91 | { 92 | $c1 = substr_count($ip1, ':'); 93 | } 94 | if ($ip2 === '') 95 | { 96 | $c2 = -1; 97 | } 98 | else 99 | { 100 | $c2 = substr_count($ip2, ':'); 101 | } 102 | if (strpos($ip2, '.') !== false) 103 | { 104 | $c2++; 105 | } 106 | // :: 107 | if ($c1 === -1 && $c2 === -1) 108 | { 109 | $ip = '0:0:0:0:0:0:0:0'; 110 | } 111 | // ::xxx 112 | else if ($c1 === -1) 113 | { 114 | $fill = str_repeat('0:', 7 - $c2); 115 | $ip = str_replace('::', $fill, $ip); 116 | } 117 | // xxx:: 118 | else if ($c2 === -1) 119 | { 120 | $fill = str_repeat(':0', 7 - $c1); 121 | $ip = str_replace('::', $fill, $ip); 122 | } 123 | // xxx::xxx 124 | else 125 | { 126 | $fill = ':' . str_repeat('0:', 6 - $c2 - $c1); 127 | $ip = str_replace('::', $fill, $ip); 128 | } 129 | } 130 | return $ip; 131 | } 132 | 133 | /** 134 | * Compresses an IPv6 address 135 | * 136 | * RFC 4291 allows you to compress concecutive zero pieces in an address to 137 | * '::'. This method expects a valid IPv6 address and compresses consecutive 138 | * zero pieces to '::'. 139 | * 140 | * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 141 | * 0:0:0:0:0:0:0:1 -> ::1 142 | * 143 | * @see uncompress() 144 | * @param string $ip An IPv6 address 145 | * @return string The compressed IPv6 address 146 | */ 147 | public static function compress($ip) 148 | { 149 | // Prepare the IP to be compressed 150 | $ip = self::uncompress($ip); 151 | $ip_parts = self::split_v6_v4($ip); 152 | 153 | // Replace all leading zeros 154 | $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]); 155 | 156 | // Find bunches of zeros 157 | if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) 158 | { 159 | $max = 0; 160 | $pos = null; 161 | foreach ($matches[0] as $match) 162 | { 163 | if (strlen($match[0]) > $max) 164 | { 165 | $max = strlen($match[0]); 166 | $pos = $match[1]; 167 | } 168 | } 169 | 170 | $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max); 171 | } 172 | 173 | if ($ip_parts[1] !== '') 174 | { 175 | return implode(':', $ip_parts); 176 | } 177 | else 178 | { 179 | return $ip_parts[0]; 180 | } 181 | } 182 | 183 | /** 184 | * Splits an IPv6 address into the IPv6 and IPv4 representation parts 185 | * 186 | * RFC 4291 allows you to represent the last two parts of an IPv6 address 187 | * using the standard IPv4 representation 188 | * 189 | * Example: 0:0:0:0:0:0:13.1.68.3 190 | * 0:0:0:0:0:FFFF:129.144.52.38 191 | * 192 | * @param string $ip An IPv6 address 193 | * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part 194 | */ 195 | private static function split_v6_v4($ip) 196 | { 197 | if (strpos($ip, '.') !== false) 198 | { 199 | $pos = strrpos($ip, ':'); 200 | $ipv6_part = substr($ip, 0, $pos); 201 | $ipv4_part = substr($ip, $pos + 1); 202 | return array($ipv6_part, $ipv4_part); 203 | } 204 | else 205 | { 206 | return array($ip, ''); 207 | } 208 | } 209 | 210 | /** 211 | * Checks an IPv6 address 212 | * 213 | * Checks if the given IP is a valid IPv6 address 214 | * 215 | * @param string $ip An IPv6 address 216 | * @return bool true if $ip is a valid IPv6 address 217 | */ 218 | public static function check_ipv6($ip) 219 | { 220 | $ip = self::uncompress($ip); 221 | list($ipv6, $ipv4) = self::split_v6_v4($ip); 222 | $ipv6 = explode(':', $ipv6); 223 | $ipv4 = explode('.', $ipv4); 224 | if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) 225 | { 226 | foreach ($ipv6 as $ipv6_part) 227 | { 228 | // The section can't be empty 229 | if ($ipv6_part === '') 230 | return false; 231 | 232 | // Nor can it be over four characters 233 | if (strlen($ipv6_part) > 4) 234 | return false; 235 | 236 | // Remove leading zeros (this is safe because of the above) 237 | $ipv6_part = ltrim($ipv6_part, '0'); 238 | if ($ipv6_part === '') 239 | $ipv6_part = '0'; 240 | 241 | // Check the value is valid 242 | $value = hexdec($ipv6_part); 243 | if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) 244 | return false; 245 | } 246 | if (count($ipv4) === 4) 247 | { 248 | foreach ($ipv4 as $ipv4_part) 249 | { 250 | $value = (int) $ipv4_part; 251 | if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) 252 | return false; 253 | } 254 | } 255 | return true; 256 | } 257 | else 258 | { 259 | return false; 260 | } 261 | } 262 | 263 | /** 264 | * Checks if the given IP is a valid IPv6 address 265 | * 266 | * @codeCoverageIgnore 267 | * @deprecated Use {@see SimplePie_Net_IPv6::check_ipv6()} instead 268 | * @see check_ipv6 269 | * @param string $ip An IPv6 address 270 | * @return bool true if $ip is a valid IPv6 address 271 | */ 272 | public static function checkIPv6($ip) 273 | { 274 | return self::check_ipv6($ip); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Rating.php: -------------------------------------------------------------------------------- 1 | ` or `` tags as defined in Media RSS and iTunes RSS respectively 47 | * 48 | * Used by {@see SimplePie_Enclosure::get_rating()} and {@see SimplePie_Enclosure::get_ratings()} 49 | * 50 | * This class can be overloaded with {@see SimplePie::set_rating_class()} 51 | * 52 | * @package SimplePie 53 | * @subpackage API 54 | */ 55 | class SimplePie_Rating 56 | { 57 | /** 58 | * Rating scheme 59 | * 60 | * @var string 61 | * @see get_scheme() 62 | */ 63 | var $scheme; 64 | 65 | /** 66 | * Rating value 67 | * 68 | * @var string 69 | * @see get_value() 70 | */ 71 | var $value; 72 | 73 | /** 74 | * Constructor, used to input the data 75 | * 76 | * For documentation on all the parameters, see the corresponding 77 | * properties and their accessors 78 | */ 79 | public function __construct($scheme = null, $value = null) 80 | { 81 | $this->scheme = $scheme; 82 | $this->value = $value; 83 | } 84 | 85 | /** 86 | * String-ified version 87 | * 88 | * @return string 89 | */ 90 | public function __toString() 91 | { 92 | // There is no $this->data here 93 | return md5(serialize($this)); 94 | } 95 | 96 | /** 97 | * Get the organizational scheme for the rating 98 | * 99 | * @return string|null 100 | */ 101 | public function get_scheme() 102 | { 103 | if ($this->scheme !== null) 104 | { 105 | return $this->scheme; 106 | } 107 | else 108 | { 109 | return null; 110 | } 111 | } 112 | 113 | /** 114 | * Get the value of the rating 115 | * 116 | * @return string|null 117 | */ 118 | public function get_value() 119 | { 120 | if ($this->value !== null) 121 | { 122 | return $this->value; 123 | } 124 | else 125 | { 126 | return null; 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Registry.php: -------------------------------------------------------------------------------- 1 | 'SimplePie_Cache', 63 | 'Locator' => 'SimplePie_Locator', 64 | 'Parser' => 'SimplePie_Parser', 65 | 'File' => 'SimplePie_File', 66 | 'Sanitize' => 'SimplePie_Sanitize', 67 | 'Item' => 'SimplePie_Item', 68 | 'Author' => 'SimplePie_Author', 69 | 'Category' => 'SimplePie_Category', 70 | 'Enclosure' => 'SimplePie_Enclosure', 71 | 'Caption' => 'SimplePie_Caption', 72 | 'Copyright' => 'SimplePie_Copyright', 73 | 'Credit' => 'SimplePie_Credit', 74 | 'Rating' => 'SimplePie_Rating', 75 | 'Restriction' => 'SimplePie_Restriction', 76 | 'Content_Type_Sniffer' => 'SimplePie_Content_Type_Sniffer', 77 | 'Source' => 'SimplePie_Source', 78 | 'Misc' => 'SimplePie_Misc', 79 | 'XML_Declaration_Parser' => 'SimplePie_XML_Declaration_Parser', 80 | 'Parse_Date' => 'SimplePie_Parse_Date', 81 | ); 82 | 83 | /** 84 | * Class mapping 85 | * 86 | * @see register() 87 | * @var array 88 | */ 89 | protected $classes = array(); 90 | 91 | /** 92 | * Legacy classes 93 | * 94 | * @see register() 95 | * @var array 96 | */ 97 | protected $legacy = array(); 98 | 99 | /** 100 | * Constructor 101 | * 102 | * No-op 103 | */ 104 | public function __construct() { } 105 | 106 | /** 107 | * Register a class 108 | * 109 | * @param string $type See {@see $default} for names 110 | * @param string $class Class name, must subclass the corresponding default 111 | * @param bool $legacy Whether to enable legacy support for this class 112 | * @return bool Successfulness 113 | */ 114 | public function register($type, $class, $legacy = false) 115 | { 116 | if (!is_subclass_of($class, $this->default[$type])) 117 | { 118 | return false; 119 | } 120 | 121 | $this->classes[$type] = $class; 122 | 123 | if ($legacy) 124 | { 125 | $this->legacy[] = $class; 126 | } 127 | 128 | return true; 129 | } 130 | 131 | /** 132 | * Get the class registered for a type 133 | * 134 | * Where possible, use {@see create()} or {@see call()} instead 135 | * 136 | * @param string $type 137 | * @return string|null 138 | */ 139 | public function get_class($type) 140 | { 141 | if (!empty($this->classes[$type])) 142 | { 143 | return $this->classes[$type]; 144 | } 145 | if (!empty($this->default[$type])) 146 | { 147 | return $this->default[$type]; 148 | } 149 | 150 | return null; 151 | } 152 | 153 | /** 154 | * Create a new instance of a given type 155 | * 156 | * @param string $type 157 | * @param array $parameters Parameters to pass to the constructor 158 | * @return object Instance of class 159 | */ 160 | public function &create($type, $parameters = array()) 161 | { 162 | $class = $this->get_class($type); 163 | 164 | if (in_array($class, $this->legacy)) 165 | { 166 | switch ($type) 167 | { 168 | case 'locator': 169 | // Legacy: file, timeout, useragent, file_class, max_checked_feeds, content_type_sniffer_class 170 | // Specified: file, timeout, useragent, max_checked_feeds 171 | $replacement = array($this->get_class('file'), $parameters[3], $this->get_class('content_type_sniffer')); 172 | array_splice($parameters, 3, 1, $replacement); 173 | break; 174 | } 175 | } 176 | 177 | if (!method_exists($class, '__construct')) 178 | { 179 | $instance = new $class; 180 | } 181 | else 182 | { 183 | $reflector = new ReflectionClass($class); 184 | $instance = $reflector->newInstanceArgs($parameters); 185 | } 186 | 187 | if (method_exists($instance, 'set_registry')) 188 | { 189 | $instance->set_registry($this); 190 | } 191 | return $instance; 192 | } 193 | 194 | /** 195 | * Call a static method for a type 196 | * 197 | * @param string $type 198 | * @param string $method 199 | * @param array $parameters 200 | * @return mixed 201 | */ 202 | public function &call($type, $method, $parameters = array()) 203 | { 204 | $class = $this->get_class($type); 205 | 206 | if (in_array($class, $this->legacy)) 207 | { 208 | switch ($type) 209 | { 210 | case 'Cache': 211 | // For backwards compatibility with old non-static 212 | // Cache::create() methods 213 | if ($method === 'get_handler') 214 | { 215 | $result = @call_user_func_array(array($class, 'create'), $parameters); 216 | return $result; 217 | } 218 | break; 219 | } 220 | } 221 | 222 | $result = call_user_func_array(array($class, $method), $parameters); 223 | return $result; 224 | } 225 | } -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/Restriction.php: -------------------------------------------------------------------------------- 1 | ` as defined in Media RSS 47 | * 48 | * Used by {@see SimplePie_Enclosure::get_restriction()} and {@see SimplePie_Enclosure::get_restrictions()} 49 | * 50 | * This class can be overloaded with {@see SimplePie::set_restriction_class()} 51 | * 52 | * @package SimplePie 53 | * @subpackage API 54 | */ 55 | class SimplePie_Restriction 56 | { 57 | /** 58 | * Relationship ('allow'/'deny') 59 | * 60 | * @var string 61 | * @see get_relationship() 62 | */ 63 | var $relationship; 64 | 65 | /** 66 | * Type of restriction 67 | * 68 | * @var string 69 | * @see get_type() 70 | */ 71 | var $type; 72 | 73 | /** 74 | * Restricted values 75 | * 76 | * @var string 77 | * @see get_value() 78 | */ 79 | var $value; 80 | 81 | /** 82 | * Constructor, used to input the data 83 | * 84 | * For documentation on all the parameters, see the corresponding 85 | * properties and their accessors 86 | */ 87 | public function __construct($relationship = null, $type = null, $value = null) 88 | { 89 | $this->relationship = $relationship; 90 | $this->type = $type; 91 | $this->value = $value; 92 | } 93 | 94 | /** 95 | * String-ified version 96 | * 97 | * @return string 98 | */ 99 | public function __toString() 100 | { 101 | // There is no $this->data here 102 | return md5(serialize($this)); 103 | } 104 | 105 | /** 106 | * Get the relationship 107 | * 108 | * @return string|null Either 'allow' or 'deny' 109 | */ 110 | public function get_relationship() 111 | { 112 | if ($this->relationship !== null) 113 | { 114 | return $this->relationship; 115 | } 116 | else 117 | { 118 | return null; 119 | } 120 | } 121 | 122 | /** 123 | * Get the type 124 | * 125 | * @return string|null 126 | */ 127 | public function get_type() 128 | { 129 | if ($this->type !== null) 130 | { 131 | return $this->type; 132 | } 133 | else 134 | { 135 | return null; 136 | } 137 | } 138 | 139 | /** 140 | * Get the list of restricted things 141 | * 142 | * @return string|null 143 | */ 144 | public function get_value() 145 | { 146 | if ($this->value !== null) 147 | { 148 | return $this->value; 149 | } 150 | else 151 | { 152 | return null; 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/XML/Declaration/Parser.php: -------------------------------------------------------------------------------- 1 | data = $data; 119 | $this->data_length = strlen($this->data); 120 | } 121 | 122 | /** 123 | * Parse the input data 124 | * 125 | * @access public 126 | * @return bool true on success, false on failure 127 | */ 128 | public function parse() 129 | { 130 | while ($this->state && $this->state !== 'emit' && $this->has_data()) 131 | { 132 | $state = $this->state; 133 | $this->$state(); 134 | } 135 | $this->data = ''; 136 | if ($this->state === 'emit') 137 | { 138 | return true; 139 | } 140 | else 141 | { 142 | $this->version = ''; 143 | $this->encoding = ''; 144 | $this->standalone = ''; 145 | return false; 146 | } 147 | } 148 | 149 | /** 150 | * Check whether there is data beyond the pointer 151 | * 152 | * @access private 153 | * @return bool true if there is further data, false if not 154 | */ 155 | public function has_data() 156 | { 157 | return (bool) ($this->position < $this->data_length); 158 | } 159 | 160 | /** 161 | * Advance past any whitespace 162 | * 163 | * @return int Number of whitespace characters passed 164 | */ 165 | public function skip_whitespace() 166 | { 167 | $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position); 168 | $this->position += $whitespace; 169 | return $whitespace; 170 | } 171 | 172 | /** 173 | * Read value 174 | */ 175 | public function get_value() 176 | { 177 | $quote = substr($this->data, $this->position, 1); 178 | if ($quote === '"' || $quote === "'") 179 | { 180 | $this->position++; 181 | $len = strcspn($this->data, $quote, $this->position); 182 | if ($this->has_data()) 183 | { 184 | $value = substr($this->data, $this->position, $len); 185 | $this->position += $len + 1; 186 | return $value; 187 | } 188 | } 189 | return false; 190 | } 191 | 192 | public function before_version_name() 193 | { 194 | if ($this->skip_whitespace()) 195 | { 196 | $this->state = 'version_name'; 197 | } 198 | else 199 | { 200 | $this->state = false; 201 | } 202 | } 203 | 204 | public function version_name() 205 | { 206 | if (substr($this->data, $this->position, 7) === 'version') 207 | { 208 | $this->position += 7; 209 | $this->skip_whitespace(); 210 | $this->state = 'version_equals'; 211 | } 212 | else 213 | { 214 | $this->state = false; 215 | } 216 | } 217 | 218 | public function version_equals() 219 | { 220 | if (substr($this->data, $this->position, 1) === '=') 221 | { 222 | $this->position++; 223 | $this->skip_whitespace(); 224 | $this->state = 'version_value'; 225 | } 226 | else 227 | { 228 | $this->state = false; 229 | } 230 | } 231 | 232 | public function version_value() 233 | { 234 | if ($this->version = $this->get_value()) 235 | { 236 | $this->skip_whitespace(); 237 | if ($this->has_data()) 238 | { 239 | $this->state = 'encoding_name'; 240 | } 241 | else 242 | { 243 | $this->state = 'emit'; 244 | } 245 | } 246 | else 247 | { 248 | $this->state = false; 249 | } 250 | } 251 | 252 | public function encoding_name() 253 | { 254 | if (substr($this->data, $this->position, 8) === 'encoding') 255 | { 256 | $this->position += 8; 257 | $this->skip_whitespace(); 258 | $this->state = 'encoding_equals'; 259 | } 260 | else 261 | { 262 | $this->state = 'standalone_name'; 263 | } 264 | } 265 | 266 | public function encoding_equals() 267 | { 268 | if (substr($this->data, $this->position, 1) === '=') 269 | { 270 | $this->position++; 271 | $this->skip_whitespace(); 272 | $this->state = 'encoding_value'; 273 | } 274 | else 275 | { 276 | $this->state = false; 277 | } 278 | } 279 | 280 | public function encoding_value() 281 | { 282 | if ($this->encoding = $this->get_value()) 283 | { 284 | $this->skip_whitespace(); 285 | if ($this->has_data()) 286 | { 287 | $this->state = 'standalone_name'; 288 | } 289 | else 290 | { 291 | $this->state = 'emit'; 292 | } 293 | } 294 | else 295 | { 296 | $this->state = false; 297 | } 298 | } 299 | 300 | public function standalone_name() 301 | { 302 | if (substr($this->data, $this->position, 10) === 'standalone') 303 | { 304 | $this->position += 10; 305 | $this->skip_whitespace(); 306 | $this->state = 'standalone_equals'; 307 | } 308 | else 309 | { 310 | $this->state = false; 311 | } 312 | } 313 | 314 | public function standalone_equals() 315 | { 316 | if (substr($this->data, $this->position, 1) === '=') 317 | { 318 | $this->position++; 319 | $this->skip_whitespace(); 320 | $this->state = 'standalone_value'; 321 | } 322 | else 323 | { 324 | $this->state = false; 325 | } 326 | } 327 | 328 | public function standalone_value() 329 | { 330 | if ($standalone = $this->get_value()) 331 | { 332 | switch ($standalone) 333 | { 334 | case 'yes': 335 | $this->standalone = true; 336 | break; 337 | 338 | case 'no': 339 | $this->standalone = false; 340 | break; 341 | 342 | default: 343 | $this->state = false; 344 | return; 345 | } 346 | 347 | $this->skip_whitespace(); 348 | if ($this->has_data()) 349 | { 350 | $this->state = false; 351 | } 352 | else 353 | { 354 | $this->state = 'emit'; 355 | } 356 | } 357 | else 358 | { 359 | $this->state = false; 360 | } 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /vendor/simplepie/library/SimplePie/gzdecode.php: -------------------------------------------------------------------------------- 1 | compressed_data = $data; 194 | $this->compressed_size = strlen($data); 195 | } 196 | 197 | /** 198 | * Decode the GZIP stream 199 | * 200 | * @return bool Successfulness 201 | */ 202 | public function parse() 203 | { 204 | if ($this->compressed_size >= $this->min_compressed_size) 205 | { 206 | // Check ID1, ID2, and CM 207 | if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08") 208 | { 209 | return false; 210 | } 211 | 212 | // Get the FLG (FLaGs) 213 | $this->flags = ord($this->compressed_data[3]); 214 | 215 | // FLG bits above (1 << 4) are reserved 216 | if ($this->flags > 0x1F) 217 | { 218 | return false; 219 | } 220 | 221 | // Advance the pointer after the above 222 | $this->position += 4; 223 | 224 | // MTIME 225 | $mtime = substr($this->compressed_data, $this->position, 4); 226 | // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness 227 | if (current(unpack('S', "\x00\x01")) === 1) 228 | { 229 | $mtime = strrev($mtime); 230 | } 231 | $this->MTIME = current(unpack('l', $mtime)); 232 | $this->position += 4; 233 | 234 | // Get the XFL (eXtra FLags) 235 | $this->XFL = ord($this->compressed_data[$this->position++]); 236 | 237 | // Get the OS (Operating System) 238 | $this->OS = ord($this->compressed_data[$this->position++]); 239 | 240 | // Parse the FEXTRA 241 | if ($this->flags & 4) 242 | { 243 | // Read subfield IDs 244 | $this->SI1 = $this->compressed_data[$this->position++]; 245 | $this->SI2 = $this->compressed_data[$this->position++]; 246 | 247 | // SI2 set to zero is reserved for future use 248 | if ($this->SI2 === "\x00") 249 | { 250 | return false; 251 | } 252 | 253 | // Get the length of the extra field 254 | $len = current(unpack('v', substr($this->compressed_data, $this->position, 2))); 255 | $this->position += 2; 256 | 257 | // Check the length of the string is still valid 258 | $this->min_compressed_size += $len + 4; 259 | if ($this->compressed_size >= $this->min_compressed_size) 260 | { 261 | // Set the extra field to the given data 262 | $this->extra_field = substr($this->compressed_data, $this->position, $len); 263 | $this->position += $len; 264 | } 265 | else 266 | { 267 | return false; 268 | } 269 | } 270 | 271 | // Parse the FNAME 272 | if ($this->flags & 8) 273 | { 274 | // Get the length of the filename 275 | $len = strcspn($this->compressed_data, "\x00", $this->position); 276 | 277 | // Check the length of the string is still valid 278 | $this->min_compressed_size += $len + 1; 279 | if ($this->compressed_size >= $this->min_compressed_size) 280 | { 281 | // Set the original filename to the given string 282 | $this->filename = substr($this->compressed_data, $this->position, $len); 283 | $this->position += $len + 1; 284 | } 285 | else 286 | { 287 | return false; 288 | } 289 | } 290 | 291 | // Parse the FCOMMENT 292 | if ($this->flags & 16) 293 | { 294 | // Get the length of the comment 295 | $len = strcspn($this->compressed_data, "\x00", $this->position); 296 | 297 | // Check the length of the string is still valid 298 | $this->min_compressed_size += $len + 1; 299 | if ($this->compressed_size >= $this->min_compressed_size) 300 | { 301 | // Set the original comment to the given string 302 | $this->comment = substr($this->compressed_data, $this->position, $len); 303 | $this->position += $len + 1; 304 | } 305 | else 306 | { 307 | return false; 308 | } 309 | } 310 | 311 | // Parse the FHCRC 312 | if ($this->flags & 2) 313 | { 314 | // Check the length of the string is still valid 315 | $this->min_compressed_size += $len + 2; 316 | if ($this->compressed_size >= $this->min_compressed_size) 317 | { 318 | // Read the CRC 319 | $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2))); 320 | 321 | // Check the CRC matches 322 | if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc) 323 | { 324 | $this->position += 2; 325 | } 326 | else 327 | { 328 | return false; 329 | } 330 | } 331 | else 332 | { 333 | return false; 334 | } 335 | } 336 | 337 | // Decompress the actual data 338 | if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false) 339 | { 340 | return false; 341 | } 342 | else 343 | { 344 | $this->position = $this->compressed_size - 8; 345 | } 346 | 347 | // Check CRC of data 348 | $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4))); 349 | $this->position += 4; 350 | /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc)) 351 | { 352 | return false; 353 | }*/ 354 | 355 | // Check ISIZE of data 356 | $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4))); 357 | $this->position += 4; 358 | if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize)) 359 | { 360 | return false; 361 | } 362 | 363 | // Wow, against all odds, we've actually got a valid gzip string 364 | return true; 365 | } 366 | else 367 | { 368 | return false; 369 | } 370 | } 371 | } 372 | --------------------------------------------------------------------------------