Orderassets/css/jquery-clockpicker.min.css000064400000014173147577721060013766 0ustar00/*! * ClockPicker v0.0.7 for jQuery (http://weareoutman.github.io/clockpicker/) * Copyright 2014 Wang Shenwei. * Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE) * * Bootstrap v3.1.1 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid;overflow:visible;margin:0;padding:0;z-index:auto;background-color:transparent;-webkit-box-shadow:none;box-shadow:none;bottom:auto;left:auto;right:auto;top:auto;-webkit-transform:none;-ms-transform:none;transform:none}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.btn{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent}.btn.active:focus,.btn:active:focus,.btn:focus{outline:dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px}.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.active,.btn-default:active,.btn-default:focus,.btn-default:hover,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default.active,.btn-default:active,.open .dropdown-toggle.btn-default{background-image:none}.btn-block{display:block;width:100%}.text-primary{color:#428bca}.clockpicker .input-group-addon{cursor:pointer}.clockpicker-moving{cursor:move}.clockpicker-align-left.popover>.arrow{left:25px}.clockpicker-align-top.popover>.arrow{top:17px}.clockpicker-align-right.popover>.arrow{left:auto;right:25px}.clockpicker-align-bottom.popover>.arrow{top:auto;bottom:6px}.clockpicker-popover .popover-title{background-color:#fff;color:#999;font-size:24px;font-weight:700;line-height:30px;text-align:center}.clockpicker-popover .popover-title span{cursor:pointer}.clockpicker-popover .popover-content{background-color:#f8f8f8;padding:12px}.popover-content:last-child{border-bottom-left-radius:5px;border-bottom-right-radius:5px}.clockpicker-plate{background-color:#fff;border:1px solid #ccc;border-radius:50%;width:200px;height:200px;overflow:visible;position:relative;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.clockpicker-canvas,.clockpicker-dial{width:200px;height:200px;position:absolute;left:-1px;top:-1px}.clockpicker-minutes{visibility:hidden}.clockpicker-tick{border-radius:50%;color:#666;line-height:26px;text-align:center;width:26px;height:26px;position:absolute;cursor:pointer}.clockpicker-tick.active,.clockpicker-tick:hover{background-color:#c0e5f7;background-color:rgba(0,149,221,.25)}.clockpicker-button{background-image:none;background-color:#fff;border-width:1px 0 0;border-top-left-radius:0;border-top-right-radius:0;margin:0;padding:10px 0}.clockpicker-button:hover{background-image:none;background-color:#ebebeb}.clockpicker-button:focus{outline:0!important}.clockpicker-dial{-webkit-transition:-webkit-transform 350ms,opacity 350ms;-moz-transition:-moz-transform 350ms,opacity 350ms;-ms-transition:-ms-transform 350ms,opacity 350ms;-o-transition:-o-transform 350ms,opacity 350ms;transition:transform 350ms,opacity 350ms}.clockpicker-dial-out{opacity:0}.clockpicker-hours.clockpicker-dial-out{-webkit-transform:scale(1.2,1.2);-moz-transform:scale(1.2,1.2);-ms-transform:scale(1.2,1.2);-o-transform:scale(1.2,1.2);transform:scale(1.2,1.2)}.clockpicker-minutes.clockpicker-dial-out{-webkit-transform:scale(.8,.8);-moz-transform:scale(.8,.8);-ms-transform:scale(.8,.8);-o-transform:scale(.8,.8);transform:scale(.8,.8)}.clockpicker-canvas{-webkit-transition:opacity 175ms;-moz-transition:opacity 175ms;-ms-transition:opacity 175ms;-o-transition:opacity 175ms;transition:opacity 175ms}.clockpicker-canvas-out{opacity:.25}.clockpicker-canvas-bearing,.clockpicker-canvas-fg{stroke:none;fill:#0095dd}.clockpicker-canvas-bg{stroke:none;fill:#c0e5f7}.clockpicker-canvas-bg-trans{fill:rgba(0,149,221,.25)}.clockpicker-canvas line{stroke:#0095dd;stroke-width:1;stroke-linecap:round}.clockpicker-button.am-button{margin:1px;padding:5px;border:1px solid rgba(0,0,0,.2);border-radius:4px}.clockpicker-button.pm-button{margin:1px 1px 1px 136px;padding:5px;border:1px solid rgba(0,0,0,.2);border-radius:4px}assets/js/jquery-clockpicker.min.js000064400000025623147577721060013440 0ustar00;;;/*! * ClockPicker v0.0.7 (http://weareoutman.github.io/clockpicker/) * Copyright 2014 Wang Shenwei. * Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE) */ !function(){function t(t){return document.createElementNS(p,t)}function i(t){return(10>t?"0":"")+t}function e(t){var i=++m+"";return t?t+i:i}function s(s,r){function p(t,i){var e=u.offset(),s=/^touch/.test(t.type),o=e.left+b,n=e.top+b,p=(s?t.originalEvent.touches[0]:t).pageX-o,h=(s?t.originalEvent.touches[0]:t).pageY-n,k=Math.sqrt(p*p+h*h),v=!1;if(!i||!(g-y>k||k>g+y)){t.preventDefault();var m=setTimeout(function(){c.addClass("clockpicker-moving")},200);l&&u.append(x.canvas),x.setHand(p,h,!i,!0),a.off(d).on(d,function(t){t.preventDefault();var i=/^touch/.test(t.type),e=(i?t.originalEvent.touches[0]:t).pageX-o,s=(i?t.originalEvent.touches[0]:t).pageY-n;(v||e!==p||s!==h)&&(v=!0,x.setHand(e,s,!1,!0))}),a.off(f).on(f,function(t){a.off(f),t.preventDefault();var e=/^touch/.test(t.type),s=(e?t.originalEvent.changedTouches[0]:t).pageX-o,l=(e?t.originalEvent.changedTouches[0]:t).pageY-n;(i||v)&&s===p&&l===h&&x.setHand(s,l),"hours"===x.currentView?x.toggleView("minutes",A/2):r.autoclose&&(x.minutesView.addClass("clockpicker-dial-out"),setTimeout(function(){x.done()},A/2)),u.prepend(j),clearTimeout(m),c.removeClass("clockpicker-moving"),a.off(d)})}}var h=n(V),u=h.find(".clockpicker-plate"),v=h.find(".clockpicker-hours"),m=h.find(".clockpicker-minutes"),T=h.find(".clockpicker-am-pm-block"),C="INPUT"===s.prop("tagName"),H=C?s:s.find("input"),P=s.find(".input-group-addon"),x=this;if(this.id=e("cp"),this.element=s,this.options=r,this.isAppended=!1,this.isShown=!1,this.currentView="hours",this.isInput=C,this.input=H,this.addon=P,this.popover=h,this.plate=u,this.hoursView=v,this.minutesView=m,this.amPmBlock=T,this.spanHours=h.find(".clockpicker-span-hours"),this.spanMinutes=h.find(".clockpicker-span-minutes"),this.spanAmPm=h.find(".clockpicker-span-am-pm"),this.amOrPm="PM",r.twelvehour){{var S=['
','",'","
"].join("");n(S)}n('').on("click",function(){x.amOrPm="AM",n(".clockpicker-span-am-pm").empty().append("AM")}).appendTo(this.amPmBlock),n('').on("click",function(){x.amOrPm="PM",n(".clockpicker-span-am-pm").empty().append("PM")}).appendTo(this.amPmBlock)}r.autoclose||n('").click(n.proxy(this.done,this)).appendTo(h),"top"!==r.placement&&"bottom"!==r.placement||"top"!==r.align&&"bottom"!==r.align||(r.align="left"),"left"!==r.placement&&"right"!==r.placement||"left"!==r.align&&"right"!==r.align||(r.align="top"),h.addClass(r.placement),h.addClass("clockpicker-align-"+r.align),this.spanHours.click(n.proxy(this.toggleView,this,"hours")),this.spanMinutes.click(n.proxy(this.toggleView,this,"minutes")),H.on("focus.clockpicker click.clockpicker",n.proxy(this.show,this)),P.on("click.clockpicker",n.proxy(this.toggle,this));var E,D,I,B,z=n('
');if(r.twelvehour)for(E=1;13>E;E+=1)D=z.clone(),I=E/6*Math.PI,B=g,D.css("font-size","120%"),D.css({left:b+Math.sin(I)*B-y,top:b-Math.cos(I)*B-y}),D.html(0===E?"00":E),v.append(D),D.on(k,p);else for(E=0;24>E;E+=1){D=z.clone(),I=E/6*Math.PI;var O=E>0&&13>E;B=O?w:g,D.css({left:b+Math.sin(I)*B-y,top:b-Math.cos(I)*B-y}),O&&D.css("font-size","120%"),D.html(0===E?"00":E),v.append(D),D.on(k,p)}for(E=0;60>E;E+=5)D=z.clone(),I=E/30*Math.PI,D.css({left:b+Math.sin(I)*g-y,top:b-Math.cos(I)*g-y}),D.css("font-size","120%"),D.html(i(E)),m.append(D),D.on(k,p);if(u.on(k,function(t){0===n(t.target).closest(".clockpicker-tick").length&&p(t,!0)}),l){var j=h.find(".clockpicker-canvas"),L=t("svg");L.setAttribute("class","clockpicker-svg"),L.setAttribute("width",M),L.setAttribute("height",M);var U=t("g");U.setAttribute("transform","translate("+b+","+b+")");var W=t("circle");W.setAttribute("class","clockpicker-canvas-bearing"),W.setAttribute("cx",0),W.setAttribute("cy",0),W.setAttribute("r",2);var N=t("line");N.setAttribute("x1",0),N.setAttribute("y1",0);var X=t("circle");X.setAttribute("class","clockpicker-canvas-bg"),X.setAttribute("r",y);var Y=t("circle");Y.setAttribute("class","clockpicker-canvas-fg"),Y.setAttribute("r",3.5),U.appendChild(N),U.appendChild(X),U.appendChild(Y),U.appendChild(W),L.appendChild(U),j.append(L),this.hand=N,this.bg=X,this.fg=Y,this.bearing=W,this.g=U,this.canvas=j}o(this.options.init)}function o(t){t&&"function"==typeof t&&t()}var c,n=window.jQuery,r=n(window),a=n(document),p="http://www.w3.org/2000/svg",l="SVGAngle"in window&&function(){var t,i=document.createElement("div");return i.innerHTML="",t=(i.firstChild&&i.firstChild.namespaceURI)==p,i.innerHTML="",t}(),h=function(){var t=document.createElement("div").style;return"transition"in t||"WebkitTransition"in t||"MozTransition"in t||"msTransition"in t||"OTransition"in t}(),u="ontouchstart"in window,k="mousedown"+(u?" touchstart":""),d="mousemove.clockpicker"+(u?" touchmove.clockpicker":""),f="mouseup.clockpicker"+(u?" touchend.clockpicker":""),v=navigator.vibrate?"vibrate":navigator.webkitVibrate?"webkitVibrate":null,m=0,b=100,g=80,w=54,y=13,M=2*b,A=h?350:1,V=['
','
','
',''," : ",'','',"
",'
','
','
','
','
',"
",'',"","
","
"].join("");s.DEFAULTS={"default":"",fromnow:0,placement:"bottom",align:"left",donetext:"完成",autoclose:!1,twelvehour:!1,vibrate:!0},s.prototype.toggle=function(){this[this.isShown?"hide":"show"]()},s.prototype.locate=function(){var t=this.element,i=this.popover,e=t.offset(),s=t.outerWidth(),o=t.outerHeight(),c=this.options.placement,n=this.options.align,r={};switch(i.show(),c){case"bottom":r.top=e.top+o;break;case"right":r.left=e.left+s;break;case"top":r.top=e.top-i.outerHeight();break;case"left":r.left=e.left-i.outerWidth()}switch(n){case"left":r.left=e.left;break;case"right":r.left=e.left+s-i.outerWidth();break;case"top":r.top=e.top;break;case"bottom":r.top=e.top+o-i.outerHeight()}i.css(r)},s.prototype.show=function(){if(!this.isShown){o(this.options.beforeShow);var t=this;this.isAppended||(c=n(document.body).append(this.popover),r.on("resize.clockpicker"+this.id,function(){t.isShown&&t.locate()}),this.isAppended=!0);var e=((this.input.prop("value")||this.options["default"]||"")+"").split(":");if("now"===e[0]){var s=new Date(+new Date+this.options.fromnow);e=[s.getHours(),s.getMinutes()]}this.hours=+e[0]||0,this.minutes=+e[1]||0,this.spanHours.html(i(this.hours)),this.spanMinutes.html(i(this.minutes)),this.toggleView("hours"),this.locate(),this.isShown=!0,a.on("click.clockpicker."+this.id+" focusin.clockpicker."+this.id,function(i){var e=n(i.target);0===e.closest(t.popover).length&&0===e.closest(t.addon).length&&0===e.closest(t.input).length&&t.hide()}),a.on("keyup.clockpicker."+this.id,function(i){27===i.keyCode&&t.hide()}),o(this.options.afterShow)}},s.prototype.hide=function(){o(this.options.beforeHide),this.isShown=!1,a.off("click.clockpicker."+this.id+" focusin.clockpicker."+this.id),a.off("keyup.clockpicker."+this.id),this.popover.hide(),o(this.options.afterHide)},s.prototype.toggleView=function(t,i){var e=!1;"minutes"===t&&"visible"===n(this.hoursView).css("visibility")&&(o(this.options.beforeHourSelect),e=!0);var s="hours"===t,c=s?this.hoursView:this.minutesView,r=s?this.minutesView:this.hoursView;this.currentView=t,this.spanHours.toggleClass("text-primary",s),this.spanMinutes.toggleClass("text-primary",!s),r.addClass("clockpicker-dial-out"),c.css("visibility","visible").removeClass("clockpicker-dial-out"),this.resetClock(i),clearTimeout(this.toggleViewTimer),this.toggleViewTimer=setTimeout(function(){r.css("visibility","hidden")},A),e&&o(this.options.afterHourSelect)},s.prototype.resetClock=function(t){var i=this.currentView,e=this[i],s="hours"===i,o=Math.PI/(s?6:30),c=e*o,n=s&&e>0&&13>e?w:g,r=Math.sin(c)*n,a=-Math.cos(c)*n,p=this;l&&t?(p.canvas.addClass("clockpicker-canvas-out"),setTimeout(function(){p.canvas.removeClass("clockpicker-canvas-out"),p.setHand(r,a)},t)):this.setHand(r,a)},s.prototype.setHand=function(t,e,s,o){var c,r=Math.atan2(t,-e),a="hours"===this.currentView,p=Math.PI/(a||s?6:30),h=Math.sqrt(t*t+e*e),u=this.options,k=a&&(g+w)/2>h,d=k?w:g;if(u.twelvehour&&(d=g),0>r&&(r=2*Math.PI+r),c=Math.round(r/p),r=c*p,u.twelvehour?a?0===c&&(c=12):(s&&(c*=5),60===c&&(c=0)):a?(12===c&&(c=0),c=k?0===c?12:c:0===c?0:c+12):(s&&(c*=5),60===c&&(c=0)),this[this.currentView]!==c&&v&&this.options.vibrate&&(this.vibrateTimer||(navigator[v](10),this.vibrateTimer=setTimeout(n.proxy(function(){this.vibrateTimer=null},this),100))),this[this.currentView]=c,this[a?"spanHours":"spanMinutes"].html(i(c)),!l)return void this[a?"hoursView":"minutesView"].find(".clockpicker-tick").each(function(){var t=n(this);t.toggleClass("active",c===+t.html())});o||!a&&c%5?(this.g.insertBefore(this.hand,this.bearing),this.g.insertBefore(this.bg,this.fg),this.bg.setAttribute("class","clockpicker-canvas-bg clockpicker-canvas-bg-trans")):(this.g.insertBefore(this.hand,this.bg),this.g.insertBefore(this.fg,this.bg),this.bg.setAttribute("class","clockpicker-canvas-bg"));var f=Math.sin(r)*d,m=-Math.cos(r)*d;this.hand.setAttribute("x2",f),this.hand.setAttribute("y2",m),this.bg.setAttribute("cx",f),this.bg.setAttribute("cy",m),this.fg.setAttribute("cx",f),this.fg.setAttribute("cy",m)},s.prototype.done=function(){o(this.options.beforeDone),this.hide();var t=this.input.prop("value"),e=i(this.hours)+":"+i(this.minutes);this.options.twelvehour&&(e+=this.amOrPm),this.input.prop("value",e),e!==t&&(this.input.triggerHandler("change"),this.isInput||this.element.trigger("change")),this.options.autoclose&&this.input.trigger("blur"),o(this.options.afterDone)},s.prototype.remove=function(){this.element.removeData("clockpicker"),this.input.off("focus.clockpicker click.clockpicker"),this.addon.off("click.clockpicker"),this.isShown&&this.hide(),this.isAppended&&(r.off("resize.clockpicker"+this.id),this.popover.remove())},n.fn.clockpicker=function(t){var i=Array.prototype.slice.call(arguments,1);return this.each(function(){var e=n(this),o=e.data("clockpicker");if(o)"function"==typeof o[t]&&o[t].apply(o,i);else{var c=n.extend({},s.DEFAULTS,e.data(),"object"==typeof t&&t);e.data("clockpicker",new s(e,c))}})}}();assets/js/jquery.qrcode.min.js000064400000051434147577721060012424 0ustar00;;;/* jQuery.qrcode 0.12.0 - http://larsjung.de/jquery-qrcode/ - uses //github.com/kazuhikoarase/qrcode-generator (MIT) */ !function(r){"use strict";function t(t,e,n,o){function i(r,t){return r-=o,t-=o,0>r||r>=u||0>t||t>=u?!1:a.isDark(r,t)}var a=r(n,e);a.addData(t),a.make(),o=o||0;var u=a.getModuleCount(),f=a.getModuleCount()+2*o,c=function(r,t,e,n){var o=this.isDark,i=1/f;this.isDark=function(a,u){var f=u*i,c=a*i,l=f+i,g=c+i;return o(a,u)&&(r>l||f>e||t>g||c>n)}};this.text=t,this.level=e,this.version=n,this.moduleCount=f,this.isDark=i,this.addBlank=c}function e(r,e,n,o,i){n=Math.max(1,n||1),o=Math.min(40,o||40);for(var a=n;o>=a;a+=1)try{return new t(r,e,a,i)}catch(u){}}function n(r,t,e){var n=e.size,o="bold "+e.mSize*n+"px "+e.fontname,i=w("")[0].getContext("2d");i.font=o;var a=i.measureText(e.label).width,u=e.mSize,f=a/n,c=(1-f)*e.mPosX,l=(1-u)*e.mPosY,g=c+f,s=l+u,h=.01;1===e.mode?r.addBlank(0,l-h,n,s+h):r.addBlank(c-h,l-h,g+h,s+h),t.fillStyle=e.fontcolor,t.font=o,t.fillText(e.label,c*n,l*n+.75*e.mSize*n)}function o(r,t,e){var n=e.size,o=e.image.naturalWidth||1,i=e.image.naturalHeight||1,a=e.mSize,u=a*o/i,f=(1-u)*e.mPosX,c=(1-a)*e.mPosY,l=f+u,g=c+a,s=.01;3===e.mode?r.addBlank(0,c-s,n,g+s):r.addBlank(f-s,c-s,l+s,g+s),t.drawImage(e.image,f*n,c*n,u*n,a*n)}function i(r,t,e){w(e.background).is("img")?t.drawImage(e.background,0,0,e.size,e.size):e.background&&(t.fillStyle=e.background,t.fillRect(e.left,e.top,e.size,e.size));var i=e.mode;1===i||2===i?n(r,t,e):(3===i||4===i)&&o(r,t,e)}function a(r,t,e,n,o,i,a,u){r.isDark(a,u)&&t.rect(n,o,i,i)}function u(r,t,e,n,o,i,a,u,f,c){a?r.moveTo(t+i,e):r.moveTo(t,e),u?(r.lineTo(n-i,e),r.arcTo(n,e,n,o,i)):r.lineTo(n,e),f?(r.lineTo(n,o-i),r.arcTo(n,o,t,o,i)):r.lineTo(n,o),c?(r.lineTo(t+i,o),r.arcTo(t,o,t,e,i)):r.lineTo(t,o),a?(r.lineTo(t,e+i),r.arcTo(t,e,n,e,i)):r.lineTo(t,e)}function f(r,t,e,n,o,i,a,u,f,c){a&&(r.moveTo(t+i,e),r.lineTo(t,e),r.lineTo(t,e+i),r.arcTo(t,e,t+i,e,i)),u&&(r.moveTo(n-i,e),r.lineTo(n,e),r.lineTo(n,e+i),r.arcTo(n,e,n-i,e,i)),f&&(r.moveTo(n-i,o),r.lineTo(n,o),r.lineTo(n,o-i),r.arcTo(n,o,n-i,o,i)),c&&(r.moveTo(t+i,o),r.lineTo(t,o),r.lineTo(t,o-i),r.arcTo(t,o,t+i,o,i))}function c(r,t,e,n,o,i,a,c){var l=r.isDark,g=n+i,s=o+i,h=e.radius*i,v=a-1,d=a+1,w=c-1,m=c+1,p=l(a,c),y=l(v,w),T=l(v,c),B=l(v,m),A=l(a,m),E=l(d,m),k=l(d,c),M=l(d,w),C=l(a,w);p?u(t,n,o,g,s,h,!T&&!C,!T&&!A,!k&&!A,!k&&!C):f(t,n,o,g,s,h,T&&C&&y,T&&A&&B,k&&A&&E,k&&C&&M)}function l(r,t,e){var n,o,i=r.moduleCount,u=e.size/i,f=a;for(p&&e.radius>0&&e.radius<=.5&&(f=c),t.beginPath(),n=0;i>n;n+=1)for(o=0;i>o;o+=1){var l=e.left+o*u,g=e.top+n*u,s=u;f(r,t,e,l,g,s,n,o)}if(w(e.fill).is("img")){t.strokeStyle="rgba(0,0,0,0.5)",t.lineWidth=2,t.stroke();var h=t.globalCompositeOperation;t.globalCompositeOperation="destination-out",t.fill(),t.globalCompositeOperation=h,t.clip(),t.drawImage(e.fill,0,0,e.size,e.size),t.restore()}else t.fillStyle=e.fill,t.fill()}function g(r,t){var n=e(t.text,t.ecLevel,t.minVersion,t.maxVersion,t.quiet);if(!n)return null;var o=w(r).data("qrcode",n),a=o[0].getContext("2d");return i(n,a,t),l(n,a,t),o}function s(r){var t=w("").attr("width",r.size).attr("height",r.size);return g(t,r)}function h(r){return w("").attr("src",s(r)[0].toDataURL("image/png"))}function v(r){var t=e(r.text,r.ecLevel,r.minVersion,r.maxVersion,r.quiet);if(!t)return null;var n,o,i=r.size,a=r.background,u=Math.floor,f=t.moduleCount,c=u(i/f),l=u(.5*(i-c*f)),g={position:"relative",left:0,top:0,padding:0,margin:0,width:i,height:i},s={position:"absolute",padding:0,margin:0,width:c,height:c,"background-color":r.fill},h=w("
").data("qrcode",t).css(g);for(a&&h.css("background-color",a),n=0;f>n;n+=1)for(o=0;f>o;o+=1)t.isDark(n,o)&&w("
").css(s).css({left:l+o*c,top:l+n*c}).appendTo(h);return h}function d(r){return m&&"canvas"===r.render?s(r):m&&"image"===r.render?h(r):v(r)}var w=jQuery,m=function(){var r=document.createElement("canvas");return Boolean(r.getContext&&r.getContext("2d"))}(),p="[object Opera]"!==Object.prototype.toString.call(window.opera),y={render:"canvas",minVersion:1,maxVersion:40,ecLevel:"L",left:0,top:0,size:200,fill:"#000",background:null,text:"no text",radius:0,quiet:0,mode:0,mSize:.1,mPosX:.5,mPosY:.5,label:"no label",fontname:"sans",fontcolor:"#000",image:null};w.fn.qrcode=function(r){var t=w.extend({},y,r);return this.each(function(){"canvas"===this.nodeName.toLowerCase()?g(this,t):w(this).append(d(t))})}}(function(){var r=function(){function r(t,e){if("undefined"==typeof t.length)throw new Error(t.length+"/"+e);var n=function(){for(var r=0;re;e+=1){t[e]=new Array(r);for(var n=0;r>n;n+=1)t[e][n]=null}return t}(h),y(0,0),y(h-7,0),y(0,h-7),A(),B(),k(r,t),l>=7&&E(r),null==d&&(d=D(l,g,w)),M(d,t)},y=function(r,t){for(var e=-1;7>=e;e+=1)if(!(-1>=r+e||r+e>=h))for(var n=-1;7>=n;n+=1)-1>=t+n||t+n>=h||(e>=0&&6>=e&&(0==n||6==n)||n>=0&&6>=n&&(0==e||6==e)||e>=2&&4>=e&&n>=2&&4>=n?s[r+e][t+n]=!0:s[r+e][t+n]=!1)},T=function(){for(var r=0,t=0,e=0;8>e;e+=1){p(!0,e);var n=i.getLostPoint(m);(0==e||r>n)&&(r=n,t=e)}return t},B=function(){for(var r=8;h-8>r;r+=1)null==s[r][6]&&(s[r][6]=r%2==0);for(var t=8;h-8>t;t+=1)null==s[6][t]&&(s[6][t]=t%2==0)},A=function(){for(var r=i.getPatternPosition(l),t=0;t=a;a+=1)for(var u=-2;2>=u;u+=1)-2==a||2==a||-2==u||2==u||0==a&&0==u?s[n+a][o+u]=!0:s[n+a][o+u]=!1}},E=function(r){for(var t=i.getBCHTypeNumber(l),e=0;18>e;e+=1){var n=!r&&1==(t>>e&1);s[Math.floor(e/3)][e%3+h-8-3]=n}for(var e=0;18>e;e+=1){var n=!r&&1==(t>>e&1);s[e%3+h-8-3][Math.floor(e/3)]=n}},k=function(r,t){for(var e=g<<3|t,n=i.getBCHTypeInfo(e),o=0;15>o;o+=1){var a=!r&&1==(n>>o&1);6>o?s[o][8]=a:8>o?s[o+1][8]=a:s[h-15+o][8]=a}for(var o=0;15>o;o+=1){var a=!r&&1==(n>>o&1);8>o?s[8][h-o-1]=a:9>o?s[8][15-o-1+1]=a:s[8][15-o-1]=a}s[h-8][8]=!r},M=function(r,t){for(var e=-1,n=h-1,o=7,a=0,u=i.getMaskFunction(t),f=h-1;f>0;f-=2)for(6==f&&(f-=1);;){for(var c=0;2>c;c+=1)if(null==s[n][f-c]){var l=!1;a>>o&1));var g=u(n,f-c);g&&(l=!l),s[n][f-c]=l,o-=1,-1==o&&(a+=1,o=7)}if(n+=e,0>n||n>=h){n-=e,e=-e;break}}},C=function(t,e){for(var n=0,o=0,a=0,u=new Array(e.length),f=new Array(e.length),c=0;c=0?d.getAt(w):0}}for(var m=0,s=0;ss;s+=1)for(var c=0;cs;s+=1)for(var c=0;c8*s)throw new Error("code length overflow. ("+c.getLengthInBits()+">"+8*s+")");for(c.getLengthInBits()+4<=8*s&&c.put(0,4);c.getLengthInBits()%8!=0;)c.putBit(!1);for(;;){if(c.getLengthInBits()>=8*s)break;if(c.put(o,8),c.getLengthInBits()>=8*s)break;c.put(a,8)}return C(c,n)};return m.addData=function(r){var t=c(r);w.push(t),d=null},m.isDark=function(r,t){if(0>r||r>=h||0>t||t>=h)throw new Error(r+","+t);return s[r][t]},m.getModuleCount=function(){return h},m.make=function(){p(!1,T())},m.createTableTag=function(r,t){r=r||2,t="undefined"==typeof t?4*r:t;var e="";e+='";for(var o=0;o';e+=""}return e+="",e+="
"},m.createImgTag=function(r,t){r=r||2,t="undefined"==typeof t?4*r:t;var e=m.getModuleCount()*r+2*t,n=t,o=e-t;return v(e,e,function(t,e){if(t>=n&&o>t&&e>=n&&o>e){var i=Math.floor((t-n)/r),a=Math.floor((e-n)/r);return m.isDark(a,i)?0:1}return 1})},m};t.stringToBytes=function(r){for(var t=new Array,e=0;ei)t.push(i);else{var a=e[r.charAt(o)];"number"==typeof a?(255&a)==a?t.push(a):(t.push(a>>>8),t.push(255&a)):t.push(n)}}return t}};var e={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},n={L:1,M:0,Q:3,H:2},o={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},i=function(){var t=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],n=1335,i=7973,u=21522,f={},c=function(r){for(var t=0;0!=r;)t+=1,r>>>=1;return t};return f.getBCHTypeInfo=function(r){for(var t=r<<10;c(t)-c(n)>=0;)t^=n<=0;)t^=i<n;n+=1)e=e.multiply(r([1,a.gexp(n)],0));return e},f.getLengthInBits=function(r,t){if(t>=1&&10>t)switch(r){case e.MODE_NUMBER:return 10;case e.MODE_ALPHA_NUM:return 9;case e.MODE_8BIT_BYTE:return 8;case e.MODE_KANJI:return 8;default:throw new Error("mode:"+r)}else if(27>t)switch(r){case e.MODE_NUMBER:return 12;case e.MODE_ALPHA_NUM:return 11;case e.MODE_8BIT_BYTE:return 16;case e.MODE_KANJI:return 10;default:throw new Error("mode:"+r)}else{if(!(41>t))throw new Error("type:"+t);switch(r){case e.MODE_NUMBER:return 14;case e.MODE_ALPHA_NUM:return 13;case e.MODE_8BIT_BYTE:return 16;case e.MODE_KANJI:return 12;default:throw new Error("mode:"+r)}}},f.getLostPoint=function(r){for(var t=r.getModuleCount(),e=0,n=0;t>n;n+=1)for(var o=0;t>o;o+=1){for(var i=0,a=r.isDark(n,o),u=-1;1>=u;u+=1)if(!(0>n+u||n+u>=t))for(var f=-1;1>=f;f+=1)0>o+f||o+f>=t||(0!=u||0!=f)&&a==r.isDark(n+u,o+f)&&(i+=1);i>5&&(e+=3+i-5)}for(var n=0;t-1>n;n+=1)for(var o=0;t-1>o;o+=1){var c=0;r.isDark(n,o)&&(c+=1),r.isDark(n+1,o)&&(c+=1),r.isDark(n,o+1)&&(c+=1),r.isDark(n+1,o+1)&&(c+=1),(0==c||4==c)&&(e+=3)}for(var n=0;t>n;n+=1)for(var o=0;t-6>o;o+=1)r.isDark(n,o)&&!r.isDark(n,o+1)&&r.isDark(n,o+2)&&r.isDark(n,o+3)&&r.isDark(n,o+4)&&!r.isDark(n,o+5)&&r.isDark(n,o+6)&&(e+=40);for(var o=0;t>o;o+=1)for(var n=0;t-6>n;n+=1)r.isDark(n,o)&&!r.isDark(n+1,o)&&r.isDark(n+2,o)&&r.isDark(n+3,o)&&r.isDark(n+4,o)&&!r.isDark(n+5,o)&&r.isDark(n+6,o)&&(e+=40);for(var l=0,o=0;t>o;o+=1)for(var n=0;t>n;n+=1)r.isDark(n,o)&&(l+=1);var g=Math.abs(100*l/t/t-50)/5;return e+=10*g},f}(),a=function(){for(var r=new Array(256),t=new Array(256),e=0;8>e;e+=1)r[e]=1<e;e+=1)r[e]=r[e-4]^r[e-5]^r[e-6]^r[e-8];for(var e=0;255>e;e+=1)t[r[e]]=e;var n={};return n.glog=function(r){if(1>r)throw new Error("glog("+r+")");return t[r]},n.gexp=function(t){for(;0>t;)t+=255;for(;t>=256;)t-=255;return r[t]},n}(),u=function(){var r=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12,7,37,13],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],t=function(r,t){var e={};return e.totalCount=r,e.dataCount=t,e},e={},o=function(t,e){switch(e){case n.L:return r[4*(t-1)+0];case n.M:return r[4*(t-1)+1];case n.Q:return r[4*(t-1)+2];case n.H:return r[4*(t-1)+3];default:return void 0}};return e.getRSBlocks=function(r,e){var n=o(r,e);if("undefined"==typeof n)throw new Error("bad rs block @ typeNumber:"+r+"/errorCorrectLevel:"+e);for(var i=n.length/3,a=new Array,u=0;i>u;u+=1)for(var f=n[3*u+0],c=n[3*u+1],l=n[3*u+2],g=0;f>g;g+=1)a.push(t(c,l));return a},e}(),f=function(){var r=new Array,t=0,e={};return e.getBuffer=function(){return r},e.getAt=function(t){var e=Math.floor(t/8);return 1==(r[e]>>>7-t%8&1)},e.put=function(r,t){for(var n=0;t>n;n+=1)e.putBit(1==(r>>>t-n-1&1))},e.getLengthInBits=function(){return t},e.putBit=function(e){var n=Math.floor(t/8);r.length<=n&&r.push(0),e&&(r[n]|=128>>>t%8),t+=1},e},c=function(r){var n=e.MODE_8BIT_BYTE,o=t.stringToBytes(r),i={};return i.getMode=function(){return n},i.getLength=function(r){return o.length},i.write=function(r){for(var t=0;t>>8)},t.writeBytes=function(r,e,n){e=e||0,n=n||r.length;for(var o=0;n>o;o+=1)t.writeByte(r[o+e])},t.writeString=function(r){for(var e=0;e0&&(t+=","),t+=r[e];return t+="]"},t},g=function(){var r=0,t=0,e=0,n="",o={},i=function(r){n+=String.fromCharCode(a(63&r))},a=function(r){if(0>r);else{if(26>r)return 65+r;if(52>r)return 97+(r-26);if(62>r)return 48+(r-52);if(62==r)return 43;if(63==r)return 47}throw new Error("n:"+r)};return o.writeByte=function(n){for(r=r<<8|255&n,t+=8,e+=1;t>=6;)i(r>>>t-6),t-=6},o.flush=function(){if(t>0&&(i(r<<6-t),r=0,t=0),e%3!=0)for(var o=3-e%3,a=0;o>a;a+=1)n+="="},o.toString=function(){return n},o},s=function(r){var t=r,e=0,n=0,o=0,i={};i.read=function(){for(;8>o;){if(e>=t.length){if(0==o)return-1;throw new Error("unexpected end of file./"+o)}var r=t.charAt(e);if(e+=1,"="==r)return o=0,-1;r.match(/^\s$/)||(n=n<<6|a(r.charCodeAt(0)),o+=6)}var i=n>>>o-8&255;return o-=8,i};var a=function(r){if(r>=65&&90>=r)return r-65;if(r>=97&&122>=r)return r-97+26;if(r>=48&&57>=r)return r-48+52;if(43==r)return 62;if(47==r)return 63;throw new Error("c:"+r)};return i},h=function(r,t){var e=r,n=t,o=new Array(r*t),i={};i.setPixel=function(r,t,n){o[t*e+r]=n},i.write=function(r){r.writeString("GIF87a"),r.writeShort(e),r.writeShort(n),r.writeByte(128),r.writeByte(0),r.writeByte(0),r.writeByte(0),r.writeByte(0),r.writeByte(0),r.writeByte(255),r.writeByte(255),r.writeByte(255),r.writeString(","),r.writeShort(0),r.writeShort(0),r.writeShort(e),r.writeShort(n),r.writeByte(0);var t=2,o=u(t);r.writeByte(t);for(var i=0;o.length-i>255;)r.writeByte(255),r.writeBytes(o,i,255),i+=255;r.writeByte(o.length-i),r.writeBytes(o,i,o.length-i),r.writeByte(0),r.writeString(";")};var a=function(r){var t=r,e=0,n=0,o={};return o.write=function(r,o){if(r>>>o!=0)throw new Error("length over");for(;e+o>=8;)t.writeByte(255&(r<>>=8-e,n=0,e=0;n=r<0&&t.writeByte(n)},o},u=function(r){for(var t=1<u;u+=1)i.add(String.fromCharCode(u));i.add(String.fromCharCode(t)),i.add(String.fromCharCode(e));var c=l(),g=a(c);g.write(t,n);var s=0,h=String.fromCharCode(o[s]);for(s+=1;si;i+=1)for(var a=0;r>a;a+=1)o.setPixel(a,i,e(a,i));var u=l();o.write(u);for(var f=g(),c=u.toByteArray(),s=0;sn?t.push(n):2048>n?t.push(192|n>>6,128|63&n):55296>n||n>=57344?t.push(224|n>>12,128|n>>6&63,128|63&n):(e++,n=65536+((1023&n)<<10|1023&r.charCodeAt(e)),t.push(240|n>>18,128|n>>12&63,128|n>>6&63,128|63&n))}return t}return t(r)}}(r),r}());assets/js/multi-recaptcha.js000064400000003152147577721060012123 0ustar00;;; // WooCommerce Registration form jQuery(document).ready(function(){ show_lz_multi_cap(); }); function captcha_v2_invisible_process(el){ grecaptcha.ready(function() { var cap_div_id = grecaptcha.render(el,{ 'sitekey': lz_cap_sitekey, 'size': 'invisible', 'callback' : function (recaptchaToken) { }, 'expired-callback' : function(){grecaptcha.reset(cap_div_id);} }); grecaptcha.execute(cap_div_id); }); } function show_lz_multi_cap(){ if(typeof grecaptcha == 'undefined'){ setTimeout(function(){show_lz_multi_cap()}, 1000); return; } // reCAPTCHA v2 tick box jQuery('.lz-recaptcha').each(function(index, el) { // Get the sitekey var lz_cap_key = jQuery(this).attr('data-sitekey'); //alert(lz_cap_key); // Render it try{ grecaptcha.render(el, {'sitekey' : lz_cap_key}); }catch(e){} }); // reCAPTCHA v2 invisible if(typeof lz_cap_invisible !== 'undefined' && lz_cap_invisible == 1 && typeof lz_cap_ver !== 'undefined' && lz_cap_ver == 2){ for (var i = 0; i < document.forms.length; ++i) { var form = document.forms[i]; var cap_div = form.querySelector("."+lz_cap_div_class); if (null === cap_div) continue; cap_div.innerHTML = ''; captcha_v2_invisible_process(cap_div); } } // reCAPTCHA v3 if(typeof lz_cap_ver !== 'undefined' && lz_cap_ver == 3){ grecaptcha.ready(function() { grecaptcha.execute(lz_cap_sitekey, {action: lz_cap_page_type}).then(function(token) { // pass the token to the backend script for verification jQuery(".lz-v3-input").each(function() { jQuery(this).val(token); }); }); }); } }lib/hybridauth/Provider/TwitchTV.php000064400000003742147577721060013522 0ustar00apiRequestHeaders['Client-ID'] = $this->clientId; } /** * {@inheritdoc} */ public function getUserProfile() { $response = $this->apiRequest('users'); $data = new Data\Collection($response); if (!$data->exists('data')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $users = $data->filter('data')->values(); $user = new Data\Collection($users[0]); $userProfile = new User\Profile(); $userProfile->identifier = $user->get('id'); $userProfile->displayName = $user->get('display_name'); $userProfile->photoURL = $user->get('profile_image_url'); $userProfile->email = $user->get('email'); $userProfile->description = strip_tags($user->get('description')); $userProfile->profileURL = "https://www.twitch.tv/{$userProfile->displayName}"; return $userProfile; } } lib/hybridauth/Provider/Twitter.php000064400000017147147577721060013454 0ustar00 Hybridauth\HttpClient\Util::getCurrentUrl(), * 'keys' => ['key' => '', 'secret' => ''], // OAuth1 uses 'key' not 'id' * 'authorize' => true // Needed to perform actions on behalf of users (see below link) * // https://developer.twitter.com/en/docs/authentication/oauth-1-0a/obtaining-user-access-tokens * ]; * * $adapter = new Hybridauth\Provider\Twitter($config); * * try { * $adapter->authenticate(); * * $userProfile = $adapter->getUserProfile(); * $tokens = $adapter->getAccessToken(); * $contacts = $adapter->getUserContacts(['screen_name' =>'andypiper']); // get those of @andypiper * $activity = $adapter->getUserActivity('me'); * } catch (\Exception $e) { * echo $e->getMessage() ; * } */ class Twitter extends OAuth1 { /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://api.twitter.com/1.1/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://api.twitter.com/oauth/authenticate'; /** * {@inheritdoc} */ protected $requestTokenUrl = 'https://api.twitter.com/oauth/request_token'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://api.twitter.com/oauth/access_token'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://dev.twitter.com/web/sign-in/implementing'; /** * {@inheritdoc} */ protected function getAuthorizeUrl($parameters = []) { if ($this->config->get('authorize') === true) { $this->authorizeUrl = 'https://api.twitter.com/oauth/authorize'; } return parent::getAuthorizeUrl($parameters); } /** * {@inheritdoc} */ public function getUserProfile() { $response = $this->apiRequest('account/verify_credentials.json', 'GET', [ 'include_email' => $this->config->get('include_email') === false ? 'false' : 'true', ]); $data = new Data\Collection($response); if (!$data->exists('id_str')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('id_str'); $userProfile->displayName = $data->get('screen_name'); $userProfile->description = $data->get('description'); $userProfile->firstName = $data->get('name'); $userProfile->email = $data->get('email'); $userProfile->emailVerified = $data->get('email'); $userProfile->webSiteURL = $data->get('url'); $userProfile->region = $data->get('location'); $userProfile->profileURL = $data->exists('screen_name') ? ('https://twitter.com/' . $data->get('screen_name')) : ''; $photoSize = $this->config->get('photo_size') ?: 'original'; $photoSize = $photoSize === 'original' ? '' : "_{$photoSize}"; $userProfile->photoURL = $data->exists('profile_image_url_https') ? str_replace('_normal', $photoSize, $data->get('profile_image_url_https')) : ''; $userProfile->data = [ 'followed_by' => $data->get('followers_count'), 'follows' => $data->get('friends_count'), ]; return $userProfile; } /** * {@inheritdoc} */ public function getUserContacts($parameters = []) { $parameters = ['cursor' => '-1'] + $parameters; $response = $this->apiRequest('friends/ids.json', 'GET', $parameters); $data = new Data\Collection($response); if (!$data->exists('ids')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } if ($data->filter('ids')->isEmpty()) { return []; } $contacts = []; // 75 id per time should be okey $contactsIds = array_chunk((array)$data->get('ids'), 75); foreach ($contactsIds as $chunk) { $parameters = ['user_id' => implode(',', $chunk)]; try { $response = $this->apiRequest('users/lookup.json', 'GET', $parameters); if ($response && count($response)) { foreach ($response as $item) { $contacts[] = $this->fetchUserContact($item); } } } catch (\Exception $e) { continue; } } return $contacts; } /** * @param $item * * @return User\Contact */ protected function fetchUserContact($item) { $item = new Data\Collection($item); $userContact = new User\Contact(); $userContact->identifier = $item->get('id_str'); $userContact->displayName = $item->get('name'); $userContact->photoURL = $item->get('profile_image_url'); $userContact->description = $item->get('description'); $userContact->profileURL = $item->exists('screen_name') ? ('https://twitter.com/' . $item->get('screen_name')) : ''; return $userContact; } /** * {@inheritdoc} */ public function setUserStatus($status) { if (is_string($status)) { $status = ['status' => $status]; } // Prepare request parameters. $params = []; if (isset($status['status'])) { $params['status'] = $status['status']; } if (isset($status['picture'])) { $media = $this->apiRequest('https://upload.twitter.com/1.1/media/upload.json', 'POST', [ 'media' => base64_encode(file_get_contents($status['picture'])), ]); $params['media_ids'] = $media->media_id; } $response = $this->apiRequest('statuses/update.json', 'POST', $params); return $response; } /** * {@inheritdoc} */ public function getUserActivity($stream = 'me') { $apiUrl = ($stream == 'me') ? 'statuses/user_timeline.json' : 'statuses/home_timeline.json'; $response = $this->apiRequest($apiUrl); if (!$response) { return []; } $activities = []; foreach ($response as $item) { $activities[] = $this->fetchUserActivity($item); } return $activities; } /** * @param $item * @return User\Activity */ protected function fetchUserActivity($item) { $item = new Data\Collection($item); $userActivity = new User\Activity(); $userActivity->id = $item->get('id_str'); $userActivity->date = $item->get('created_at'); $userActivity->text = $item->get('text'); $userActivity->user->identifier = $item->filter('user')->get('id_str'); $userActivity->user->displayName = $item->filter('user')->get('name'); $userActivity->user->photoURL = $item->filter('user')->get('profile_image_url'); $userActivity->user->profileURL = $item->filter('user')->get('screen_name') ? ('https://twitter.com/' . $item->filter('user')->get('screen_name')) : ''; return $userActivity; } } lib/hybridauth/Provider/Google.php000064400000013247147577721060013223 0ustar00 Hybridauth\HttpClient\Util::getCurrentUrl(), * 'keys' => ['id' => '', 'secret' => ''], * 'scope' => 'https://www.googleapis.com/auth/userinfo.profile', * * // google's custom auth url params * 'authorize_url_parameters' => [ * 'approval_prompt' => 'force', // to pass only when you need to acquire a new refresh token. * 'access_type' => .., // is set to 'offline' by default * 'hd' => .., * 'state' => .., * // etc. * ] * ]; * * $adapter = new Hybridauth\Provider\Google($config); * * try { * $adapter->authenticate(); * * $userProfile = $adapter->getUserProfile(); * $tokens = $adapter->getAccessToken(); * $contacts = $adapter->getUserContacts(['max-results' => 75]); * } catch (\Exception $e) { * echo $e->getMessage() ; * } */ class Google extends OAuth2 { /** * {@inheritdoc} */ // phpcs:ignore protected $scope = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email'; /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://www.googleapis.com/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://accounts.google.com/o/oauth2/v2/auth'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://oauth2.googleapis.com/token'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://developers.google.com/identity/protocols/OAuth2'; /** * {@inheritdoc} */ protected function initialize() { parent::initialize(); $this->AuthorizeUrlParameters += [ 'access_type' => 'offline' ]; if ($this->isRefreshTokenAvailable()) { $this->tokenRefreshParameters += [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret ]; } } /** * {@inheritdoc} * * See: https://developers.google.com/identity/protocols/OpenIDConnect#obtainuserinfo */ public function getUserProfile() { $response = $this->apiRequest('oauth2/v3/userinfo'); $data = new Data\Collection($response); if (!$data->exists('sub')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('sub'); $userProfile->firstName = $data->get('given_name'); $userProfile->lastName = $data->get('family_name'); $userProfile->displayName = $data->get('name'); $userProfile->photoURL = $data->get('picture'); $userProfile->profileURL = $data->get('profile'); $userProfile->gender = $data->get('gender'); $userProfile->language = $data->get('locale'); $userProfile->email = $data->get('email'); $userProfile->emailVerified = $data->get('email_verified') ? $userProfile->email : ''; if ($this->config->get('photo_size')) { $userProfile->photoURL .= '?sz=' . $this->config->get('photo_size'); } return $userProfile; } /** * {@inheritdoc} */ public function getUserContacts($parameters = []) { $parameters = ['max-results' => 500] + $parameters; // Google Gmail and Android contacts if (false !== strpos($this->scope, '/m8/feeds/') || false !== strpos($this->scope, '/auth/contacts.readonly')) { return $this->getGmailContacts($parameters); } return []; } /** * Retrieve Gmail contacts * * @param array $parameters * * @return array * * @throws \Exception */ protected function getGmailContacts($parameters = []) { $url = 'https://www.google.com/m8/feeds/contacts/default/full?' . http_build_query(array_replace(['alt' => 'json', 'v' => '3.0'], (array)$parameters)); $response = $this->apiRequest($url); if (!$response) { return []; } $contacts = []; if (isset($response->feed->entry)) { foreach ($response->feed->entry as $idx => $entry) { $uc = new User\Contact(); $uc->email = isset($entry->{'gd$email'}[0]->address) ? (string)$entry->{'gd$email'}[0]->address : ''; $uc->displayName = isset($entry->title->{'$t'}) ? (string)$entry->title->{'$t'} : ''; $uc->identifier = ($uc->email != '') ? $uc->email : ''; $uc->description = ''; if (property_exists($response, 'website')) { if (is_array($response->website)) { foreach ($response->website as $w) { if ($w->primary == true) { $uc->webSiteURL = $w->value; } } } else { $uc->webSiteURL = $response->website->value; } } else { $uc->webSiteURL = ''; } $contacts[] = $uc; } } return $contacts; } } lib/hybridauth/Provider/WordPress.php000064400000003445147577721060013736 0ustar00apiRequest('me/'); $data = new Data\Collection($response); if (!$data->exists('ID')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('ID'); $userProfile->displayName = $data->get('display_name'); $userProfile->photoURL = $data->get('avatar_URL'); $userProfile->profileURL = $data->get('profile_URL'); $userProfile->email = $data->get('email'); $userProfile->language = $data->get('language'); $userProfile->displayName = $userProfile->displayName ?: $data->get('username'); $userProfile->emailVerified = $data->get('email_verified') ? $data->get('email') : ''; return $userProfile; } } lib/hybridauth/Provider/Facebook.php000064400000032157147577721060013521 0ustar00 Hybridauth\HttpClient\Util::getCurrentUrl(), * 'keys' => ['id' => '', 'secret' => ''], * 'scope' => 'email, user_status, user_posts', * 'exchange_by_expiry_days' => 45, // null for no token exchange * ]; * * $adapter = new Hybridauth\Provider\Facebook($config); * * try { * $adapter->authenticate(); * * $userProfile = $adapter->getUserProfile(); * $tokens = $adapter->getAccessToken(); * $response = $adapter->setUserStatus("Hybridauth test message.."); * } catch (\Exception $e) { * echo $e->getMessage() ; * } */ class Facebook extends OAuth2 { /** * {@inheritdoc} */ protected $scope = 'email, public_profile'; /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://graph.facebook.com/v8.0/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://www.facebook.com/dialog/oauth'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://graph.facebook.com/oauth/access_token'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://developers.facebook.com/docs/facebook-login/overview'; /** * @var string Profile URL template as the fallback when no `link` returned from the API. */ protected $profileUrlTemplate = 'https://www.facebook.com/%s'; /** * {@inheritdoc} */ protected function initialize() { parent::initialize(); // Require proof on all Facebook api calls // https://developers.facebook.com/docs/graph-api/securing-requests#appsecret_proof if ($accessToken = $this->getStoredData('access_token')) { $this->apiRequestParameters['appsecret_proof'] = hash_hmac('sha256', $accessToken, $this->clientSecret); } } /** * {@inheritdoc} */ public function maintainToken() { if (!$this->isConnected()) { return; } // Handle token exchange prior to the standard handler for an API request $exchange_by_expiry_days = $this->config->get('exchange_by_expiry_days') ?: 45; if ($exchange_by_expiry_days !== null) { $projected_timestamp = time() + 60 * 60 * 24 * $exchange_by_expiry_days; if (!$this->hasAccessTokenExpired() && $this->hasAccessTokenExpired($projected_timestamp)) { $this->exchangeAccessToken(); } } } /** * Exchange the Access Token with one that expires further in the future. * * @return string Raw Provider API response * @throws \Hybridauth\Exception\HttpClientFailureException * @throws \Hybridauth\Exception\HttpRequestFailedException * @throws \Hybridauth\Exception\InvalidAccessTokenException */ public function exchangeAccessToken() { $exchangeTokenParameters = [ 'grant_type' => 'fb_exchange_token', 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'fb_exchange_token' => $this->getStoredData('access_token'), ]; $response = $this->httpClient->request( $this->accessTokenUrl, 'GET', $exchangeTokenParameters ); $this->validateApiResponse('Unable to exchange the access token'); $this->validateAccessTokenExchange($response); return $response; } /** * {@inheritdoc} */ public function getUserProfile() { $fields = [ 'id', 'name', 'first_name', 'last_name', 'website', 'locale', 'about', 'email', 'hometown', 'birthday', ]; if (strpos($this->scope, 'user_link') !== false) { $fields[] = 'link'; } if (strpos($this->scope, 'user_gender') !== false) { $fields[] = 'gender'; } // Note that en_US is needed for gender fields to match convention. $locale = $this->config->get('locale') ?: 'en_US'; $response = $this->apiRequest('me', 'GET', [ 'fields' => implode(',', $fields), 'locale' => $locale, ]); $data = new Data\Collection($response); if (!$data->exists('id')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('id'); $userProfile->displayName = $data->get('name'); $userProfile->firstName = $data->get('first_name'); $userProfile->lastName = $data->get('last_name'); $userProfile->profileURL = $data->get('link'); $userProfile->webSiteURL = $data->get('website'); $userProfile->gender = $data->get('gender'); $userProfile->language = $data->get('locale'); $userProfile->description = $data->get('about'); $userProfile->email = $data->get('email'); // Fallback for profile URL in case Facebook does not provide "pretty" link with username (if user set it). if (empty($userProfile->profileURL)) { $userProfile->profileURL = $this->getProfileUrl($userProfile->identifier); } $userProfile->region = $data->filter('hometown')->get('name'); $photoSize = $this->config->get('photo_size') ?: '150'; $userProfile->photoURL = $this->apiBaseUrl . $userProfile->identifier; $userProfile->photoURL .= '/picture?width=' . $photoSize . '&height=' . $photoSize; $userProfile->emailVerified = $userProfile->email; $userProfile = $this->fetchUserRegion($userProfile); $userProfile = $this->fetchBirthday($userProfile, $data->get('birthday')); return $userProfile; } /** * Retrieve the user region. * * @param User\Profile $userProfile * * @return \Hybridauth\User\Profile */ protected function fetchUserRegion(User\Profile $userProfile) { if (!empty($userProfile->region)) { $regionArr = explode(',', $userProfile->region); if (count($regionArr) > 1) { $userProfile->city = trim($regionArr[0]); $userProfile->country = trim($regionArr[1]); } } return $userProfile; } /** * Retrieve the user birthday. * * @param User\Profile $userProfile * @param string $birthday * * @return \Hybridauth\User\Profile */ protected function fetchBirthday(User\Profile $userProfile, $birthday) { $result = (new Data\Parser())->parseBirthday($birthday); $userProfile->birthYear = (int)$result[0]; $userProfile->birthMonth = (int)$result[1]; $userProfile->birthDay = (int)$result[2]; return $userProfile; } /** * /v2.0/me/friends only returns the user's friends who also use the app. * In the cases where you want to let people tag their friends in stories published by your app, * you can use the Taggable Friends API. * * https://developers.facebook.com/docs/apps/faq#unable_full_friend_list */ public function getUserContacts() { $contacts = []; $apiUrl = 'me/friends?fields=link,name'; do { $response = $this->apiRequest($apiUrl); $data = new Data\Collection($response); if (!$data->exists('data')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } if (!$data->filter('data')->isEmpty()) { foreach ($data->filter('data')->toArray() as $item) { $contacts[] = $this->fetchUserContact($item); } } if ($data->filter('paging')->exists('next')) { $apiUrl = $data->filter('paging')->get('next'); $pagedList = true; } else { $pagedList = false; } } while ($pagedList); return $contacts; } /** * Parse the user contact. * * @param array $item * * @return \Hybridauth\User\Contact */ protected function fetchUserContact($item) { $userContact = new User\Contact(); $item = new Data\Collection($item); $userContact->identifier = $item->get('id'); $userContact->displayName = $item->get('name'); $userContact->profileURL = $item->exists('link') ?: $this->getProfileUrl($userContact->identifier); $userContact->photoURL = $this->apiBaseUrl . $userContact->identifier . '/picture?width=150&height=150'; return $userContact; } /** * {@inheritdoc} */ public function setPageStatus($status, $pageId) { $status = is_string($status) ? ['message' => $status] : $status; // Post on user wall. if ($pageId === 'me') { return $this->setUserStatus($status); } // Retrieve writable user pages and filter by given one. $pages = $this->getUserPages(true); $pages = array_filter($pages, function ($page) use ($pageId) { return $page->id == $pageId; }); if (!$pages) { throw new InvalidArgumentException('Could not find a page with given id.'); } $page = reset($pages); // Use page access token instead of user access token. $headers = [ 'Authorization' => 'Bearer ' . $page->access_token, ]; // Refresh proof for API call. $parameters = $status + [ 'appsecret_proof' => hash_hmac('sha256', $page->access_token, $this->clientSecret), ]; $response = $this->apiRequest("{$pageId}/feed", 'POST', $parameters, $headers); return $response; } /** * {@inheritdoc} */ public function getUserPages($writable = false) { $pages = $this->apiRequest('me/accounts'); if (!$writable) { return $pages->data; } // Filter user pages by CREATE_CONTENT permission. return array_filter($pages->data, function ($page) { return in_array('CREATE_CONTENT', $page->tasks); }); } /** * {@inheritdoc} */ public function getUserActivity($stream = 'me') { $apiUrl = $stream == 'me' ? 'me/feed' : 'me/home'; $response = $this->apiRequest($apiUrl); $data = new Data\Collection($response); if (!$data->exists('data')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $activities = []; foreach ($data->filter('data')->toArray() as $item) { $activities[] = $this->fetchUserActivity($item); } return $activities; } /** * @param $item * * @return User\Activity */ protected function fetchUserActivity($item) { $userActivity = new User\Activity(); $item = new Data\Collection($item); $userActivity->id = $item->get('id'); $userActivity->date = $item->get('created_time'); if ('video' == $item->get('type') || 'link' == $item->get('type')) { $userActivity->text = $item->get('link'); } if (empty($userActivity->text) && $item->exists('story')) { $userActivity->text = $item->get('link'); } if (empty($userActivity->text) && $item->exists('message')) { $userActivity->text = $item->get('message'); } if (!empty($userActivity->text) && $item->exists('from')) { $userActivity->user->identifier = $item->filter('from')->get('id'); $userActivity->user->displayName = $item->filter('from')->get('name'); $userActivity->user->profileURL = $this->getProfileUrl($userActivity->user->identifier); $userActivity->user->photoURL = $this->apiBaseUrl . $userActivity->user->identifier; $userActivity->user->photoURL .= '/picture?width=150&height=150'; } return $userActivity; } /** * Get profile URL. * * @param int $identity User ID. * @return string|null NULL when identity is not provided. */ protected function getProfileUrl($identity) { if (!is_numeric($identity)) { return null; } return sprintf($this->profileUrlTemplate, $identity); } } lib/hybridauth/Provider/GitHub.php000064400000005556147577721060013175 0ustar00apiRequest('user'); $data = new Data\Collection($response); if (!$data->exists('id')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('id'); $userProfile->displayName = $data->get('name'); $userProfile->description = $data->get('bio'); $userProfile->photoURL = $data->get('avatar_url'); $userProfile->profileURL = $data->get('html_url'); $userProfile->email = $data->get('email'); $userProfile->webSiteURL = $data->get('blog'); $userProfile->region = $data->get('location'); $userProfile->displayName = $userProfile->displayName ?: $data->get('login'); if (empty($userProfile->email) && strpos($this->scope, 'user:email') !== false) { try { // user email is not mandatory so keep it quite. $userProfile = $this->requestUserEmail($userProfile); } catch (\Exception $e) { } } return $userProfile; } /** * Request connected user email * * https://developer.github.com/v3/users/emails/ * @param User\Profile $userProfile * * @return User\Profile * * @throws \Exception */ protected function requestUserEmail(User\Profile $userProfile) { $response = $this->apiRequest('user/emails'); foreach ($response as $idx => $item) { if (!empty($item->primary) && $item->primary == 1) { $userProfile->email = $item->email; if (!empty($item->verified) && $item->verified == 1) { $userProfile->emailVerified = $userProfile->email; } break; } } return $userProfile; } } lib/hybridauth/Provider/Discord.php000064400000004561147577721060013375 0ustar00isRefreshTokenAvailable()) { $this->tokenRefreshParameters += [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, ]; } } /** * {@inheritdoc} */ public function getUserProfile() { $response = $this->apiRequest('users/@me'); $data = new Data\Collection($response); if (!$data->exists('id')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } // Makes display name more unique. $displayName = $data->get('username') ?: $data->get('login'); if ($discriminator = $data->get('discriminator')) { $displayName .= "#{$discriminator}"; } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('id'); $userProfile->displayName = $displayName; $userProfile->email = $data->get('email'); if ($data->get('verified')) { $userProfile->emailVerified = $data->get('email'); } if ($data->get('avatar')) { $userProfile->photoURL = 'https://cdn.discordapp.com/avatars/'; $userProfile->photoURL .= $data->get('id') . '/' . $data->get('avatar') . '.png'; } return $userProfile; } } lib/hotp.php000064400000012555147577721060007025 0ustar00= 0; $i--) { $cur_counter[$i] = pack ('C*', $counter); $counter = $counter >> 8; } $bin_counter = implode($cur_counter); // Pad to 8 chars if (strlen($bin_counter) < 8) { $bin_counter = str_repeat (chr(0), 8 - strlen ($bin_counter)) . $bin_counter; } // HMAC $hash = hash_hmac('sha1', $bin_counter, $key); return new \Loginizer\HOTPResult($hash); } /** * Generate a HOTP key based on a timestamp and window size * @param string $key the key to use for hashing * @param int $window the size of the window a key is valid for in seconds * @param int $timestamp a timestamp to calculate for, defaults to time() * @return HOTPResult a HOTP Result which can be truncated or output */ public static function generateByTime($key, $window, $timestamp = false) { if (!$timestamp && $timestamp !== 0) { $timestamp = \Loginizer\HOTP::getTime(); } $counter = intval($timestamp / $window); return \Loginizer\HOTP::generateByCounter($key, $counter); } /** * Generate a HOTP key collection based on a timestamp and window size * all keys that could exist between a start and end time will be included * in the returned array * @param string $key the key to use for hashing * @param int $window the size of the window a key is valid for in seconds * @param int $min the minimum window to accept before $timestamp * @param int $max the maximum window to accept after $timestamp * @param int $timestamp a timestamp to calculate for, defaults to time() * @return array of HOTPResult */ public static function generateByTimeWindow($key, $window, $min = -1, $max = 1, $timestamp = false) { if (!$timestamp && $timestamp !== 0) { $timestamp = \Loginizer\HOTP::getTime(); } $counter = intval($timestamp / $window); $window = range($min, $max); $out = array(); for ($i = 0; $i < count($window); $i++) { $shift_counter = $window[$i]; $out[$shift_counter] = \Loginizer\HOTP::generateByCounter($key, $counter + $shift_counter); } return $out; } /** * Gets the current time * Ensures we are operating in UTC for the entire framework * Restores the timezone on exit. * @return int the current time */ public static function getTime() { return time(); // PHP's time is always UTC } } /** * The HOTPResult Class converts an HOTP item to various forms * Supported formats include hex, decimal, string, and HOTP * @author Jakob Heuser (firstname)@felocity.com */ class HOTPResult { protected $hash; protected $binary; protected $decimal; /** * Build an HOTP Result * @param string $value the value to construct with */ public function __construct($value) { // store raw $this->hash = $value; } /** * Returns the string version of the HOTP * @return string */ public function toString() { return $this->hash; } /** * Returns the hex version of the HOTP * @return string */ public function toHex() { if( !$this->hex ) { $this->hex = dechex($this->toDec()); } return $this->hex; } /** * Returns the decimal version of the HOTP * @return int */ public function toDec() { if( !$this->decimal ) { // store calculate decimal $hmac_result = array(); // Convert to decimal foreach(str_split($this->hash,2) as $hex) { $hmac_result[] = hexdec($hex); } $offset = $hmac_result[19] & 0xf; $this->decimal = ( (($hmac_result[$offset+0] & 0x7f) << 24 ) | (($hmac_result[$offset+1] & 0xff) << 16 ) | (($hmac_result[$offset+2] & 0xff) << 8 ) | ($hmac_result[$offset+3] & 0xff) ); } return $this->decimal; } /** * Returns the truncated decimal form of the HOTP * @param int $length the length of the HOTP to return * @return string */ public function toHOTP($length) { $str = str_pad($this->toDec(), $length, "0", STR_PAD_LEFT); $str = substr($str, (-1 * $length)); return $str; } } lib/Base32.php000064400000010705147577721060007065 0ustar00 * @link http://christianriesen.com * @license MIT License see LICENSE file */ class Base32 { /** * Table for encoding base32 * * @var array */ private static $encode = array( 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 2, 27 => 3, 28 => 4, 29 => 5, 30 => 6, 31 => 7, 32 => '=', ); /** * Table for decoding base32 * * @var array */ private static $decode = array( 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, 'H' => 7, 'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 2 => 26, 3 => 27, 4 => 28, 5 => 29, 6 => 30, 7 => 31, '=' => 32, ); /** * Creates an array from a binary string into a given chunk size * * @param string $binaryString String to chunk * @param integer $bits Number of bits per chunk * @return array */ private static function chunk($binaryString, $bits) { $binaryString = chunk_split($binaryString, $bits, ' '); if (substr($binaryString, (strlen($binaryString)) - 1) == ' ') { $binaryString = substr($binaryString, 0, strlen($binaryString)-1); } return explode(' ', $binaryString); } /** * Encodes into base32 * * @param string $string Clear text string * @return string Base32 encoded string */ public static function encode($string) { if (strlen($string) == 0) { // Gives an empty string return ''; } // Convert string to binary $binaryString = ''; foreach (str_split($string) as $s) { // Return each character as an 8-bit binary string $s = decbin(ord($s)); $binaryString .= str_pad($s, 8, 0, STR_PAD_LEFT); } // Break into 5-bit chunks, then break that into an array $binaryArray = self::chunk($binaryString, 5); // Pad array to be divisible by 8 while (count($binaryArray) % 8 !== 0) { $binaryArray[] = null; } $base32String = ''; // Encode in base32 foreach ($binaryArray as $bin) { $char = 32; if (!is_null($bin)) { // Pad the binary strings $bin = str_pad($bin, 5, 0, STR_PAD_RIGHT); $char = bindec($bin); } // Base32 character $base32String .= self::$encode[$char]; } return $base32String; } /** * Decodes base32 * * @param string $base32String Base32 encoded string * @return string Clear text string */ public static function decode($base32String) { if (strlen($base32String) == 0) { // Gives an empty string return ''; } // Only work in upper cases $base32String = strtoupper($base32String); // Remove anything that is not base32 alphabet $pattern = '/[^A-Z2-7]/'; $replacement = ''; $base32String = preg_replace($pattern, '', $base32String); $base32Array = str_split($base32String); $binaryArray = array(); $string = ''; foreach ($base32Array as $str) { $char = self::$decode[$str]; // Ignore the padding character if ($char !== 32) { $char = decbin($char); $string .= str_pad($char, 5, 0, STR_PAD_LEFT); } } while (strlen($string) %8 !== 0) { $string = substr($string, 0, strlen($string)-1); } $binaryArray = self::chunk($string, 8); $realString = ''; foreach ($binaryArray as $bin) { // Pad each value to 8 bits $bin = str_pad($bin, 8, 0, STR_PAD_RIGHT); // Convert binary strings to ascii $realString .= chr(bindec($bin)); } return $realString; } } main/settings/social_login.php000064400000041135147577721060012527 0ustar00

'.__('Create Google APP', 'loginizer').'

'.__('Last Updated','loginizer').': 24th June 2024

'.__('To allow your users to be able to login through their Google Account, you first need to create a Google App. For that follow the App creation steps below.', 'loginizer').'

  1. '.__('Go to Google Developer Console','loginizer').' https://console.developers.google.com/apis/
  2. '.__('If you are not logged in then login, and create a project if you don\'t have that already', 'loginizer').'
  3. '.__('Once you have a project in the Console make sure you are on API and services page and then go to OAuth consent screen in the left navigation', 'loginizer').'
  4. '.__('Under the "Authorized domains" section press the "Add Domain" button and enter your domain name, without subdomains!', 'loginizer').'
  5. '.__('At the "Developer contact information" section, enter an email address that Google can use to notify you about any changes to your project.').'
  6. '.__('Press "Save and Continue" then press it again on the "Scopes", "Test users" pages, too!', 'loginizer').'
  7. '.__('After that you will need to create the credentials. For that in the same left navigation click on Credentials.', 'loginizer').'
  8. '.__('On the credentials page, click on Create Credentials and then choose OAuth client ID', 'loginizer').'
  9. '.__('A select field which is Application Type will appear, in that select web appilication and then other fields will show up', 'loginizer').'
  10. '.__('Now set it\'s name to anything you like.', 'loginizer').'
  11. '.__('After that in Authorize redirect URIs Add URI', 'loginizer').'
  12. '.__('The URI you need to add is', 'loginizer').''.esc_url(wp_login_url()).'?lz_social_provider=Google
  13. '.__('Now click on create and you will get your Client ID and secret key', 'loginizer').'
  14. '.__('Copy those keys in the required field and save settings.', 'loginizer').'
'; } function loginizer_how_to_facebook(){ // NOTE: update the date of last updated of this doc if you make any change. echo '

'.__('Create Facebook App', 'loginizer').'

'.__('Last Updated','loginizer').': 24th June 2024

'.__('To allow your users to be able to login through their facebook account, you first need to create a Facebook App. For that follow the App creation steps below.', 'loginizer').'

  1. '.__('Go to Facebook Developer Platform, make sure you are already logged-in to Facebook','loginizer').' https://developers.facebook.com/apps/
  2. '.__('If you are not logged in then login, and click on Create App button and choose "Others" as use case and click Next', 'loginizer').'
  3. '.__('After selecting Others you will be asked to select App type there select Consumer and click next button', 'loginizer').'
  4. '.__('A form will appear which will ask for your Apps name fill it and other details on the form and then Create App', 'loginizer').'
  5. '.__('Your app will be created now on next page, you will get multiple options of Products you can add to your App, there setup Facebook Login', 'loginizer').'
  6. '.__('After clicking on Setup Facebook login, you will be asked which platform you want to use it on there select Web', 'loginizer').'
  7. '.__('Now you will need to fill up some details, first you will be asked about URL of the website after adding that in the field save and click on Continue', 'loginizer').'
  8. '.__('Thats all you need to do in this form, for other steps you can just click on next.', 'loginizer').'
  9. '.__('Once you are done with the steps in the Left Navigation go to Facebook Login then Settings.', 'loginizer').'
  10. '.__('The settings releated to OAuth will open there you will need to fill the Valid OAuth Redirect URIs field with this URL.', 'loginizer').''.esc_url(wp_login_url()).'?lz_social_provider=Facebook
  11. '.__('After adding the URL save changes and now from the Left Navigation go to App Settings the Basic.', 'loginizer').'
  12. '.__('On the Baisc page of App Settings you will find App ID and App Secret copy that as we need that to setup Facebook Login through Loginizer.', 'loginizer').'
  13. '.__('Now go to your WordPress admin then Loginizer then Social Login and then Facebook Settings', 'loginizer').'
  14. '.__('In the Facebook settings page add the App ID to client ID and App Secret to App Secret', 'loginizer').'
  15. '.__('Enable Facebook and save the settings, now a test info will show in there click on the Test button to verify that the setup went as expected.', 'loginizer').'
'; } function loginizer_how_to_github(){ // NOTE: update the date of last updated of this doc if you make any change. echo '

'.__('Create Github App', 'loginizer').'

'.__('Last Updated','loginizer').': 24th June 2024

'.__('To allow your users to be able to login through their Github Account, you first need to create a Github App. For that follow the App creation steps below.', 'loginizer').'

  1. '.__('Go to Github developer settings page','loginizer').' https://github.com/settings/developers/
  2. '.__('Make sure you are already logged in, if you are not then please login', 'loginizer').'
  3. '.__('Now you will be in Developer Settings page and make sure you are on the OAuth App tab.', 'loginizer').'
  4. '.__('There you will find a button to Register a New Application, Click on that button.', 'loginizer').'
  5. '.__('A form will appear, fill that form with the required details, and in Authorization Callback URL field enter this URL', 'loginizer').''.esc_url(wp_login_url()).'?lz_social_provider=GitHub
  6. '.__('Now save the details, and your client ID will be generated', 'loginizer').'
  7. '.__('Now in the Client Secret section, look for Generate a new client secret button and click it and it will generate the secret key', 'loginizer').'
  8. '.__('You have both keys with you now, so go to WordPress admin --> Loginizer --> Social Login --> in Provider tab go to Github and enter these keys in the respective fields and save it.', 'loginizer').'
'; } function loginizer_how_to_wordpress(){ // NOTE: update the date of last updated of this doc if you make any change. echo '

'.__('Create WordPress App', 'loginizer').'

'.__('Last Updated','loginizer').': 24th June 2024

'.__('To allow your users to be able to login through their WordPress.com Account, you first need to create a WordPress.com Application. For that follow the Application creation steps below.', 'loginizer').'

  1. '.__('Go to WordPress.com developer App page.','loginizer').' https://developer.wordpress.com/apps/
  2. '.__('Make sure you are already logged in, if you are not then please login', 'loginizer').'
  3. '.__('Now you will be in WordPress.com Developer My Application page.', 'loginizer').'
  4. '.__('There you will find a link to Create new Application a New Application, Click on that link.', 'loginizer').'
  5. '.__('A form will appear, fill that form with the required details, and in Redirect URLS field enter this URL', 'loginizer').''.esc_url(wp_login_url()).'?lz_social_provider=WordPress
  6. '.__('Now click on Create button and the App will be created.', 'loginizer').'
  7. '.__('You will need to go back to the My Application page, and find the app you just created and click on it', 'loginizer').'
  8. '.__('On the app page, in the bottom you will find OAuth information, Copy the client ID and client secret.', 'loginizer').'
  9. '.__('You have both keys with you now, so go to WordPress admin --> Loginizer --> Social Login --> in Provider tab go to WordPress and enter these keys in the respective fields and save it.', 'loginizer').'
'; } function loginizer_how_to_discord(){ // NOTE: update the date of last updated of this doc if you make any change. echo '

'.__('Create Discord App', 'loginizer').'

'.__('Last Updated','loginizer').': 24th June 2024

'.__('To allow your users to be able to login through their Discord Account, you first need to create a Discord Application. For that follow the Application creation steps below.', 'loginizer').'

  1. '.__('Go to Discord developer App page.','loginizer').' https://discord.com/developers/applications
  2. '.__('Make sure you are already logged in, if you are not then please login', 'loginizer').'
  3. '.__('Now you will be in Discord Developer Portal on Applications page.', 'loginizer').'
  4. '.__('There you will find a button which says New Application on the top right, click on that button.', 'loginizer').'
  5. '.__('A poup will appear, fill the Application name and check the terms and condition checkbox, and then click create.', 'loginizer').'
  6. '.__('A form will appear with optional fields fill those as per your wish and save them.', 'loginizer').'
  7. '.__('Now in the left navigation click on OAuth, and you will find client ID and an option to generate Client Secret', 'loginizer').'
  8. '.__('And add the Redirect URL as without that Login won\'t work, the URL is ', 'loginizer').''.esc_url(wp_login_url()).'?lz_social_provider=Discord
  9. '.__('Copy both Client ID and Client Secret and go to WordPress Admin, Loginizer, Social Login, Discord and enter the copied keys in the respective field and save it.', 'loginizer').'
'; } function loginizer_how_to_twitchtv(){ // NOTE: update the date of last updated of this doc if you make any change. echo '

'.__('Create Twitch App', 'loginizer').'

'.__('Last Updated','loginizer').': 24th June 2024

'.__('To allow your users to be able to login through their Twitch Account, you first need to create a Twitch Application. For that follow the Application creation steps below.', 'loginizer').'

  1. '.__('There is a pre-requisite in Twitch that the account you are using to create the keys should have 2FA enabled on it. You can do it from this page','loginizer').' Security and Privacy
  2. '.__('After you have enabled 2FA go to Twitch developer page.','loginizer').' https://dev.twitch.tv/console/
  3. '.__('Now from the left navigation go to Applications.', 'loginizer').'
  4. '.__('On the applications page click on Register your application button.', 'loginizer').'
  5. '.__('A form will appear, fill the Application name, in Category select Website integration and client type as Confidential.', 'loginizer').'
  6. '.__('Now the last field we need to fill is OAuth Redirect URLs in there add this URL.', 'loginizer').''.esc_url(wp_login_url()).'?lz_social_provider=TwitchTV
  7. '.__('After all the fields are filled click on save and a App will be created and you will be redirected to the Application page with your App listed there, now click on the manage button of the listed app.', 'loginizer').'
  8. '.__('You will get the Client ID and Client Secret here so copy them as we need to use them in Loginizer', 'loginizer').'
  9. '.__('Now go to WordPress Admin, Loginizer, Social Login, Twitch and enter the copied keys in the respective field and save it.', 'loginizer').'
'; } function loginizer_how_to_twitter(){ // NOTE: update the date of last updated of this doc if you make any change. echo '

'.__('Create X App(Formly Twitter)', 'loginizer').'

'.__('Last Updated','loginizer').': 24th June 2024

'.__('To allow your users to be able to login through their X Account, you first need to create a X Project. For that follow the Project creation steps below.', 'loginizer').'

  1. '.__('Go to X Project page for that','loginizer').' Navigate to https://developer.twitter.com/en/portal/projects-and-apps
  2. '.__('If you don\'t have a developer account then apply for it by filling required details. It is mandatory to fill this form to get access to the developer account.', 'loginizer').'
  3. '.__('Once you have the developer account and you are on the Project and App page click on Add Project.','loginizer').'
  4. '.__('You will have to fill a form with Project name, use case, project desccription and then App name.', 'loginizer').'
  5. '.__('Once you do that it will show the API Key and API secret, copy that and come to your WordPress dashboard.', 'loginizer').'
  6. '.__('On your WordPress admin go to Loginizer, Social Login and then X(formly Twitter) there place the API key in Client ID and API Secret in Client Secret and enable it and save it.', 'loginizer').'
  7. '.__('The setup is not done yet, Loginzer will show a notice to test your Integration, but dont do it now, as few more steps are yet to be done, go back to Twitter Developer Accound and go to Project and APP from the Left Navigation then Your Project and then your APP.', 'loginizer').'
  8. '.__('On the page of your APP there will be a section named User authentication settings in that section click on Set up button.', 'loginizer').'
  9. '.__('Set up setting will appear in that set App Permission to Read and enable Request email from user.', 'loginizer').'
  10. '.__('Next in the Type of App select Web App and then in the App Info section in the Callback URI/Redirect URL set this URL', 'loginizer').''.esc_url(wp_login_url()).'?lz_social_provider=Twitter
  11. '.__('Next fill your Websites URL in Website URL field, then Twitter requires you to give it a Privacy Policy and Terms and Conditions page because we are requesting for user email. Fill those details and hit save', 'loginizer').'
  12. '.__('Now you can test Twitter Login on Loginizer so it can start showing on the Login page.', 'loginizer').'
'; }main/ajax.php000064400000006512147577721060007150 0ustar00' . "\n"; $rule .= 'RewriteEngine On' . "\n"; $rule .= 'RewriteBase ' . $home_root . "\n\n"; $rule .= 'RewriteRule ^' . $admin_slug . '(-lzs.{20})?(/?)(.*) wp-admin/$3 [L]' . "\n"; $rule .= '' . "\n"; $rule .= '# END Loginizer'; $htaccess_file = ABSPATH . '/.htaccess'; if(!file_exists($htaccess_file)){ wp_send_json_error(0); } $contents = file_get_contents($htaccess_file); if(strpos($contents, '# BEGIN Loginizer') !== FALSE){ $contents = preg_replace('/# BEGIN Loginizer.*# END Loginizer/ms', '', $contents); } if(!file_put_contents($htaccess_file, trim($rule . "\n" . $contents))){ wp_send_json_error(0); } wp_send_json(array('success' => true)); }main/admin.php000064400000005063147577721060007315 0ustar00 0 && (empty($dismissed_pro) || time() > $dismissed_pro)){ $showing_error = true; echo '

'.esc_html__('You are using an older version of Loginizer Security. We recommend updating to the latest version to ensure seamless and uninterrupted use of the application.', 'loginizer').'

'; }elseif(version_compare(LOGINIZER_VERSION, LOGINIZER_PRO_VERSION) < 0 && (empty($dismissed_free) || time() > $dismissed_free)){ $showing_error = true; echo '

'.esc_html__('You are using an older version of Loginizer. We recommend updating to the latest free version to ensure smooth and uninterrupted use of the application.', 'loginizer').'

'; } if(!empty($showing_error)){ wp_register_script('loginizer-pro-version-notice', '', array('jquery'), LOGINIZER_PRO_VERSION, true ); wp_enqueue_script('loginizer-pro-version-notice'); wp_add_inline_script('loginizer-pro-version-notice', ' function loginizer_pro_dismiss_notice(e){ e.preventDefault(); let target = jQuery(e.target); if(!target.hasClass("notice-dismiss")){ return; } let jEle = target.closest("#loginizer-pro-version-notice"), type = jEle.data("type"); jEle.slideUp(); jQuery.post("'.admin_url('admin-ajax.php').'", { security : "'.wp_create_nonce('loginizer_version_notice').'", action: "loginizer_pro_version_notice", type: type }, function(res){ if(!res["success"]){ alert(res["data"]); } }).fail(function(data){ alert("There seems to be some issue dismissing this alert"); }); }'); } }main/social-login.php000064400000011004147577721060010575 0ustar00 'icon', 'divider' => 'above', 'shape' => 'square' ], $atts); $errors = loginizer_social_login_error_handler(); if(!empty($errors) || is_wp_error($errors)){ $error = '
'; $args = [ 'type' => 'error', ]; // Add the number of retires left as well if(count($errors->get_error_codes()) > 0 && isset($loginizer['retries_left'])){ $errors->add('retries_left', loginizer_retries_left()); } $messages = $errors->get_error_messages(); $notice = ''; if(count($messages) == 1){ $notice .= '

'.wp_kses_post($messages[0]).'

'; } else { $notice .= ''; } $error .= wp_get_admin_notice($notice, $args); $error .= '
'; } if(!empty($error)){ return $error . loginizer_social_btn(true, 'login', $atts); } return loginizer_social_btn(true, 'login', $atts); } function loginizer_social_wc_error(){ // Showing woocommerce error if(!function_exists('wc_add_wp_error_notices')){ return; } $errors = loginizer_social_login_error_handler(); if(empty($errors) || !is_wp_error($errors)){ return; } wc_add_wp_error_notices($errors); loginizer_woocommerce_error_handler(); woocommerce_output_all_notices(); } function loginizer_social_update_avatar($avatar, $id_or_email, $size, $default, $alt){ global $wpdb, $blog_id; $user = false; if(empty($id_or_email)){ return $avatar; } if(is_numeric($id_or_email)){ $id = (int) $id_or_email; $user = get_user_by('id' , $id); } elseif(is_object($id_or_email)){ if(!empty($id_or_email->user_id)){ $id = (int) $id_or_email->user_id; $user = get_user_by('id' , $id); } } else { $user = get_user_by('email', $id_or_email); } if(empty($user) || !is_object($user) || empty($user->ID)){ return $avatar; } // Fetching the Image now $avatar_id = get_user_meta($user->ID, $wpdb->get_blog_prefix($blog_id) . 'lz_avatar', true); if(!wp_attachment_is_image($avatar_id)){ return $avatar; } $avatar_size = 'thumbnail'; if(!empty($size)){ $avatar_size = is_numeric($size) ? [$size, $size] : $size; } $avatar_url = wp_get_attachment_image_src($avatar_id, $avatar_size); if(empty($avatar_url) || empty($avatar_url[0])){ return $avatar; } $avatar = $avatar_url[0]; $avatar = ''.esc_attr($alt).''; return $avatar; } function loginizer_social_btn_woocommerce($return = false, $id = ''){ loginizer_social_btn($return, 'woocommerce'); } function loginizer_social_btn_comment($post_id){ loginizer_social_btn(false, 'comment'); }main/upgrader.php000064400000013170147577721060010034 0ustar00addQueryArgFilter('loginizer_updater_filter_args'); // Show the text to install the license key add_filter('puc_manual_final_check_link-loginizer-security', 'loginizer_updater_check_link', 10, 1); // Nag informing the user to install the free version. if(current_user_can('activate_plugins')){ add_action('admin_notices', 'loginizer_pro_free_version_nag', 9); add_action('admin_menu', 'loginizer_pro_add_menu', 9); } $is_network_wide = loginizer_pro_is_network_active('loginizer-security'); $_ls_version = get_option('loginizer_version'); $req_free_update = !empty($_ls_version) && version_compare($_ls_version, '1.8.9', '<'); if($is_network_wide){ $ls_free_installed = get_site_option('loginizer_free_installed'); }else{ $ls_free_installed = get_option('loginizer_free_installed'); } if(!empty($ls_free_installed)){ return; } // Include the necessary stuff include_once(ABSPATH . 'wp-admin/includes/plugin-install.php'); include_once(ABSPATH . 'wp-admin/includes/plugin.php'); include_once(ABSPATH . 'wp-admin/includes/file.php'); if( file_exists( WP_PLUGIN_DIR . '/loginizer/loginizer.php' ) && is_plugin_inactive( '/loginizer/loginizer.php' ) && empty($req_free_update) ) { if($is_network_wide){ update_site_option('loginizer_free_installed', time()); }else{ update_option('loginizer_free_installed', time()); } activate_plugin('/loginizer/loginizer.php', '', $is_network_wide); remove_action('admin_notices', 'loginizer_pro_free_version_nag', 9); remove_action('admin_menu', 'loginizer_pro_add_menu', 9); return; } // Includes necessary for Plugin_Upgrader and Plugin_Installer_Skin include_once(ABSPATH . 'wp-admin/includes/misc.php'); include_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'); // Filter to prevent the activate text add_filter('install_plugin_complete_actions', 'loginizer_pro_prevent_activation_text', 10, 3); $upgrader = new Plugin_Upgrader(new WP_Ajax_Upgrader_Skin()); // Upgrade the plugin to the latest version of free already installed. if(!empty($req_free_update) && file_exists( WP_PLUGIN_DIR . '/loginizer/loginizer.php' )){ $installed = $upgrader->upgrade('loginizer/loginizer.php'); }else{ $installed = $upgrader->install('https://downloads.wordpress.org/plugin/loginizer.zip'); } if(!is_wp_error($installed) && $installed){ if($is_network_wide){ update_site_option('loginizer_free_installed', time()); }else{ update_option('loginizer_free_installed', time()); } activate_plugin('loginizer/loginizer.php', '', $is_network_wide); remove_action('admin_notices', 'loginizer_pro_free_version_nag', 9); remove_action('admin_menu', 'loginizer_pro_add_menu', 9); } } // Do not shows the activation text if function loginizer_pro_prevent_activation_text($install_actions, $api, $plugin_file){ if($plugin_file == 'loginizer/loginizer.php'){ return array(); } return $install_actions; } function loginizer_pro_free_version_nag(){ $ls_version = get_option('loginizer_version'); $lower_version = __('You have not installed the free version of Loginizer. Loginizer Pro depends on the free version, so you must install it first in order to use Loginizer Pro.'); if(!empty($ls_version) && version_compare($ls_version, '1.8.9', '<')){ $lower_version = __('You are using an older version of the free version of Loginizer, please update Loginizer to work without any issues'); } echo '

'.esc_html($lower_version).' Install/Update Now

'; } function loginizer_pro_add_menu(){ add_menu_page('Loginizer', 'Loginizer Security', 'activate_plugins', 'loginizer-security', 'loginizer_pro_menu_page'); } function loginizer_pro_menu_page(){ echo '

Loginizer Free version is not installed / outdated!

Loginizer Pro depends on the free version of Loginizer, so you need to install / update the free version first.

Install/Update Now

'; } updater/plugin-update-checker.php000064400000152147147577721060013133 0ustar00metadataUrl = $metadataUrl; $this->pluginAbsolutePath = $pluginFile; $this->pluginFile = plugin_basename($this->pluginAbsolutePath); $this->muPluginFile = $muPluginFile; $this->slug = $slug; $this->optionName = $optionName; $this->debugMode = (bool)(constant('WP_DEBUG')); //If no slug is specified, use the name of the main plugin file as the slug. //For example, 'my-cool-plugin/cool-plugin.php' becomes 'cool-plugin'. if ( empty($this->slug) ){ $this->slug = basename($this->pluginFile, '.php'); } //Plugin slugs must be unique. $slugCheckFilter = 'puc_is_slug_in_use-' . $this->slug; $slugUsedBy = apply_filters($slugCheckFilter, false); if ( $slugUsedBy ) { $this->triggerError(sprintf( 'Plugin slug "%s" is already in use by %s. Slugs must be unique.', htmlentities($this->slug), htmlentities($slugUsedBy) ), E_USER_ERROR); } add_filter($slugCheckFilter, array($this, 'getAbsolutePath')); if ( empty($this->optionName) ){ $this->optionName = 'external_updates-' . $this->slug; } //Backwards compatibility: If the plugin is a mu-plugin but no $muPluginFile is specified, assume //it's the same as $pluginFile given that it's not in a subdirectory (WP only looks in the base dir). if ( (strpbrk($this->pluginFile, '/\\') === false) && $this->isUnknownMuPlugin() ) { $this->muPluginFile = $this->pluginFile; } $this->scheduler = $this->createScheduler($checkPeriod); $this->upgraderStatus = new Loginizer_PucUpgraderStatus_3_2(); $this->installHooks(); } /** * Create an instance of the scheduler. * * This is implemented as a method to make it possible for plugins to subclass the update checker * and substitute their own scheduler. * * @param int $checkPeriod * @return Loginizer_PucScheduler_3_2 */ protected function createScheduler($checkPeriod) { return new Loginizer_PucScheduler_3_2($this, $checkPeriod); } /** * Install the hooks required to run periodic update checks and inject update info * into WP data structures. * * @return void */ protected function installHooks(){ //Override requests for plugin information add_filter('plugins_api', array($this, 'injectInfo'), 20, 3); //Insert our update info into the update array maintained by WP. add_filter('site_transient_update_plugins', array($this,'injectUpdate')); //WP 3.0+ add_filter('transient_update_plugins', array($this,'injectUpdate')); //WP 2.8+ add_filter('site_transient_update_plugins', array($this, 'injectTranslationUpdates')); add_filter('plugin_row_meta', array($this, 'addCheckForUpdatesLink'), 10, 2); add_action('admin_init', array($this, 'handleManualCheck')); add_action('all_admin_notices', array($this, 'displayManualCheckResult')); //Clear the version number cache when something - anything - is upgraded or WP clears the update cache. add_filter('upgrader_post_install', array($this, 'clearCachedVersion')); add_action('delete_site_transient_update_plugins', array($this, 'clearCachedVersion')); //Clear translation updates when WP clears the update cache. //This needs to be done directly because the library doesn't actually remove obsolete plugin updates, //it just hides them (see getUpdate()). We can't do that with translations - too much disk I/O. add_action('delete_site_transient_update_plugins', array($this, 'clearCachedTranslationUpdates')); if ( did_action('plugins_loaded') ) { $this->initDebugBarPanel(); } else { add_action('plugins_loaded', array($this, 'initDebugBarPanel')); } //Rename the update directory to be the same as the existing directory. add_filter('upgrader_source_selection', array($this, 'fixDirectoryName'), 10, 3); //Enable language support (i18n). load_plugin_textdomain('plugin-update-checker', false, plugin_basename(dirname(__FILE__)) . '/languages'); //Allow HTTP requests to the metadata URL even if it's on a local host. $this->metadataHost = @parse_url($this->metadataUrl, PHP_URL_HOST); add_filter('http_request_host_is_external', array($this, 'allowMetadataHost'), 10, 2); } /** * Explicitly allow HTTP requests to the metadata URL. * * WordPress has a security feature where the HTTP API will reject all requests that are sent to * another site hosted on the same server as the current site (IP match), a local host, or a local * IP, unless the host exactly matches the current site. * * This feature is opt-in (at least in WP 4.4). Apparently some people enable it. * * That can be a problem when you're developing your plugin and you decide to host the update information * on the same server as your test site. Update requests will mysteriously fail. * * We fix that by adding an exception for the metadata host. * * @param bool $allow * @param string $host * @return bool */ public function allowMetadataHost($allow, $host) { if ( strtolower($host) === strtolower($this->metadataHost) ) { return true; } return $allow; } /** * Retrieve plugin info from the configured API endpoint. * * @uses wp_remote_get() * * @param array $queryArgs Additional query arguments to append to the request. Optional. * @return LoginizerInfo_3_2 */ public function requestInfo($queryArgs = array()){ //Query args to append to the URL. Plugins can add their own by using a filter callback (see addQueryArgFilter()). $installedVersion = $this->getInstalledVersion(); $queryArgs['installed_version'] = ($installedVersion !== null) ? $installedVersion : ''; $queryArgs = apply_filters('puc_request_info_query_args-'.$this->slug, $queryArgs); //Various options for the wp_remote_get() call. Plugins can filter these, too. $options = array( 'timeout' => 10, //seconds 'headers' => array( 'Accept' => 'application/json' ), ); $options = apply_filters('puc_request_info_options-'.$this->slug, $options); //The plugin info should be at 'http://your-api.com/url/here/$slug/info.json' $url = $this->metadataUrl; if ( !empty($queryArgs) ){ $url = add_query_arg($queryArgs, $url); } $result = wp_remote_get( $url, $options ); //Try to parse the response $status = $this->validateApiResponse($result); $pluginInfo = null; if ( !is_wp_error($status) ){ $pluginInfo = LoginizerInfo_3_2::fromJson($result['body']); if ( $pluginInfo !== null ) { $pluginInfo->filename = $this->pluginFile; $pluginInfo->slug = $this->slug; } } else { $this->triggerError( sprintf('The URL %s does not point to a valid plugin metadata file. ', $url) . $status->get_error_message(), E_USER_WARNING ); } $pluginInfo = apply_filters('puc_request_info_result-'.$this->slug, $pluginInfo, $result); return $pluginInfo; } /** * Check if $result is a successful update API response. * * @param array|WP_Error $result * @return true|WP_Error */ private function validateApiResponse($result) { if ( is_wp_error($result) ) { /** @var WP_Error $result */ return new WP_Error($result->get_error_code(), 'WP HTTP Error: ' . $result->get_error_message()); } if ( !isset($result['response']['code']) ) { return new WP_Error('puc_no_response_code', 'wp_remote_get() returned an unexpected result.'); } if ( $result['response']['code'] !== 200 ) { return new WP_Error( 'puc_unexpected_response_code', 'HTTP response code is ' . $result['response']['code'] . ' (expected: 200)' ); } if ( empty($result['body']) ) { return new WP_Error('puc_empty_response', 'The metadata file appears to be empty.'); } return true; } /** * Retrieve the latest update (if any) from the configured API endpoint. * * @uses LoginizerUpdateChecker::requestInfo() * * @return LoginizerUpdate_3_2 An instance of LoginizerUpdate, or NULL when no updates are available. */ public function requestUpdate(){ //For the sake of simplicity, this function just calls requestInfo() //and transforms the result accordingly. $pluginInfo = $this->requestInfo(array('checking_for_updates' => '1')); if ( $pluginInfo == null ){ return null; } $update = LoginizerUpdate_3_2::fromLoginizerInfo($pluginInfo); //Keep only those translation updates that apply to this site. $update->translations = $this->filterApplicableTranslations($update->translations); return $update; } /** * Filter a list of translation updates and return a new list that contains only updates * that apply to the current site. * * @param array $translations * @return array */ private function filterApplicableTranslations($translations) { $languages = array_flip(array_values(get_available_languages())); $installedTranslations = wp_get_installed_translations('plugins'); if ( isset($installedTranslations[$this->slug]) ) { $installedTranslations = $installedTranslations[$this->slug]; } else { $installedTranslations = array(); } $applicableTranslations = array(); foreach($translations as $translation) { //Does it match one of the available core languages? $isApplicable = array_key_exists($translation->language, $languages); //Is it more recent than an already-installed translation? if ( isset($installedTranslations[$translation->language]) ) { $updateTimestamp = strtotime($translation->updated); $installedTimestamp = strtotime($installedTranslations[$translation->language]['PO-Revision-Date']); $isApplicable = $updateTimestamp > $installedTimestamp; } if ( $isApplicable ) { $applicableTranslations[] = $translation; } } return $applicableTranslations; } /** * Get the currently installed version of the plugin. * * @return string Version number. */ public function getInstalledVersion(){ if ( isset($this->cachedInstalledVersion) ) { return $this->cachedInstalledVersion; } $pluginHeader = $this->getPluginHeader(); if ( isset($pluginHeader['Version']) ) { $this->cachedInstalledVersion = $pluginHeader['Version']; return $pluginHeader['Version']; } else { //This can happen if the filename points to something that is not a plugin. $this->triggerError( sprintf( "Can't to read the Version header for '%s'. The filename is incorrect or is not a plugin.", $this->pluginFile ), E_USER_WARNING ); return null; } } /** * Get plugin's metadata from its file header. * * @return array */ protected function getPluginHeader() { if ( !is_file($this->pluginAbsolutePath) ) { //This can happen if the plugin filename is wrong. $this->triggerError( sprintf( "Can't to read the plugin header for '%s'. The file does not exist.", $this->pluginFile ), E_USER_WARNING ); return array(); } if ( !function_exists('get_plugin_data') ){ /** @noinspection PhpIncludeInspection */ require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); } return get_plugin_data($this->pluginAbsolutePath, false, false); } /** * Check for plugin updates. * The results are stored in the DB option specified in $optionName. * * @return LoginizerUpdate_3_2|null */ public function checkForUpdates(){ $installedVersion = $this->getInstalledVersion(); //Fail silently if we can't find the plugin or read its header. if ( $installedVersion === null ) { $this->triggerError( sprintf('Skipping update check for %s - installed version unknown.', $this->pluginFile), E_USER_WARNING ); return null; } $state = $this->getUpdateState(); if ( empty($state) ){ $state = new stdClass; $state->lastCheck = 0; $state->checkedVersion = ''; $state->update = null; } $state->lastCheck = time(); $state->checkedVersion = $installedVersion; $this->setUpdateState($state); //Save before checking in case something goes wrong $state->update = $this->requestUpdate(); $this->setUpdateState($state); return $this->getUpdate(); } /** * Load the update checker state from the DB. * * @return stdClass|null */ public function getUpdateState() { $state = get_site_option($this->optionName, null); if ( empty($state) || !is_object($state)) { $state = null; } if ( isset($state, $state->update) && is_object($state->update) ) { $state->update = LoginizerUpdate_3_2::fromObject($state->update); } return $state; } /** * Persist the update checker state to the DB. * * @param StdClass $state * @return void */ private function setUpdateState($state) { if ( isset($state->update) && is_object($state->update) && method_exists($state->update, 'toStdClass') ) { $update = $state->update; /** @var LoginizerUpdate_3_2 $update */ $state->update = $update->toStdClass(); } update_site_option($this->optionName, $state); } /** * Reset update checker state - i.e. last check time, cached update data and so on. * * Call this when your plugin is being uninstalled, or if you want to * clear the update cache. */ public function resetUpdateState() { delete_site_option($this->optionName); } /** * Intercept plugins_api() calls that request information about our plugin and * use the configured API endpoint to satisfy them. * * @see plugins_api() * * @param mixed $result * @param string $action * @param array|object $args * @return mixed */ public function injectInfo($result, $action = null, $args = null){ $relevant = ($action == 'plugin_information') && isset($args->slug) && ( ($args->slug == $this->slug) || ($args->slug == dirname($this->pluginFile)) ); if ( !$relevant ) { return $result; } $pluginInfo = $this->requestInfo(); $pluginInfo = apply_filters('puc_pre_inject_info-' . $this->slug, $pluginInfo); if ( $pluginInfo ) { return $pluginInfo->toWpFormat(); } return $result; } /** * Insert the latest update (if any) into the update list maintained by WP. * * @param StdClass $updates Update list. * @return StdClass Modified update list. */ public function injectUpdate($updates){ //Is there an update to insert? $update = $this->getUpdate(); //No update notifications for mu-plugins unless explicitly enabled. The MU plugin file //is usually different from the main plugin file so the update wouldn't show up properly anyway. if ( $this->isUnknownMuPlugin() ) { $update = null; } if ( !empty($update) ) { //Let plugins filter the update info before it's passed on to WordPress. $update = apply_filters('puc_pre_inject_update-' . $this->slug, $update); $updates = $this->addUpdateToList($updates, $update); } else { //Clean up any stale update info. $updates = $this->removeUpdateFromList($updates); } return $updates; } /** * @param StdClass|null $updates * @param LoginizerUpdate_3_2 $updateToAdd * @return StdClass */ private function addUpdateToList($updates, $updateToAdd) { if ( !is_object($updates) ) { $updates = new stdClass(); $updates->response = array(); } $wpUpdate = $updateToAdd->toWpFormat(); $pluginFile = $this->pluginFile; if ( $this->isMuPlugin() ) { //WP does not support automatic update installation for mu-plugins, but we can still display a notice. $wpUpdate->package = null; $pluginFile = $this->muPluginFile; } $updates->response[$pluginFile] = $wpUpdate; return $updates; } /** * @param stdClass|null $updates * @return stdClass|null */ private function removeUpdateFromList($updates) { if ( isset($updates, $updates->response) ) { unset($updates->response[$this->pluginFile]); if ( !empty($this->muPluginFile) ) { unset($updates->response[$this->muPluginFile]); } } return $updates; } /** * Insert translation updates into the list maintained by WordPress. * * @param stdClass $updates * @return stdClass */ public function injectTranslationUpdates($updates) { $translationUpdates = $this->getTranslationUpdates(); if ( empty($translationUpdates) ) { return $updates; } //Being defensive. if ( !is_object($updates) ) { $updates = new stdClass(); } if ( !isset($updates->translations) ) { $updates->translations = array(); } //In case there's a name collision with a plugin hosted on wordpress.org, //remove any preexisting updates that match our plugin. $translationType = 'plugin'; $filteredTranslations = array(); foreach($updates->translations as $translation) { if ( ($translation['type'] === $translationType) && ($translation['slug'] === $this->slug) ) { continue; } $filteredTranslations[] = $translation; } $updates->translations = $filteredTranslations; //Add our updates to the list. foreach($translationUpdates as $update) { $convertedUpdate = array_merge( array( 'type' => $translationType, 'slug' => $this->slug, 'autoupdate' => 0, //AFAICT, WordPress doesn't actually use the "version" field for anything. //But lets make sure it's there, just in case. 'version' => isset($update->version) ? $update->version : ('1.' . strtotime($update->updated)), ), (array)$update ); $updates->translations[] = $convertedUpdate; } return $updates; } /** * Rename the update directory to match the existing plugin directory. * * When WordPress installs a plugin or theme update, it assumes that the ZIP file will contain * exactly one directory, and that the directory name will be the same as the directory where * the plugin/theme is currently installed. * * GitHub and other repositories provide ZIP downloads, but they often use directory names like * "project-branch" or "project-tag-hash". We need to change the name to the actual plugin folder. * * This is a hook callback. Don't call it from a plugin. * * @param string $source The directory to copy to /wp-content/plugins. Usually a subdirectory of $remoteSource. * @param string $remoteSource WordPress has extracted the update to this directory. * @param WP_Upgrader $upgrader * @return string|WP_Error */ public function fixDirectoryName($source, $remoteSource, $upgrader) { global $wp_filesystem; /** @var WP_Filesystem_Base $wp_filesystem */ //Basic sanity checks. if ( !isset($source, $remoteSource, $upgrader, $upgrader->skin, $wp_filesystem) ) { return $source; } //If WordPress is upgrading anything other than our plugin, leave the directory name unchanged. if ( !$this->isPluginBeingUpgraded($upgrader) ) { return $source; } //Rename the source to match the existing plugin directory. $pluginDirectoryName = dirname($this->pluginFile); if ( $pluginDirectoryName === '.' ) { return $source; } $correctedSource = trailingslashit($remoteSource) . $pluginDirectoryName . '/'; if ( $source !== $correctedSource ) { //The update archive should contain a single directory that contains the rest of plugin files. Otherwise, //WordPress will try to copy the entire working directory ($source == $remoteSource). We can't rename //$remoteSource because that would break WordPress code that cleans up temporary files after update. if ( $this->isBadDirectoryStructure($remoteSource) ) { return new WP_Error( 'puc-incorrect-directory-structure', sprintf( 'The directory structure of the update is incorrect. All plugin files should be inside ' . 'a directory named %s, not at the root of the ZIP file.', htmlentities($this->slug) ) ); } /** @var WP_Upgrader_Skin $upgrader->skin */ $upgrader->skin->feedback(sprintf( 'Renaming %s to %s…', '' . basename($source) . '', '' . $pluginDirectoryName . '' )); if ( $wp_filesystem->move($source, $correctedSource, true) ) { $upgrader->skin->feedback('Plugin directory successfully renamed.'); return $correctedSource; } else { return new WP_Error( 'puc-rename-failed', 'Unable to rename the update to match the existing plugin directory.' ); } } return $source; } /** * Check for incorrect update directory structure. An update must contain a single directory, * all other files should be inside that directory. * * @param string $remoteSource Directory path. * @return bool */ private function isBadDirectoryStructure($remoteSource) { global $wp_filesystem; /** @var WP_Filesystem_Base $wp_filesystem */ $sourceFiles = $wp_filesystem->dirlist($remoteSource); if ( is_array($sourceFiles) ) { $sourceFiles = array_keys($sourceFiles); $firstFilePath = trailingslashit($remoteSource) . $sourceFiles[0]; return (count($sourceFiles) > 1) || (!$wp_filesystem->is_dir($firstFilePath)); } //Assume it's fine. return false; } /** * Is there and update being installed RIGHT NOW, for this specific plugin? * * @param WP_Upgrader|null $upgrader The upgrader that's performing the current update. * @return bool */ public function isPluginBeingUpgraded($upgrader = null) { return $this->upgraderStatus->isPluginBeingUpgraded($this->pluginFile, $upgrader); } /** * Get the details of the currently available update, if any. * * If no updates are available, or if the last known update version is below or equal * to the currently installed version, this method will return NULL. * * Uses cached update data. To retrieve update information straight from * the metadata URL, call requestUpdate() instead. * * @return LoginizerUpdate_3_2|null */ public function getUpdate() { $state = $this->getUpdateState(); /** @var StdClass $state */ //Is there an update available? if ( isset($state, $state->update) ) { $update = $state->update; //Check if the update is actually newer than the currently installed version. $installedVersion = $this->getInstalledVersion(); if ( ($installedVersion !== null) && version_compare($update->version, $installedVersion, '>') ){ $update->filename = $this->pluginFile; return $update; } } return null; } /** * Get a list of available translation updates. * * This method will return an empty array if there are no updates. * Uses cached update data. * * @return array */ public function getTranslationUpdates() { $state = $this->getUpdateState(); if ( isset($state, $state->update, $state->update->translations) ) { return $state->update->translations; } return array(); } /** * Remove all cached translation updates. * * @see wp_clean_update_cache */ public function clearCachedTranslationUpdates() { $state = $this->getUpdateState(); if ( isset($state, $state->update, $state->update->translations) ) { $state->update->translations = array(); $this->setUpdateState($state); } } /** * Add a "Check for updates" link to the plugin row in the "Plugins" page. By default, * the new link will appear after the "Visit plugin site" link. * * You can change the link text by using the "puc_manual_check_link-$slug" filter. * Returning an empty string from the filter will disable the link. * * @param array $pluginMeta Array of meta links. * @param string $pluginFile * @return array */ public function addCheckForUpdatesLink($pluginMeta, $pluginFile) { $isRelevant = ($pluginFile == $this->pluginFile) || (!empty($this->muPluginFile) && $pluginFile == $this->muPluginFile); if ( $isRelevant && current_user_can('update_plugins') ) { $linkUrl = wp_nonce_url( add_query_arg( array( 'puc_check_for_updates' => 1, 'puc_slug' => $this->slug, ), self_admin_url('plugins.php') ), 'puc_check_for_updates' ); $linkText = apply_filters('puc_manual_check_link-' . $this->slug, __('Check for updates', 'plugin-update-checker')); if ( !empty($linkText) ) { $final_link = sprintf('%s', esc_attr($linkUrl), $linkText); $pluginMeta[] = apply_filters('puc_manual_final_check_link-' . $this->slug, $final_link); } } return $pluginMeta; } /** * Check for updates when the user clicks the "Check for updates" link. * @see self::addCheckForUpdatesLink() * * @return void */ public function handleManualCheck() { $shouldCheck = isset($_GET['puc_check_for_updates'], $_GET['puc_slug']) && $_GET['puc_slug'] == $this->slug && current_user_can('update_plugins') && check_admin_referer('puc_check_for_updates'); if ( $shouldCheck ) { $update = $this->checkForUpdates(); $status = ($update === null) ? 'no_update' : 'update_available'; wp_redirect(add_query_arg( array( 'puc_update_check_result' => $status, 'puc_slug' => $this->slug, ), self_admin_url('plugins.php') )); } } /** * Display the results of a manual update check. * @see self::handleManualCheck() * * You can change the result message by using the "puc_manual_check_message-$slug" filter. */ public function displayManualCheckResult() { if ( isset($_GET['puc_update_check_result'], $_GET['puc_slug']) && ($_GET['puc_slug'] == $this->slug) ) { $status = strval($_GET['puc_update_check_result']); if ( $status == 'no_update' ) { $message = __('This plugin is up to date.', 'plugin-update-checker'); } else if ( $status == 'update_available' ) { $message = __('A new version of this plugin is available.', 'plugin-update-checker'); } else { $message = sprintf(__('Unknown update checker status "%s"', 'plugin-update-checker'), htmlentities($status)); } printf( '

%s

', apply_filters('puc_manual_check_message-' . $this->slug, $message, $status) ); } } /** * Check if the plugin file is inside the mu-plugins directory. * * @return bool */ protected function isMuPlugin() { static $cachedResult = null; if ( $cachedResult === null ) { if ( !defined('WPMU_PLUGIN_DIR') || !is_string(WPMU_PLUGIN_DIR) ) { $cachedResult = false; return $cachedResult; } //Convert both paths to the canonical form before comparison. $muPluginDir = realpath(WPMU_PLUGIN_DIR); $pluginPath = realpath($this->pluginAbsolutePath); if ( empty($muPluginDir) ) { $cachedResult = false; return $cachedResult; } $cachedResult = (strpos($pluginPath, $muPluginDir) === 0); } return $cachedResult; } /** * MU plugins are partially supported, but only when we know which file in mu-plugins * corresponds to this plugin. * * @return bool */ protected function isUnknownMuPlugin() { return empty($this->muPluginFile) && $this->isMuPlugin(); } /** * Clear the cached plugin version. This method can be set up as a filter (hook) and will * return the filter argument unmodified. * * @param mixed $filterArgument * @return mixed */ public function clearCachedVersion($filterArgument = null) { $this->cachedInstalledVersion = null; return $filterArgument; } /** * Get absolute path to the main plugin file. * * @return string */ public function getAbsolutePath() { return $this->pluginAbsolutePath; } /** * Register a callback for filtering query arguments. * * The callback function should take one argument - an associative array of query arguments. * It should return a modified array of query arguments. * * @uses add_filter() This method is a convenience wrapper for add_filter(). * * @param callable $callback * @return void */ public function addQueryArgFilter($callback){ add_filter('puc_request_info_query_args-'.$this->slug, $callback); } /** * Register a callback for filtering arguments passed to wp_remote_get(). * * The callback function should take one argument - an associative array of arguments - * and return a modified array or arguments. See the WP documentation on wp_remote_get() * for details on what arguments are available and how they work. * * @uses add_filter() This method is a convenience wrapper for add_filter(). * * @param callable $callback * @return void */ public function addHttpRequestArgFilter($callback){ add_filter('puc_request_info_options-'.$this->slug, $callback); } /** * Register a callback for filtering the plugin info retrieved from the external API. * * The callback function should take two arguments. If the plugin info was retrieved * successfully, the first argument passed will be an instance of LoginizerInfo. Otherwise, * it will be NULL. The second argument will be the corresponding return value of * wp_remote_get (see WP docs for details). * * The callback function should return a new or modified instance of LoginizerInfo or NULL. * * @uses add_filter() This method is a convenience wrapper for add_filter(). * * @param callable $callback * @return void */ public function addResultFilter($callback){ add_filter('puc_request_info_result-'.$this->slug, $callback, 10, 2); } /** * Register a callback for one of the update checker filters. * * Identical to add_filter(), except it automatically adds the "puc_" prefix * and the "-$plugin_slug" suffix to the filter name. For example, "request_info_result" * becomes "puc_request_info_result-your_plugin_slug". * * @param string $tag * @param callable $callback * @param int $priority * @param int $acceptedArgs */ public function addFilter($tag, $callback, $priority = 10, $acceptedArgs = 1) { add_filter('puc_' . $tag . '-' . $this->slug, $callback, $priority, $acceptedArgs); } /** * Initialize the update checker Debug Bar plugin/add-on thingy. */ public function initDebugBarPanel() { $debugBarPlugin = dirname(__FILE__) . '/debug-bar-plugin.php'; if ( class_exists('Debug_Bar', false) && file_exists($debugBarPlugin) ) { /** @noinspection PhpIncludeInspection */ require_once $debugBarPlugin; $this->debugBarPlugin = new Loginizer_PucDebugBarPlugin_3_2($this); } } /** * Trigger a PHP error, but only when $debugMode is enabled. * * @param string $message * @param int $errorType */ protected function triggerError($message, $errorType) { if ( $this->debugMode ) { trigger_error($message, $errorType); } } } endif; if ( !class_exists('LoginizerInfo_3_2', false) ): /** * A container class for holding and transforming various plugin metadata. * * @author Janis Elsts * @copyright 2016 * @version 3.2 * @access public */ #[\AllowDynamicProperties] class LoginizerInfo_3_2 { //Most fields map directly to the contents of the plugin's info.json file. //See the relevant docs for a description of their meaning. public $name; public $slug; public $version; public $homepage; public $sections = array(); public $banners; public $translations = array(); public $download_url; public $author; public $author_homepage; public $requires; public $tested; public $upgrade_notice; public $rating; public $num_ratings; public $downloaded; public $active_installs; public $last_updated; public $id = 0; //The native WP.org API returns numeric plugin IDs, but they're not used for anything. public $filename; //Plugin filename relative to the plugins directory. /** * Create a new instance of LoginizerInfo from JSON-encoded plugin info * returned by an external update API. * * @param string $json Valid JSON string representing plugin info. * @return LoginizerInfo_3_2|null New instance of LoginizerInfo, or NULL on error. */ public static function fromJson($json){ /** @var StdClass $apiResponse */ $apiResponse = json_decode($json); if ( empty($apiResponse) || !is_object($apiResponse) ){ trigger_error( "Failed to parse plugin metadata. Try validating your .json file with http://jsonlint.com/", E_USER_NOTICE ); return null; } $valid = self::validateMetadata($apiResponse); if ( is_wp_error($valid) ){ trigger_error($valid->get_error_message(), E_USER_NOTICE); return null; } $info = new self(); foreach(get_object_vars($apiResponse) as $key => $value){ $info->$key = $value; } //json_decode decodes assoc. arrays as objects. We want it as an array. $info->sections = (array)$info->sections; return $info; } /** * Very, very basic validation. * * @param StdClass $apiResponse * @return bool|WP_Error */ protected static function validateMetadata($apiResponse) { if ( !isset($apiResponse->name, $apiResponse->version) || empty($apiResponse->name) || empty($apiResponse->version) ) { return new WP_Error( 'puc-invalid-metadata', "The plugin metadata file does not contain the required 'name' and/or 'version' keys." ); } return true; } /** * Transform plugin info into the format used by the native WordPress.org API * * @return object */ public function toWpFormat(){ $info = new stdClass; //The custom update API is built so that many fields have the same name and format //as those returned by the native WordPress.org API. These can be assigned directly. $sameFormat = array( 'name', 'slug', 'version', 'requires', 'tested', 'rating', 'upgrade_notice', 'num_ratings', 'downloaded', 'active_installs', 'homepage', 'last_updated', ); foreach($sameFormat as $field){ if ( isset($this->$field) ) { $info->$field = $this->$field; } else { $info->$field = null; } } //Other fields need to be renamed and/or transformed. $info->download_link = $this->download_url; $info->author = $this->getFormattedAuthor(); $info->sections = array_merge(array('description' => ''), $this->sections); if ( !empty($this->banners) ) { //WP expects an array with two keys: "high" and "low". Both are optional. //Docs: https://wordpress.org/plugins/about/faq/#banners $info->banners = is_object($this->banners) ? get_object_vars($this->banners) : $this->banners; $info->banners = array_intersect_key($info->banners, array('high' => true, 'low' => true)); } return $info; } protected function getFormattedAuthor() { if ( !empty($this->author_homepage) ){ return sprintf('%s', $this->author_homepage, $this->author); } return $this->author; } } endif; if ( !class_exists('LoginizerUpdate_3_2', false) ): /** * A simple container class for holding information about an available update. * * @author Janis Elsts * @copyright 2016 * @version 3.2 * @access public */ #[\AllowDynamicProperties] class LoginizerUpdate_3_2 { public $id = 0; public $slug; public $version; public $homepage; public $download_url; public $upgrade_notice; public $tested; public $translations = array(); public $filename; //Plugin filename relative to the plugins directory. private static $fields = array( 'id', 'slug', 'version', 'homepage', 'tested', 'download_url', 'upgrade_notice', 'filename', 'translations', 'icons', 'banners' ); /** * Create a new instance of LoginizerUpdate from its JSON-encoded representation. * * @param string $json * @return LoginizerUpdate_3_2|null */ public static function fromJson($json){ //Since update-related information is simply a subset of the full plugin info, //we can parse the update JSON as if it was a plugin info string, then copy over //the parts that we care about. $pluginInfo = LoginizerInfo_3_2::fromJson($json); if ( $pluginInfo != null ) { return self::fromLoginizerInfo($pluginInfo); } else { return null; } } /** * Create a new instance of LoginizerUpdate based on an instance of LoginizerInfo. * Basically, this just copies a subset of fields from one object to another. * * @param LoginizerInfo_3_2 $info * @return LoginizerUpdate_3_2 */ public static function fromLoginizerInfo($info){ return self::fromObject($info); } /** * Create a new instance of LoginizerUpdate by copying the necessary fields from * another object. * * @param StdClass|LoginizerInfo_3_2|LoginizerUpdate_3_2 $object The source object. * @return LoginizerUpdate_3_2 The new copy. */ public static function fromObject($object) { $update = new self(); $fields = self::$fields; if ( !empty($object->slug) ) { $fields = apply_filters('puc_retain_fields-' . $object->slug, $fields); } foreach($fields as $field){ if (property_exists($object, $field)) { if(in_array($field, array('icons', 'banners'))){ $update->$field = (array) $object->$field; }else{ $update->$field = $object->$field; } } } return $update; } /** * Create an instance of StdClass that can later be converted back to * a LoginizerUpdate. Useful for serialization and caching, as it avoids * the "incomplete object" problem if the cached value is loaded before * this class. * * @return StdClass */ public function toStdClass() { $object = new stdClass(); $fields = self::$fields; if ( !empty($this->slug) ) { $fields = apply_filters('puc_retain_fields-' . $this->slug, $fields); } foreach($fields as $field){ if (property_exists($this, $field)) { $object->$field = $this->$field; } } return $object; } /** * Transform the update into the format used by WordPress native plugin API. * * @return object */ public function toWpFormat(){ $update = new stdClass; $update->id = $this->id; $update->slug = $this->slug; $update->new_version = $this->version; $update->url = $this->homepage; $update->package = $this->download_url; $update->tested = $this->tested; $update->plugin = $this->filename; $update->icons = $this->icons; $update->banners = $this->banners; if ( !empty($this->upgrade_notice) ){ $update->upgrade_notice = $this->upgrade_notice; } return $update; } } endif; if ( !class_exists('Loginizer_PucScheduler_3_2', false) ): /** * The scheduler decides when and how often to check for updates. * It calls @see LoginizerUpdateChecker::checkForUpdates() to perform the actual checks. * * @version 3.2 */ class Loginizer_PucScheduler_3_2 { public $checkPeriod = 12; //How often to check for updates (in hours). public $throttleRedundantChecks = false; //Check less often if we already know that an update is available. public $throttledCheckPeriod = 72; /** * @var LoginizerUpdateChecker_3_2 */ protected $updateChecker; private $cronHook = null; /** * Scheduler constructor. * * @param LoginizerUpdateChecker_3_2 $updateChecker * @param int $checkPeriod How often to check for updates (in hours). */ public function __construct($updateChecker, $checkPeriod) { $this->updateChecker = $updateChecker; $this->checkPeriod = $checkPeriod; //Set up the periodic update checks $this->cronHook = 'check_plugin_updates-' . $this->updateChecker->slug; if ( $this->checkPeriod > 0 ){ //Trigger the check via Cron. //Try to use one of the default schedules if possible as it's less likely to conflict //with other plugins and their custom schedules. $defaultSchedules = array( 1 => 'hourly', 12 => 'twicedaily', 24 => 'daily', ); if ( array_key_exists($this->checkPeriod, $defaultSchedules) ) { $scheduleName = $defaultSchedules[$this->checkPeriod]; } else { //Use a custom cron schedule. $scheduleName = 'every' . $this->checkPeriod . 'hours'; add_filter('cron_schedules', array($this, '_addCustomSchedule')); } if ( !wp_next_scheduled($this->cronHook) && !defined('WP_INSTALLING') ) { wp_schedule_event(time(), $scheduleName, $this->cronHook); } add_action($this->cronHook, array($this, 'maybeCheckForUpdates')); register_deactivation_hook($this->updateChecker->pluginFile, array($this, '_removeUpdaterCron')); //In case Cron is disabled or unreliable, we also manually trigger //the periodic checks while the user is browsing the Dashboard. add_action( 'admin_init', array($this, 'maybeCheckForUpdates') ); //Like WordPress itself, we check more often on certain pages. /** @see wp_update_plugins */ add_action('load-update-core.php', array($this, 'maybeCheckForUpdates')); add_action('load-plugins.php', array($this, 'maybeCheckForUpdates')); add_action('load-update.php', array($this, 'maybeCheckForUpdates')); //This hook fires after a bulk update is complete. add_action('upgrader_process_complete', array($this, 'maybeCheckForUpdates'), 11, 0); } else { //Periodic checks are disabled. wp_clear_scheduled_hook($this->cronHook); } } /** * Check for updates if the configured check interval has already elapsed. * Will use a shorter check interval on certain admin pages like "Dashboard -> Updates" or when doing cron. * * You can override the default behaviour by using the "puc_check_now-$slug" filter. * The filter callback will be passed three parameters: * - Current decision. TRUE = check updates now, FALSE = don't check now. * - Last check time as a Unix timestamp. * - Configured check period in hours. * Return TRUE to check for updates immediately, or FALSE to cancel. * * This method is declared public because it's a hook callback. Calling it directly is not recommended. */ public function maybeCheckForUpdates(){ if ( empty($this->checkPeriod) ){ return; } $state = $this->updateChecker->getUpdateState(); $shouldCheck = empty($state) || !isset($state->lastCheck) || ( (time() - $state->lastCheck) >= $this->getEffectiveCheckPeriod() ); //Let plugin authors substitute their own algorithm. $shouldCheck = apply_filters( 'puc_check_now-' . $this->updateChecker->slug, $shouldCheck, (!empty($state) && isset($state->lastCheck)) ? $state->lastCheck : 0, $this->checkPeriod ); if ( $shouldCheck ) { $this->updateChecker->checkForUpdates(); } } /** * Calculate the actual check period based on the current status and environment. * * @return int Check period in seconds. */ protected function getEffectiveCheckPeriod() { $currentFilter = current_filter(); if ( in_array($currentFilter, array('load-update-core.php', 'upgrader_process_complete')) ) { //Check more often when the user visits "Dashboard -> Updates" or does a bulk update. $period = 60; } else if ( in_array($currentFilter, array('load-plugins.php', 'load-update.php')) ) { //Also check more often on the "Plugins" page and /wp-admin/update.php. $period = 3600; } else if ( $this->throttleRedundantChecks && ($this->updateChecker->getUpdate() !== null) ) { //Check less frequently if it's already known that an update is available. $period = $this->throttledCheckPeriod * 3600; } else if ( defined('DOING_CRON') && constant('DOING_CRON') ) { //WordPress cron schedules are not exact, so lets do an update check even //if slightly less than $checkPeriod hours have elapsed since the last check. $cronFuzziness = 20 * 60; $period = $this->checkPeriod * 3600 - $cronFuzziness; } else { $period = $this->checkPeriod * 3600; } return $period; } /** * Add our custom schedule to the array of Cron schedules used by WP. * * @param array $schedules * @return array */ public function _addCustomSchedule($schedules){ if ( $this->checkPeriod && ($this->checkPeriod > 0) ){ $scheduleName = 'every' . $this->checkPeriod . 'hours'; $schedules[$scheduleName] = array( 'interval' => $this->checkPeriod * 3600, 'display' => sprintf('Every %d hours', $this->checkPeriod), ); } return $schedules; } /** * Remove the scheduled cron event that the library uses to check for updates. * * @return void */ public function _removeUpdaterCron(){ wp_clear_scheduled_hook($this->cronHook); } /** * Get the name of the update checker's WP-cron hook. Mostly useful for debugging. * * @return string */ public function getCronHookName() { return $this->cronHook; } } endif; if ( !class_exists('Loginizer_PucUpgraderStatus_3_2', false) ): /** * A utility class that helps figure out which plugin WordPress is upgrading. * * It may seem strange to have an separate class just for that, but the task is surprisingly complicated. * Core classes like Plugin_Upgrader don't expose the plugin file name during an in-progress update (AFAICT). * This class uses a few workarounds and heuristics to get the file name. */ class Loginizer_PucUpgraderStatus_3_2 { private $upgradedPluginFile = null; //The plugin that is currently being upgraded by WordPress. public function __construct() { //Keep track of which plugin WordPress is currently upgrading. add_filter('upgrader_pre_install', array($this, 'setUpgradedPlugin'), 10, 2); add_filter('upgrader_package_options', array($this, 'setUpgradedPluginFromOptions'), 10, 1); add_filter('upgrader_post_install', array($this, 'clearUpgradedPlugin'), 10, 1); add_action('upgrader_process_complete', array($this, 'clearUpgradedPlugin'), 10, 1); } /** * Is there and update being installed RIGHT NOW, for a specific plugin? * * Caution: This method is unreliable. WordPress doesn't make it easy to figure out what it is upgrading, * and upgrader implementations are liable to change without notice. * * @param string $pluginFile The plugin to check. * @param WP_Upgrader|null $upgrader The upgrader that's performing the current update. * @return bool True if the plugin identified by $pluginFile is being upgraded. */ public function isPluginBeingUpgraded($pluginFile, $upgrader = null) { if ( isset($upgrader) ) { $upgradedPluginFile = $this->getPluginBeingUpgradedBy($upgrader); if ( !empty($upgradedPluginFile) ) { $this->upgradedPluginFile = $upgradedPluginFile; } } return ( !empty($this->upgradedPluginFile) && ($this->upgradedPluginFile === $pluginFile) ); } /** * Get the file name of the plugin that's currently being upgraded. * * @param Plugin_Upgrader|WP_Upgrader $upgrader * @return string|null */ private function getPluginBeingUpgradedBy($upgrader) { if ( !isset($upgrader, $upgrader->skin) ) { return null; } //Figure out which plugin is being upgraded. $pluginFile = null; $skin = $upgrader->skin; if ( $skin instanceof Plugin_Upgrader_Skin ) { if ( isset($skin->plugin) && is_string($skin->plugin) && ($skin->plugin !== '') ) { $pluginFile = $skin->plugin; } } elseif ( isset($skin->plugin_info) && is_array($skin->plugin_info) ) { //This case is tricky because Bulk_Plugin_Upgrader_Skin (etc) doesn't actually store the plugin //filename anywhere. Instead, it has the plugin headers in $plugin_info. So the best we can //do is compare those headers to the headers of installed plugins. $pluginFile = $this->identifyPluginByHeaders($skin->plugin_info); } return $pluginFile; } /** * Identify an installed plugin based on its headers. * * @param array $searchHeaders The plugin file header to look for. * @return string|null Plugin basename ("foo/bar.php"), or NULL if we can't identify the plugin. */ private function identifyPluginByHeaders($searchHeaders) { if ( !function_exists('get_plugins') ){ /** @noinspection PhpIncludeInspection */ require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); } $installedPlugins = get_plugins(); $matches = array(); foreach($installedPlugins as $pluginBasename => $headers) { $diff1 = array_diff_assoc($headers, $searchHeaders); $diff2 = array_diff_assoc($searchHeaders, $headers); if ( empty($diff1) && empty($diff2) ) { $matches[] = $pluginBasename; } } //It's possible (though very unlikely) that there could be two plugins with identical //headers. In that case, we can't unambiguously identify the plugin that's being upgraded. if ( count($matches) !== 1 ) { return null; } return reset($matches); } /** * @access private * * @param mixed $input * @param array $hookExtra * @return mixed Returns $input unaltered. */ public function setUpgradedPlugin($input, $hookExtra) { if (!empty($hookExtra['plugin']) && is_string($hookExtra['plugin'])) { $this->upgradedPluginFile = $hookExtra['plugin']; } else { $this->upgradedPluginFile = null; } return $input; } /** * @access private * * @param array $options * @return array */ public function setUpgradedPluginFromOptions($options) { if (isset($options['hook_extra']['plugin']) && is_string($options['hook_extra']['plugin'])) { $this->upgradedPluginFile = $options['hook_extra']['plugin']; } else { $this->upgradedPluginFile = null; } return $options; } /** * @access private * * @param mixed $input * @return mixed Returns $input unaltered. */ public function clearUpgradedPlugin($input = null) { $this->upgradedPluginFile = null; return $input; } } endif; if ( !class_exists('Loginizer_PucFactory', false) ): /** * A factory that builds instances of other classes from this library. * * When multiple versions of the same class have been loaded (e.g. LoginizerUpdateChecker 1.2 * and 1.3), this factory will always use the latest available version. Register class * versions by calling {@link Loginizer_PucFactory::addVersion()}. * * At the moment it can only build instances of the LoginizerUpdateChecker class. Other classes * are intended mainly for internal use and refer directly to specific implementations. If you * want to instantiate one of them anyway, you can use {@link Loginizer_PucFactory::getLatestClassVersion()} * to get the class name and then create it with new $class(...). */ class Loginizer_PucFactory { protected static $classVersions = array(); protected static $sorted = false; /** * Create a new instance of LoginizerUpdateChecker. * * @see LoginizerUpdateChecker::__construct() * * @param $metadataUrl * @param $pluginFile * @param string $slug * @param int $checkPeriod * @param string $optionName * @param string $muPluginFile * @return LoginizerUpdateChecker_3_2 */ public static function buildUpdateChecker($metadataUrl, $pluginFile, $slug = '', $checkPeriod = 12, $optionName = '', $muPluginFile = '') { $class = self::getLatestClassVersion('LoginizerUpdateChecker'); return new $class($metadataUrl, $pluginFile, $slug, $checkPeriod, $optionName, $muPluginFile); } /** * Get the specific class name for the latest available version of a class. * * @param string $class * @return string|null */ public static function getLatestClassVersion($class) { if ( !self::$sorted ) { self::sortVersions(); } if ( isset(self::$classVersions[$class]) ) { return reset(self::$classVersions[$class]); } else { return null; } } /** * Sort available class versions in descending order (i.e. newest first). */ protected static function sortVersions() { foreach ( self::$classVersions as $class => $versions ) { uksort($versions, array(__CLASS__, 'compareVersions')); self::$classVersions[$class] = $versions; } self::$sorted = true; } protected static function compareVersions($a, $b) { return -version_compare($a, $b); } /** * Register a version of a class. * * @access private This method is only for internal use by the library. * * @param string $generalClass Class name without version numbers, e.g. 'LoginizerUpdateChecker'. * @param string $versionedClass Actual class name, e.g. 'LoginizerUpdateChecker_1_2'. * @param string $version Version number, e.g. '1.2'. */ public static function addVersion($generalClass, $versionedClass, $version) { if ( !isset(self::$classVersions[$generalClass]) ) { self::$classVersions[$generalClass] = array(); } self::$classVersions[$generalClass][$version] = $versionedClass; self::$sorted = false; } } endif; //Register classes defined in this file with the factory. Loginizer_PucFactory::addVersion('LoginizerUpdateChecker', 'LoginizerUpdateChecker_3_2', '3.2'); Loginizer_PucFactory::addVersion('LoginizerUpdate', 'LoginizerUpdate_3_2', '3.2'); Loginizer_PucFactory::addVersion('LoginizerInfo', 'LoginizerInfo_3_2', '3.2'); Loginizer_PucFactory::addVersion('Loginizer_PucGitHubChecker', 'Loginizer_PucGitHubChecker_3_2', '3.2'); updater/license.txt000064400000002037147577721060010415 0ustar00Copyright (c) 2014 Jānis Elsts Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.functions.php000064400000016470147577721060007315 0ustar00response) || !isset($transient->response[$plugin])){ return $transient; } $free_version = loginizer_pro_get_free_version_num(); // Update the Loginizer version to the equivalent of Pro version if(version_compare($free_version, LOGINIZER_PRO_VERSION, '<')){ $transient->response[$plugin]->package = 'https://downloads.wordpress.org/plugin/loginizer.'.LOGINIZER_PRO_VERSION.'.zip'; }else{ unset($transient->response[$plugin]); } return $transient; } // Auto update free version after update pro version function loginizer_pro_update_free_after_pro($upgrader_object, $options) { // Check if the action is an update for the plugins if($options['action'] != 'update' || $options['type'] != 'plugin'){ return; } // Define the slugs for the free and pro plugins $free_slug = 'loginizer/loginizer.php'; $pro_slug = 'loginizer-security/loginizer-security.php'; // Check if the pro plugin is in the list of updated plugins if( (isset($options['plugins']) && in_array($pro_slug, $options['plugins'])) || (isset($options['plugin']) && $pro_slug == $options['plugin']) ){ // Trigger the update for the free plugin $current_version = loginizer_pro_get_free_version_num(); if(empty($current_version)){ return; } // Check for updates for the free plugin include_once(ABSPATH . 'wp-admin/includes/plugin.php'); $update_plugins = get_site_transient('update_plugins'); if(isset($update_plugins->response[$free_slug])){ $free_plugin_update = $update_plugins->response[$free_slug]; // If there's an update available, proceed to update the free plugin if(version_compare($free_plugin_update->new_version, $current_version, '>')){ require_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'); $upgrader = new Plugin_Upgrader(); $upgrader->upgrade($free_slug); } } } } function loginizer_pro_update_checker(){ $current_version = get_option('loginizer_pro_version', '0.0'); $version = (int) str_replace('.', '', $current_version); // No update required if($current_version == LOGINIZER_PRO_VERSION){ return true; } $is_network_wide = loginizer_pro_is_network_active('loginizer-security'); if($is_network_wide){ $free_ins = get_site_option('loginizer_free_installed'); }else{ $free_ins = get_option('loginizer_free_installed'); } // If plugin reached here it means Loginizer free installed if(empty($free_ins)){ if($is_network_wide){ update_site_option('loginizer_free_installed', time()); }else{ update_option('loginizer_free_installed', time()); } } update_option('loginizer_version_pro_nag', time()); update_option('loginizer_version_free_nag', time()); update_option('loginizer_pro_version', LOGINIZER_PRO_VERSION); } // Add our license key if ANY function loginizer_updater_filter_args($queryArgs) { global $loginizer; if ( !empty($loginizer['license']['license']) ) { $queryArgs['license'] = $loginizer['license']['license']; } $queryArgs['url'] = rawurlencode(site_url()); return $queryArgs; } // Handle the Check for update link and ask to install license key function loginizer_updater_check_link($final_link){ global $loginizer; if(empty($loginizer['license']['license'])){ return 'Install License Key to Update'; } return $final_link; } function loginizer_pro_load_license($parent = 0){ global $loginizer; // Load license if(!empty($parent)){ $license_field = 'softaculous_pro_license'; $license_api_url = 'https://a.softaculous.com/softwp/'; $prods = apply_filters('softaculous_pro_products', []); }else{ $license_field = 'loginizer_license'; $license_api_url = LOGINIZER_API; $prods = []; } $loginizer['license'] = get_option($license_field, []); // Update license details as well if(!empty($loginizer['license']) && !empty($loginizer['license']['license']) && (time() - @$loginizer['license']['last_update']) >= 86400){ $resp = wp_remote_get($license_api_url.'license.php?license='.$loginizer['license']['license'].'&prods='.implode(',', $prods).'&url='.rawurlencode(site_url())); // Did we get a response ? if(is_array($resp)){ $tosave = json_decode($resp['body'], true); // Is it the license ? if(!empty($tosave['license'])){ $tosave['last_update'] = time(); update_option($license_field, $tosave); } } } // If the license is Free or Expired check for Softaculous Pro license if(empty($loginizer['license']) || empty($loginizer['license']['active'])){ if(function_exists('softaculous_pro_load_license')){ $softaculous_license = softaculous_pro_load_license(); if(!empty($softaculous_license['license']) && (!empty($softaculous_license['active']) || empty($loginizer['license'])) ){ $loginizer['license'] = $softaculous_license; } }elseif(empty($parent)){ $loginizer['license'] = get_option('softaculous_pro_license', []); if(!empty($loginizer['license'])){ loginizer_pro_load_license(1); } } } } add_filter('softaculous_pro_products', 'loginizer_softaculous_pro_products', 10, 1); function loginizer_softaculous_pro_products($r = []){ $r['loginizer'] = 'loginizer'; return $r; } function loginizer_pro_api_url($main_server = 0, $suffix = 'loginizer'){ global $loginizer; $r = array( 'https://s0.softaculous.com/a/softwp/', 'https://s1.softaculous.com/a/softwp/', 'https://s2.softaculous.com/a/softwp/', 'https://s3.softaculous.com/a/softwp/', 'https://s4.softaculous.com/a/softwp/', 'https://s5.softaculous.com/a/softwp/', 'https://s7.softaculous.com/a/softwp/', 'https://s8.softaculous.com/a/softwp/' ); $mirror = $r[array_rand($r)]; // If the license is newly issued, we need to fetch from API only if(!empty($main_server) || empty($loginizer['license']['last_edit']) || (!empty($loginizer['license']['last_edit']) && (time() - 3600) < $loginizer['license']['last_edit']) ){ $mirror = LOGINIZER_API; } if(!empty($suffix)){ $mirror = str_replace('/softwp', '/'.$suffix, $mirror); } return $mirror; }index.php000064400000000032147577721060006377 0ustar00 30)); if(is_array($resp)){ $json = json_decode($resp['body'], true); //print_r($json); } // Save the License if(!empty($json['license'])){ update_option('loginizer_license', $json); } } unlink(__DIR__.'/license.key'); } // Load license loginizer_pro_load_license(); // Check for updates include_once('updater/plugin-update-checker.php'); $loginizer_updater = Loginizer_PucFactory::buildUpdateChecker(loginizer_pro_api_url().'/updates.php?version='.LOGINIZER_PRO_VERSION, LOGINIZER_PRO_FILE); // Add the license key to query arguments $loginizer_updater->addQueryArgFilter('loginizer_updater_filter_args'); // Show the text to install the license key add_filter('puc_manual_final_check_link-loginizer-security', 'loginizer_updater_check_link', 10, 1); add_filter('plugin_row_meta', 'loginizer_plugin_row_links', 10, 2); } // Checking For SSO if(!empty($_GET['ssotoken'])){ add_filter('authenticate', 'loginizer_sso_authenticate', 10003, 3); add_action('wp_login_errors', 'loginizer_error_handler', 10001, 2); add_action('wp_login', 'loginizer_login_success', 10, 2); } // CSRF Session URL if(!empty($loginizer['enable_csrf_protection']) && loginizer_is_csrf_prot_mod_set()){ add_action('init', 'loginizer_csrf_sess_init'); add_filter('login_redirect', 'loginizer_login_csrf_redirect', 200, 3); add_action('admin_bar_menu', 'loginizer_csrf_admin_bar_shortcut', 70); add_filter('admin_url', 'loginizer_csrf_admin_redirects', 100005, 3); add_filter('wp_redirect', 'loginizer_csrf_wp_redirects'); add_action('set_auth_cookie', 'loginizer_admin_url_cookie'); // Creates session key and handles cookies add_action('wp_logout', 'loginizer_destroy_csrf_session', 10, 1); } // Handles Concurrent Sessions if(!empty($loginizer['limit_session']) && !empty($loginizer['limit_session']['enable'])){ add_filter('wp_authenticate_user', 'loginizer_limit_sessions'); add_filter('check_password', 'loginizer_limit_destroy_sessions_handler', 10, 4); } // MasterStudy Login filter add_filter('stm_lms_login', 'loginizer_handle_stm_lms_login'); add_filter('loginizer_system_information', 'loginizer_premium_system_info', 10); add_filter('loginizer_pre_page_dashboard', 'loginizer_premium_page_dashboard', 10); // A way to remove the settings if(file_exists(LOGINIZER_PRO_DIR.'/reset_admin.txt')){ update_option('loginizer_wp_admin', array()); } // Are we to ban user emails ? if(!empty($loginizer['domains_blacklist']) && count($loginizer['domains_blacklist']) > 0){ add_filter('registration_errors', 'loginizer_domains_blacklist', 10, 3); add_filter('woocommerce_registration_errors', 'loginizer_domains_blacklist', 10, 3); } // Is email password less login enabled ? if(!empty($loginizer['email_pass_less']) && !defined('XMLRPC_REQUEST')){ // Add a handler for the GUI Login add_filter('authenticate', 'loginizer_epl_wp_authenticate', 10002, 3); // Dont show password error add_filter('wp_login_errors', 'loginizer_epl_error_handler', 10000, 2); // Hide the password field add_action('login_enqueue_scripts', 'loginizer_epl_hide_pass'); add_action('wp_enqueue_scripts', 'loginizer_epl_hide_woocommerce_pass'); } // Are we to rename the login ? if(!empty($loginizer['login_slug'])){ //$loginizer['login_slug'] = 'login'; // Add the filters / actions add_filter('site_url', 'loginizer_rl_site_url', 10, 2); add_filter('network_site_url', 'loginizer_rl_site_url', 10, 2); add_filter('wp_redirect', 'loginizer_rl_wp_redirect', 10, 2); add_filter('register', 'loginizer_rl_register'); add_action('wp_loaded', 'loginizer_rl_wp_loaded'); } // Rename the WP-ADMIN folder if(!defined('SITEPAD') && !empty($loginizer['admin_slug'])){ add_filter('admin_url', 'loginizer_admin_url', 10001, 3); add_action('set_auth_cookie', 'loginizer_admin_url_cookie'); // For multisite if(lz_is_multisite()){ add_filter('network_admin_url', 'loginizer_network_admin_url', 10001, 2); } if(!empty($loginizer['restrict_wp_admin']) && preg_match('/\/wp-admin/is', $_SERVER['REQUEST_URI'])){ die(empty($loginizer['wp_admin_msg']) ? $loginizer['wp_admin_d_msg'] : $loginizer['wp_admin_msg']); } } // Are we to rename the xmlrpc ? if(!defined('SITEPAD') && !empty($loginizer['xmlrpc_slug']) && empty($loginizer['xmlrpc_disable'])){ // Add the filters / actions add_action('wp_loaded', 'loginizer_xml_rename_wp_loaded'); } // Are we to DISABLE the xmlrpc ? if(!empty($loginizer['xmlrpc_disable'])){ // Add the filters / actions add_filter('xmlrpc_enabled', 'loginizer_xmlrpc_null'); add_filter('bloginfo_url', 'loginizer_xmlrpc_remove_pingback_url', 10000, 2); add_action('wp_loaded', 'loginizer_xmlrpc_disable'); } // Are we to disable pingbacks ? if(!empty($loginizer['pingbacks_disable'])){ // Add the filters / actions add_filter('xmlrpc_methods', 'loginizer_pingbacks_disable'); } //----------------------------------- // Add the captcha filters / actions //----------------------------------- if(!empty($loginizer['social_settings']) && !loginizer_is_blacklisted()){ // Shortcode has options shape|divider|type add_shortcode('loginizer_social', 'loginizer_social_shortcode'); if(!empty($_COOKIE['lz_social_error'])){ add_action('woocommerce_before_customer_login_form', 'loginizer_social_wc_error'); } if(!empty($loginizer['social_settings']['general']['save_avatar'])){ add_filter('get_avatar', 'loginizer_social_update_avatar', 1, 5); } if(!empty($loginizer['social_settings']['login']['registration_form'])){ add_action('register_form', 'loginizer_social_btn_login', 100); } if(in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))){ if(!empty($loginizer['social_settings']['woocommerce']['login_form'])){ add_action('woocommerce_login_form', 'loginizer_social_btn_woocommerce', 100); } if(!empty($loginizer['social_settings']['woocommerce']['registration_form'])){ add_action('woocommerce_register_form', 'loginizer_social_btn_woocommerce'); } } if(!empty($loginizer['social_settings']['comment']['enable_buttons'])){ add_action('comment_form_must_log_in_after', 'loginizer_social_btn_comment'); } } if(!empty($loginizer['captcha_key']) || !empty($loginizer['captcha_no_google']) || !empty($loginizer['captcha_status'])){ add_action('login_init', 'loginizer_cap_session_key'); // Is reCaptcha on for login ? if(!empty($loginizer['captcha_login']) && !defined('XMLRPC_REQUEST')){ add_filter('authenticate', 'loginizer_cap_login_verify', 10000); add_action('login_form', 'loginizer_cap_form_login', 100); add_action('woocommerce_login_form', 'loginizer_cap_form_login', 100); // Need to make more room for login form if(empty($loginizer['captcha_remove_css'])){ add_action('login_enqueue_scripts', 'loginizer_cap_login_form'); } } // Is reCaptcha on for Lost Password utility ? if(!empty($loginizer['captcha_lostpass'])){ add_action('allow_password_reset', 'loginizer_cap_lostpass_verify', 10, 2); add_action('lostpassword_form', 'loginizer_cap_form_login', 100); add_filter('woocommerce_lostpassword_form', 'loginizer_cap_form_login'); } // Is reCaptcha on for Reset Password utility ? if(!empty($loginizer['captcha_resetpass'])){ add_filter('validate_password_reset', 'loginizer_cap_resetpass_verify', 10, 2); add_action('resetpass_form', 'loginizer_cap_reset_form', 99); add_filter('woocommerce_resetpassword_form', 'loginizer_cap_form_login'); } // Is reCaptcha on for registration ? if(!empty($loginizer['captcha_register'])){ add_filter('registration_errors', 'loginizer_cap_register_verify', 10, 3); add_action('register_form', 'loginizer_cap_form_login', 100); // For BuddyPress add_filter('bp_signup_validate', 'loginizer_cap_register_verify_buddypress', 10, 3); add_action('bp_after_signup_profile_fields', 'loginizer_cap_form_login', 100); add_filter('woocommerce_before_checkout_process', 'loginizer_wc_before_checkout_process', 10); add_filter('woocommerce_register_form', 'loginizer_cap_form_login'); add_filter('woocommerce_registration_errors', 'loginizer_cap_register_verify', 10, 3); if(!empty($loginizer['captcha_wc_checkout'])){ add_action('woocommerce_checkout_order_review', 'loginizer_cap_form_ecommerce'); } } // Are we to show Captcha for guests only ? if((is_user_logged_in() && empty($loginizer['captcha_user_hide'])) || !is_user_logged_in()){ // Is reCaptcha on for comment utility ? if(!empty($loginizer['captcha_comment'])){ add_filter('preprocess_comment', 'loginizer_cap_comment_verify'); add_action('comment_form', 'loginizer_cap_comment_form'); } // Is reCaptcha on for WooCommerce Logout utility ? if(!empty($loginizer['captcha_wc_checkout'])){ add_action('woocommerce_after_checkout_validation', 'loginizer_wc_checkout_verify'); add_action('woocommerce_checkout_order_review', 'loginizer_cap_form_ecommerce'); } } } //----------------- // Two Factor Auth //----------------- if(!defined('SITEPAD') && loginizer_is_2fa_enabled() && !defined('XMLRPC_REQUEST')){ // After username and password check has been verified, are we to redirect ? add_filter('authenticate', 'loginizer_user_redirect', 10003, 3); $user_id = get_current_user_id(); $lz_2fa_state = get_transient('loginizer_2fa_'. $user_id); // To redirect after login if(!empty($_COOKIE['loginizer_2fa_' . $user_id]) && !empty($lz_2fa_state) && $lz_2fa_state != '2fa'){ loginizer_2fa_ajax_redirect(); } $login_slug = 'wp-login.php'; if($loginizer['login_slug']){ $login_slug = $loginizer['login_slug']; } if(loginizer_cur_page() !== $login_slug && !empty($lz_2fa_state) && $lz_2fa_state == '2fa'){ wp_logout(); wp_safe_redirect(admin_url()); exit; } // Shows the Question / 2fa field add_action('login_form_loginizer_security', 'loginizer_user_security'); $cur_user = wp_get_current_user(); //Add the Loginizer Security Settings Page for WooCommerce if(class_exists('WooCommerce') && loginizer_is_2fa_applicable($cur_user)){ add_action( 'init', 'loginizer_add_premium_security_endpoint' ); add_filter( 'query_vars', 'loginizer_premium_security_query_vars', 0 ); add_filter( 'woocommerce_account_menu_items', 'loginizer_add_premium_security_link_my_account' ); add_action( 'woocommerce_account_loginizer-security_endpoint', 'loginizer_user_page' ); } // Is the user logged in ? if(is_user_logged_in()){ // Load user settings loginizer_load_user_settings($tfa_uid, $tfa_user, $tfa_settings, $tfa_current_pref); // If 2FA applicable as per role if(loginizer_is_2fa_applicable($tfa_user)){ // Add to Settings menu on sites add_action('admin_menu', 'loginizer_user_menu'); // Show the user the notification to set a 2FA $loginizer['loginizer_2fa_notice'] = get_user_meta($tfa_uid, 'loginizer_2fa_notice'); // Are we to show the loginizer notification to set a 2FA if(empty($loginizer['loginizer_2fa_notice']) && (empty($_COOKIE['loginizer_2fa_notice_'.$tfa_uid]) || $_COOKIE['loginizer_2fa_notice_'.$tfa_uid] != md5(wp_get_session_token())) && (empty($tfa_current_pref) || $tfa_current_pref == 'none') && lz_optget('page') != 'loginizer_user' ){ add_action('admin_notices', 'loginizer_2fa_notice'); } // Are we to disable the notice forever ? if(isset($_GET['loginizer_2fa_notice']) && (int)$_GET['loginizer_2fa_notice'] == 0){ update_user_meta($tfa_uid, 'loginizer_2fa_notice', time()); die('DONE'); } // Are we to disable the notice temporarily ? if(isset($_GET['loginizer_2fa_notice']) && (int)$_GET['loginizer_2fa_notice'] == 1){ @setcookie('loginizer_2fa_notice_'.$tfa_uid, md5(wp_get_session_token()), time() + (3 * DAY_IN_SECONDS), COOKIEPATH, COOKIE_DOMAIN, is_ssl()); } } add_filter('manage_users_columns', 'loginizer_2fa_columns_users'); add_filter('manage_users_custom_column', 'loginizer_2fa_column_data', 10, 3); } } // Checksum is enabled right i.e. its not disabled ? if(!defined('SITEPAD') && empty($loginizer['disable_checksum'])){ // Create an action always add_action('loginizer_do_checksum', 'loginizer_checksums'); // Difference in seconds since last time $diff = (time() - $loginizer['checksums_last_run']); // Has it crossed the time ? if(($diff / 86400) >= $loginizer['checksum_frequency']){ //loginizer_checksums(); wp_schedule_single_event(time(), 'loginizer_do_checksum'); } } if(wp_doing_ajax()){ include_once LOGINIZER_PRO_DIR . 'main/ajax.php'; return; } if(is_admin()){ include_once LOGINIZER_PRO_DIR . 'main/admin.php'; } } function loginizer_social_wc_error(){ // Showing woocommerce error if(!function_exists('wc_add_wp_error_notices')){ return; } $errors = loginizer_social_login_error_handler(); if(empty($errors) || !is_wp_error($errors)){ return; } wc_add_wp_error_notices($errors); loginizer_woocommerce_error_handler(); woocommerce_output_all_notices(); } function loginizer_premium_system_info(){ global $loginizer; echo ' '.__('Loginizer License', 'loginizer').' '.(empty($loginizer['license']) ? 'Unlicensed    ' : '').'   '; if(!empty($loginizer['license'])){ $expires = $loginizer['license']['expires']; $expires = substr($expires, 0, 4).'/'.substr($expires, 4, 2).'/'.substr($expires, 6); echo '
License Active : '.(empty($loginizer['license']['active']) ? 'No' : 'Yes').'       License Expires : '.($loginizer['license']['expires'] <= date('Ymd') ? ''.$expires.'' : $expires).'
'; } echo ' '; } function loginizer_premium_page_dashboard(){ global $loginizer, $lz_error; // Is there a license key ? if(isset($_POST['save_lz'])){ $license = lz_optpost('lz_license'); // Check if its a valid license if(empty($license)){ $lz_error['lic_invalid'] = __('The license key was not submitted', 'loginizer'); return loginizer_page_dashboard_T(); } $resp = wp_remote_get(LOGINIZER_API.'license.php?license='.$license.'&url='.rawurlencode(site_url()), array('timeout' => 30)); if(is_array($resp)){ $json = json_decode($resp['body'], true); //print_r($json); }else{ $lz_error['resp_invalid'] = __('The response was malformed
'.var_export($resp, true), 'loginizer'); return loginizer_page_dashboard_T(); } // Save the License if(empty($json['license'])){ $lz_error['lic_invalid'] = __('The license key is invalid', 'loginizer'); return loginizer_page_dashboard_T(); }else{ update_option('loginizer_license', $json); // Mark as saved $GLOBALS['lz_saved'] = true; $loginizer['license'] = get_option('loginizer_license'); } } } // Change the Admin URL function loginizer_admin_url($url, $path, $blog_id){ global $loginizer; //echo $url."\n";echo $path."\n"; $new = str_replace('wp-admin', $loginizer['admin_slug'], $url); //echo $new.'
'; return $new; } function loginizer_network_admin_url($url, $path){ global $loginizer; //echo $url.'
';echo $path.'
'; $new = str_replace('wp-admin', $loginizer['admin_slug'], $url); //echo $new.'
'; return $new; } // Required to be able to Login function loginizer_admin_url_cookie($auth_cookie, $expire = 0, $expiration = '', $user_id = '', $scheme = ''){ global $loginizer; if($scheme == 'secure_auth' || is_ssl()){ $auth_cookie_name = SECURE_AUTH_COOKIE; $secure = true; }else { $auth_cookie_name = AUTH_COOKIE; $secure = false; } $admin_slug = $loginizer['admin_slug']; // Auth cookie has the user's username in it as the first word before pipe | so we get the user name through that if(!empty($loginizer['enable_csrf_protection']) && !empty($auth_cookie)){ $u_login = explode('|', $auth_cookie); $u_login = $u_login[0]; $user = get_user_by('login', $u_login); $session = get_user_meta($user->ID, 'loginizer_csrf_session', true); if(empty($session)){ loginizer_csrf_create_session($user->ID); } $session = get_user_meta($user->ID, 'loginizer_csrf_session', true); $admin_slug = 'wp-admin-' . $session; if(!empty($loginizer['admin_slug'])){ $admin_slug = $loginizer['admin_slug'] . '-' . $session; } } @setcookie($auth_cookie_name, $auth_cookie, $expire, SITECOOKIEPATH . $admin_slug, COOKIE_DOMAIN, $secure, true); } // Verifies if the token is valid and creates the user session function loginizer_epl_verify(){ global $loginizer; if(empty($_GET['uid']) || empty($_GET['lepltoken'])){ return false; } $uid = (int) sanitize_key($_GET['uid']); $token = sanitize_key($_GET['lepltoken']); $action = 'loginizer_epl_'.$uid; $hash = get_user_meta($uid, $action, true); $expires = get_user_meta($uid, $action.'_expires', true); include_once(ABSPATH.'/'.$loginizer['wp-includes'].'/class-phpass.php'); $wp_hasher = new PasswordHash(8, TRUE); $time = time(); if(!$wp_hasher->CheckPassword($expires.$token, $hash) || $expires < $time){ $token_error_msg = __('The token is invalid or has expired. Please request a new email', 'loginizer'); // Throw an error return new WP_Error('token_invalid', $token_error_msg, 'loginizer_epl'); }else{ if(!empty($loginizer['limit_session']) && !empty($loginizer['limit_session']['enable'])){ $limit_session = loginizer_limit_destroy_sessions($uid); if(empty($limit_session)){ return new WP_Error('loginizer_session_limit', __('User ID not found so can not proceed', 'loginizer'), 'loginizer_epl'); } } // Login the User wp_set_auth_cookie($uid); // Delete the meta delete_user_meta($uid, $action); delete_user_meta($uid, $action.'_expires'); $user = get_user_by('id', $uid); $redirect = !empty($_REQUEST['redirect_to']) ? esc_url_raw($_REQUEST['redirect_to']) : ''; if(!empty($loginizer['passwordless_redirect']) && !empty($user) && !empty($user->roles)){ $roles = $user->roles; if(!is_array($user->roles)) { $roles = [$user->roles]; } // To check if we need a custom redirect for the role this user has foreach($roles as $r){ if(!empty($loginizer['passwordless_redirect_for']) && in_array($r, $loginizer['passwordless_redirect_for'])){ $redirect = $loginizer['passwordless_redirect']; break; } } } $redirect_to = !empty($redirect) ? $redirect : admin_url(); $redirect_to = loginizer_csrf_change_url($redirect_to, $uid); loginizer_update_attempt_stats(1); // Redirect and exit wp_safe_redirect($redirect_to); exit; } return false; } // Hides the password field for the password less email login function loginizer_epl_hide_pass() { ?> errors);echo '
'; // Remove the empty password error if(is_wp_error($errors)){ $errors->remove('empty_password'); } return $errors; } // Handles the verification of the username or email function loginizer_epl_wp_authenticate($user, $username, $password){ global $loginizer; //echo 'loginizer_epl_wp_authenticate : '; print_r($user).'
'; if(is_wp_error($user)){ // Ignore certain codes $ignore_codes = array('empty_username', 'empty_password'); if(is_wp_error($user) && !in_array($user->get_error_code(), $ignore_codes)) { return $user; } } // Is it a login attempt $verified = loginizer_epl_verify(); if(is_wp_error($verified)){ return $verified; } if(empty($username) && empty($_POST)){ return $user; } $email = NULL; // Is it an email address ? if(is_email($username) && email_exists($username)){ $email = $username; } // Maybe its a username if(!is_email($username) && username_exists($username)){ $user = get_user_by('login', $username); if($user){ $email = $user->data->user_email; } } // Did you get any valid email ? if(empty($email)){ $account_error_msg = __('The username or email you provided does not exist !', 'loginizer'); return new WP_Error('invalid_account', $account_error_msg, 'loginizer_epl'); } if(!empty($loginizer['limit_session']) && !empty($loginizer['limit_session']['enable'])){ $user = get_user_by('email', $email); $session_limit = loginizer_limit_sessions($user); if(is_wp_error($session_limit)){ return $session_limit; } } // Send the email $site_name = get_bloginfo('name'); $login_url = loginizer_epl_login_url($email); $vars = array('email' => $email, 'site_name' => $site_name, 'site_url' => get_site_url(), 'login_url' => $login_url); $subject = lz_lang_vars_name($loginizer['passwordless_sub'], $vars); $message = lz_lang_vars_name($loginizer['passwordless_msg'], $vars); //echo $subject.'

';echo $message; $headers = array(); // Do we need to send the email as HTML ? if(!empty($loginizer['passwordless_html'])){ $headers[] = 'Content-Type: text/html; charset=UTF-8'; if(!empty($loginizer['passwordless_msg_is_custom'])){ $message = html_entity_decode($message); }else{ $message = preg_replace("/\/i", "
", $message); $message = preg_replace('/(?)\n/i', "
\n", $message); } } $sent = wp_mail($email, $subject, $message, $headers); //echo $login_url; if(empty($sent)){ $email_not_sent = __('There was a problem sending your email. Please try again or contact an admin.', 'loginizer'); return new WP_Error('email_not_sent', $email_not_sent, 'loginizer_epl'); }else{ $loginizer['no_loginizer_logs'] = 1; $email_sent_msg = __('An email has been sent with the Login URL', 'loginizer'); return new WP_Error('email_sent', $email_sent_msg, 'message'); } } // Generate the URL for the function loginizer_epl_login_url($email){ // Get the User ID $user = get_user_by('email', $email); $token = loginizer_epl_token($user->ID); // The current URL $redirect_url = ''; if(!empty($_REQUEST['redirect_to'])){ $redirect_url = $_REQUEST['redirect_to']; } elseif (!empty($_REQUEST['redirect'])){ $redirect_url = $_REQUEST['redirect']; } elseif(!empty(wp_validate_redirect($_SERVER['HTTP_REFERER']))){ $redirect_url = wp_validate_redirect($_SERVER['HTTP_REFERER']); } $redirect_param = (!empty($redirect_url) ? '&redirect_to='.urlencode($redirect_url) : ''); $url = wp_login_url().'?uid='.$user->ID.'&lepltoken='.$token.$redirect_param; return $url; } // Creates a one time token function loginizer_epl_token($uid = 0){ global $loginizer; // Variables $time = time(); $expires = ($time + 600); $action = 'loginizer_epl_'.$uid; include_once( ABSPATH . '/'.$loginizer['wp-includes'].'/class-phpass.php'); $wp_hasher = new PasswordHash(8, TRUE); // Create the token with a random salt and the time $token = wp_hash(wp_generate_password(20, false).$action.$time); // Create a hash of the token $stored_hash = $wp_hasher->HashPassword($expires.$token); // Store the hash and when it expires update_user_meta($uid, $action, $stored_hash); update_user_meta($uid, $action.'_expires', $expires); return $token; } // Send a 404 function loginizer_set_404(){ global $wp_query; status_header(404); $wp_query->set_404(); if( (($template = get_404_template()) || ($template = get_index_template())) && ($template = apply_filters('template_include', $template)) ){ include($template); } die(); } // Find the page being accessed function loginizer_cur_page(){ $blog_url = trailingslashit(get_bloginfo('url')); $server_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''; $server_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; // Build the Current URL $url = (is_ssl() ? 'https://' : 'http://') . $server_host . $server_uri; if(is_ssl() && preg_match('/^http\:/is', $blog_url)){ $blog_url = substr_replace($blog_url, 's', 4, 0); } // The relative URL to the Blog URL $req = str_replace($blog_url, '', $url); $req = str_replace('index.php/', '', $req); // We dont need the args $parts = explode('?', $req, 2); $relative = basename($parts[0]); // Remove trailing slash $relative = rtrim($relative, '/'); $tmp = explode('/', $relative, 2); $page = end($tmp); //echo 'Page : '.$page.'
'; return $page; } // Converts the URL as per the one stored function loginizer_rl_convert_url($link){ global $loginizer; $dbt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 7); // If the login page is to be kept secret if(!empty($loginizer['rename_login_secret']) && loginizer_cur_page() !== $loginizer['login_slug'] && !is_user_logged_in() && (empty($dbt[6]) || $dbt[6]['function'] != 'get_the_password_form')){ return $link; } $result = $link; if(!empty($loginizer['login_slug']) && strpos($link, $loginizer['login_basename']) !== false){ $result = str_replace($loginizer['login_basename'], $loginizer['login_slug'], $link); } if(!empty($loginizer['xmlrpc_slug']) && strpos($link, 'xmlrpc.php') !== false){ $result = str_replace($loginizer['login_basename'], $loginizer['login_slug'], $link); } return $result; } function loginizer_rl_site_url($link){ $result = loginizer_rl_convert_url($link); return $result; } function loginizer_rl_wp_redirect($link){ $result = loginizer_rl_convert_url($link); return $result; } function loginizer_rl_register($link){ $result = loginizer_rl_convert_url($link); return $result; } // Shows the Login correctly function loginizer_rl_wp_loaded(){ global $loginizer, $interim_login; $page = loginizer_cur_page(); // Is it wp-login.php ? if ($page === $loginizer['login_basename']) { loginizer_set_404(); } // Is it our SLUG ? If not then return if($page !== rtrim($loginizer['login_slug'], '/')){ return false; } // We dont want a WP plugin caching this page @define('NO_CACHE', true); @define('WTC_IN_MINIFY', true); @define('WP_CACHE', false); $user_login = ''; $error = ''; // Prevent errors from defining constants again error_reporting(E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); include ABSPATH.'/'.$loginizer['login_basename']; exit(); } // Renames the XML-RPC functionality function loginizer_xml_rename_wp_loaded(){ global $loginizer; $page = loginizer_cur_page(); // Is it xmlrpc.php ? if ($page === 'xmlrpc.php') { loginizer_set_404(); } // Is it our SLUG ? If not then return if($page !== $loginizer['xmlrpc_slug']){ return false; } // We dont want a WP plugin caching this page @define('NO_CACHE', true); @define('WTC_IN_MINIFY', true); @define('WP_CACHE', false); // Prevent errors from defining constants again error_reporting(E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); include ABSPATH.'/xmlrpc.php'; exit(); } // Disables the XML-RPC functionality function loginizer_xmlrpc_null(){ return null; } // Disables the XML-RPC functionality function loginizer_xmlrpc_disable(){ global $loginizer; $page = loginizer_cur_page(); // Is it xmlrpc.php ? if ($page === 'xmlrpc.php'){ echo 'XML-RPC is disabled'; exit(); } } // Disables the XML-RPC functionality function loginizer_xmlrpc_remove_pingback_url($output, $show) { if($show == 'pingback_url'){ $output = ''; } return $output; } // Disable Pingbacks function loginizer_pingbacks_disable($methods) { if(isset($methods['pingback.ping'])){ unset($methods['pingback.ping']); } if(isset($methods['pingback.extensions.getPingbacks'])){ unset($methods['pingback.extensions.getPingbacks']); } return $methods; } //======================== // Captcha Codes //======================== // Adjusts the login form function loginizer_cap_login_form(){ ?> add('loginizer_resetpass_cap_error', $captcha_fail_msg, 'loginizer_cap'); } } // Verify the register captcha is valid ? function loginizer_cap_register_verify($errors, $username = '', $email = ''){ if(!loginizer_cap_verify()){ $captcha_fail_msg = __('The CAPTCHA verification failed. Please try again.', 'loginizer'); $errors->add('loginizer_cap_register_error', $captcha_fail_msg, 'loginizer_cap'); } return $errors; } // Verify the register captcha is valid ? function loginizer_cap_register_verify_buddypress($errors, $username = '', $email = ''){ global $bp; // $bp is for BuddyPress Registration it does not pass $errors if(!loginizer_cap_verify()){ $captcha_fail_msg = __('The CAPTCHA verification failed. Please try again.', 'loginizer'); // $bp is for BuddyPress $bp->signup->errors['signup_username'] = $captcha_fail_msg; } return $errors; } // Verify the register captcha is valid ? function loginizer_cap_comment_verify($comment){ if(!loginizer_cap_verify()){ wp_die('The CAPTCHA verification failed. Please try again.', 200); } return $comment; } // Verify WooCommerce Checkout Orders function loginizer_wc_checkout_verify(){ global $loginizer; // Is the registration function verifying it ? if(!is_user_logged_in() && get_option('woocommerce_enable_signup_and_login_from_checkout', 'yes') == 'yes' && !empty($loginizer['captcha_register'])){ // So, no need of any more verification // Lets verify }elseif(!loginizer_cap_verify()){ $captcha_fail_msg = __('The CAPTCHA verification failed. Please try again.', 'loginizer'); wc_add_notice($captcha_fail_msg, 'error'); } } // Reset password form passes $user, hence we need to manually write echo function loginizer_cap_reset_form($user = false){ loginizer_cap_form_login(false); } // For comment form pass false to echo the form function loginizer_cap_comment_form($post_id = 0){ echo '
';loginizer_cap_form_social(false); } // Converts numbers to words function loginizer_cap_num_to_words( $number ) { $words = array( 1 => __( 'one', 'loginizer' ), 2 => __( 'two', 'loginizer' ), 3 => __( 'three', 'loginizer' ), 4 => __( 'four', 'loginizer' ), 5 => __( 'five', 'loginizer' ), 6 => __( 'six', 'loginizer' ), 7 => __( 'seven', 'loginizer' ), 8 => __( 'eight', 'loginizer' ), 9 => __( 'nine', 'loginizer' ), 10 => __( 'ten', 'loginizer' ), 11 => __( 'eleven', 'loginizer' ), 12 => __( 'twelve', 'loginizer' ), 13 => __( 'thirteen', 'loginizer' ), 14 => __( 'fourteen', 'loginizer' ), 15 => __( 'fifteen', 'loginizer' ), 16 => __( 'sixteen', 'loginizer' ), 17 => __( 'seventeen', 'loginizer' ), 18 => __( 'eighteen', 'loginizer' ), 19 => __( 'nineteen', 'loginizer' ), 20 => __( 'twenty', 'loginizer' ), 30 => __( 'thirty', 'loginizer' ), 40 => __( 'forty', 'loginizer' ), 50 => __( 'fifty', 'loginizer' ), 60 => __( 'sixty', 'loginizer' ), 70 => __( 'seventy', 'loginizer' ), 80 => __( 'eighty', 'loginizer' ), 90 => __( 'ninety', 'loginizer' ) ); if ( isset( $words[$number] ) ) return $words[$number]; else { $reverse = false; switch ( get_bloginfo( 'language' ) ) { case 'de-DE': $spacer = 'und'; $reverse = true; break; case 'nl-NL': $spacer = 'en'; $reverse = true; break; case 'ru-RU': case 'pl-PL': case 'en-EN': default: $spacer = ' '; } $first = (int) (substr( $number, 0, 1 ) * 10); $second = (int) substr( $number, -1 ); return ($reverse === false ? $words[$first] . $spacer . $words[$second] : $words[$second] . $spacer . $words[$first]); } } // Encode the operation function loginizer_cap_encode_op($string){ return $string; } // Get the session key. If not there create one function loginizer_cap_session_key(){ if(isset($_COOKIE['lz_math_sess']) && preg_match('/[a-z0-9]/is', $_COOKIE['lz_math_sess']) && strlen($_COOKIE['lz_math_sess']) == 40){ return $_COOKIE['lz_math_sess']; } // Generate the key $new_session_key = lz_RandomString(40); // Set the cookie if(@setcookie('lz_math_sess', $new_session_key, time() + (30 * DAY_IN_SECONDS), COOKIEPATH, COOKIE_DOMAIN, is_ssl())){ // Set this to use first time $_COOKIE['lz_math_sess'] = $new_session_key; } return $new_session_key; } // Generate the Captcha field if its a Math Captcha function loginizer_cap_phrase($form = 'default'){ global $loginizer; $ops = array('add' => '+', 'subtract' => '−', 'multiply' => '×', 'divide' => '÷', ); $input = ''; if(empty($loginizer['captcha_add'])){ unset($ops['add']); } if(empty($loginizer['captcha_subtract'])){ unset($ops['subtract']); } if(empty($loginizer['captcha_multiply'])){ unset($ops['multiply']); } if(empty($loginizer['captcha_divide'])){ unset($ops['divide']); } // Randomly select an operation $rnd_op = array_rand($ops, 1); $number[3] = $ops[$rnd_op]; // Select where to place empty input $rnd_input = mt_rand(0, 2); // Generate the numbers switch ($rnd_op){ case 'add': if($rnd_input === 0){ $number[0] = mt_rand(1, 10); $number[1] = mt_rand(1, 89); }elseif($rnd_input === 1) { $number[0] = mt_rand(1, 89); $number[1] = mt_rand(1, 10); }elseif($rnd_input === 2){ $number[0] = mt_rand(1, 9); $number[1] = mt_rand(1, 10 - $number[0]); } $number[2] = $number[0] + $number[1]; break; case 'subtract': if($rnd_input === 0){ $number[0] = mt_rand(2, 10); $number[1] = mt_rand(1, $number[0] - 1); }elseif($rnd_input === 1){ $number[0] = mt_rand(11, 99); $number[1] = mt_rand(1, 10); }elseif($rnd_input === 2){ $number[0] = mt_rand(11, 99); $number[1] = mt_rand($number[0] - 10, $number[0] - 1); } $number[2] = $number[0] - $number[1]; break; case 'multiply': if($rnd_input === 0){ $number[0] = mt_rand(1, 10); $number[1] = mt_rand(1, 9); }elseif($rnd_input === 1){ $number[0] = mt_rand(1, 9); $number[1] = mt_rand(1, 10); }elseif($rnd_input === 2){ $number[0] = mt_rand(1, 10); $number[1] = ($number[0] > 5 ? 1 : ($number[0] === 4 && $number[0] === 5 ? mt_rand(1, 2 ) : ($number[0] === 3 ? mt_rand(1, 3 ) : ($number[0] === 2 ? mt_rand(1, 5 ) : mt_rand(1, 10 ))))); } $number[2] = $number[0] * $number[1]; break; case 'divide': $divide = array( 1 => 99, 2 => 49, 3 => 33, 4 => 24, 5 => 19, 6 => 16, 7 => 14, 8 => 12, 9 => 11, 10 => 9 ); if($rnd_input === 0){ $divide = array( 2 => array( 1, 2 ), 3 => array( 1, 3 ), 4 => array( 1, 2, 4 ), 5 => array( 1, 5 ), 6 => array( 1, 2, 3, 6 ), 7 => array( 1, 7 ), 8 => array( 1, 2, 4, 8 ), 9 => array( 1, 3, 9 ), 10 => array( 1, 2, 5, 10 ) ); $number[0] = mt_rand(2, 10); $number[1] = $divide[$number[0]][mt_rand(0, count( $divide[$number[0]] ) - 1 )]; }elseif($rnd_input === 1){ $number[1] = mt_rand(1, 10); $number[0] = $number[1] * mt_rand(1, $divide[$number[1]]); }elseif($rnd_input === 2){ $number[2] = mt_rand(1, 10 ); $number[0] = $number[2] * mt_rand(1, $divide[$number[2]]); $number[1] = (int) ($number[0] / $number[2]); } if(! isset( $number[2] ) ) $number[2] = (int) ($number[0] / $number[1]); break; } // Are we to display in words ? if(!empty($loginizer['captcha_words'])){ if($rnd_input === 0){ $number[1] = loginizer_cap_num_to_words( $number[1] ); $number[2] = loginizer_cap_num_to_words( $number[2] ); }elseif($rnd_input === 1){ $number[0] = loginizer_cap_num_to_words( $number[0] ); $number[2] = loginizer_cap_num_to_words( $number[2] ); }elseif($rnd_input === 2){ $number[0] = loginizer_cap_num_to_words( $number[0] ); $number[1] = loginizer_cap_num_to_words( $number[1] ); } } // Finally make the input field if(in_array( $form, array( 'default' ) ) ){ // As per the position of the empty input if($rnd_input === 0 ){ $return = $input . ' ' . $number[3] . ' ' . loginizer_cap_encode_op( $number[1] ) . ' = ' . loginizer_cap_encode_op( $number[2] ); }elseif($rnd_input === 1 ){ $return = loginizer_cap_encode_op( $number[0] ) . ' ' . $number[3] . ' ' . $input . ' = ' . loginizer_cap_encode_op( $number[2] ); }elseif($rnd_input === 2 ){ $return = loginizer_cap_encode_op( $number[0] ) . ' ' . $number[3] . ' ' . loginizer_cap_encode_op( $number[1] ) . ' = ' . $input; } } // Get the session ID $session_id = loginizer_cap_session_key(); // Save the time set_transient('lz_math_cap_'.$session_id, sha1(AUTH_KEY . $number[$rnd_input] . $session_id, false), $loginizer['captcha_time']); // Save the value in the users cookie //loginizer_cap_cookie_set(sha1(AUTH_KEY . $number[$rnd_input] . $session_id, false)); // In some themes the input field does not look fine if it is not in a div it used to take full page height for the input text tag $return = '
'.$return.'
'; return $return; } // Captcha form for ecommerce function loginizer_cap_form_ecommerce($return = false, $id = ''){ return loginizer_cap_form($return, $id, 'ecommerce'); } // Captcha form for login function loginizer_cap_form_login($return = false, $id = ''){ return loginizer_cap_form($return, $id, 'login'); } // Captcha form for comments/social function loginizer_cap_form_social($return = false, $id = ''){ return loginizer_cap_form($return, $id, 'social'); } // Shows the captcha function loginizer_cap_form($return = false, $id = '', $page_type = 'login'){ global $loginizer; // Math Captcha if(!empty($loginizer['captcha_no_google'])){ // We generate it only once if(empty($GLOBALS['lz_captcha_no_google'])){ $GLOBALS['lz_captcha_no_google'] = $loginizer['captcha_text'].'
'.loginizer_cap_phrase().'

'; } // Store this value $field = $GLOBALS['lz_captcha_no_google']; // hcaptcha }else if(!empty($loginizer['captcha_status']) && $loginizer['captcha_status'] === 3){ if (!wp_script_is('loginizer_hcaptcha_script', 'registered')) { wp_register_script('loginizer_hcaptcha_script', 'https://js.hcaptcha.com/1/api.js', ['jquery'], 1, ['strategy' => 'defer']); } wp_enqueue_script('loginizer_hcaptcha_script'); $field = '
'; // Cloudflare Turnstile } elseif(!empty($loginizer['captcha_status']) && $loginizer['captcha_status'] === 4){ $do_multiple = false; if(!wp_script_is('loginizer_turnstil_script', 'registered')){ wp_register_script('loginizer_turnstil_script', 'https://challenges.cloudflare.com/turnstile/v0/api.js', ['jquery'], 0, ['strategy' => 'defer']); } wp_enqueue_script('loginizer_turnstil_script'); $field = '
'; // Google reCaptcha } else { $field = ''; $query_string = array(); $captcha_type = (!empty($loginizer['captcha_type']) ? $loginizer['captcha_type'] : ''); $site_key = $loginizer['captcha_key']; $theme = $loginizer['captcha_theme']; $size = $loginizer['captcha_size']; $no_js = $loginizer['captcha_no_js']; $captcha_ver = 2; $captcha_js_ver = '2.0'; $invisible = 0; if($captcha_type == 'v3'){ $invisible = 1; $captcha_ver = 3; $captcha_js_ver = '3.0'; $do_multiple = 1; $lz_cap_div_class = 'lz-recaptcha-invisible-v3'; if(!empty($site_key)){ $query_string['render'] = $site_key; } } // For v2 invisible if($captcha_type == 'v2_invisible'){ $invisible = 1; $do_multiple = 1; $size = 'invisible'; $lz_cap_div_class = 'lz-recaptcha-invisible-v2'; $query_string['render'] = 'explicit'; } // Is this a first call ? if(!wp_script_is('loginizer_cap_script', 'registered')){ $language = $loginizer['captcha_lang']; if(!empty($language)){ $query_string['hl'] = $language; } // We need these variables in JS if(!empty($invisible)){ $field .= ''; } wp_register_script('loginizer_cap_script', "https://".$loginizer['captcha_domain']."/recaptcha/api.js?".http_build_query($query_string), array('jquery'), $captcha_js_ver, true); // We need to load multiple times }else{ $do_multiple = 1; } if(!empty($do_multiple)){ if(!wp_script_is('loginizer_multi_cap_script', 'registered')){ wp_register_script('loginizer_multi_cap_script', LOGINIZER_PRO_DIR_URL.'/assets/js/multi-recaptcha.js', array('jquery'), $captcha_js_ver, true); } wp_enqueue_script('loginizer_multi_cap_script'); } wp_enqueue_script('loginizer_cap_script'); // For v3 everything is done in javascript if(empty($invisible)){ $field .= "
"; if($no_js == 1){ $field .= " "; } $field .= '
'; }else{ $field .= '
'; if($captcha_ver == 3){ $field .= ''; } } } // Are we to return the code ? if($return){ return $field; // Lets echo it }else{ echo $field; } } // Verifies the Google Captcha and is called by individual for verifiers function loginizer_cap_verify(){ global $loginizer; // WooCommerce is calling this function as well. Hence Captcha fails if(isset($GLOBALS['called_loginizer_cap_verify'])){ return $GLOBALS['called_loginizer_cap_verify']; } // Is the post set ? if(count($_POST) < 1){ return true; } // Some plugin allows to login via Google account does not post the captcha details but does add ONLY rememberme to POST causing the captcha validation to trigger if(count($_POST) == 1 && isset($_POST['rememberme'])){ return true; } $GLOBALS['called_loginizer_cap_verify'] = true; // Math Captcha if(!empty($loginizer['captcha_no_google'])){ $response = (int) (!empty($_POST['loginizer_cap_math']) ? $_POST['loginizer_cap_math'] : ''); // Is the response valid ? if(!is_numeric($response) || empty($response)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } // Get the session ID $session_id = loginizer_cap_session_key(); // Is the response valid ? if(empty($session_id)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } // Get the Value stored $captcha_value = get_transient('lz_math_cap_'.$session_id); // Do we have a stored value ? if(empty($captcha_value) || strlen($captcha_value) != 40){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } // Is the value matching if($captcha_value != sha1(AUTH_KEY . $response . $session_id, false)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } return true; // hcaptcha }else if(!empty($loginizer['hcaptcha_sitekey']) && !empty($loginizer['captcha_status']) && $loginizer['captcha_status'] == 3){ if(empty($loginizer['hcaptcha_sitekey'])){ return true; } $hresponse = (!empty($_POST['h-captcha-response']) ? $_POST['h-captcha-response'] : ''); $ip = lz_getip(); // Is the IP or response not there ? if(empty($hresponse) || empty($ip)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } $url = 'https://hcaptcha.com/siteverify'; // Verify the post $req = wp_remote_post($url, [ 'timeout' => 10, 'body' => [ 'secret' => $loginizer['hcaptcha_secretkey'], 'response' => $hresponse, 'remoteip' => $ip, ] ] ); // Was there an error posting ? if(is_wp_error($req)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } // Process the post response $resp = wp_remote_retrieve_body($req); // Is the body valid if(empty($resp)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } $json = json_decode($resp, true); if(!empty($json['success'])){ return true; } // Google Captcha } elseif(!empty($loginizer['turn_captcha_secret']) && !empty($loginizer['captcha_status']) && $loginizer['captcha_status'] == 4){ $response = (!empty($_POST['cf-turnstile-response']) ? $_POST['cf-turnstile-response'] : ''); $ip = lz_getip(); // Is the IP or response not there ? if(empty($response) || empty($ip)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } $url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'; $req = wp_remote_post($url, [ 'timeout' => 10, 'body' => [ 'response' => $response, 'secret' => $loginizer['turn_captcha_secret'], 'remoteip' => $ip ] ]); if(is_wp_error($req)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } // Process the post response $resp = wp_remote_retrieve_body($req); // Is the body valid if(empty($resp)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } $json = json_decode($resp, true); if(!empty($json['success'])){ return true; } }else{ // If secret key is not there, return if(empty($loginizer['captcha_secret'])){ return true; } $response = (!empty($_POST['g-recaptcha-response']) ? $_POST['g-recaptcha-response'] : ''); $ip = lz_getip(); // Is the IP or response not there ? if(empty($response) || empty($ip)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } $url = 'https://'.$loginizer['captcha_domain'].'/recaptcha/api/siteverify'; // Verify the post $req = wp_remote_post($url, [ 'timeout' => 10, 'body' => [ 'secret' => $loginizer['captcha_secret'], 'response' => $response, 'remoteip' => $ip ] ] ); // Was there an error posting ? if(is_wp_error($req)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } // Process the post response $resp = wp_remote_retrieve_body($req); // Is the body valid if(empty($resp)){ $GLOBALS['called_loginizer_cap_verify'] = false; return false; } $json = json_decode($resp, true); if(!empty($json['success'])){ return true; } } // Couldnt verify $GLOBALS['called_loginizer_cap_verify'] = false; return false; } function loginizer_wc_before_checkout_process(){ global $loginizer; // This is checkout page. If admin has disabled captcha on checkout // and the user is registering during checkout we have not displayed the captcha form so we should not verify the same if(empty($loginizer['captcha_wc_checkout'])){ remove_filter('woocommerce_registration_errors', 'loginizer_cap_register_verify'); } } //========================================= // Registration Domain Blacklist //========================================= function loginizer_domains_blacklist($errors, $username, $email){ global $wpdb, $loginizer, $lz_error; $domains = $loginizer['domains_blacklist']; $domains = is_array($domains) ? $domains : array(); // Are you blacklisted ? foreach($domains as $domain_to_match){ $domain_to_match = str_replace('*', '(.*?)', $domain_to_match); if(preg_match('/'.$domain_to_match.'$/is', $email)){ $match_found = 1; } } // Did we get a match ? if(!empty($match_found)){ $errors->add('loginizer_domains_blacklist_error', 'The domain of your email is banned from registering on this website', 'loginizer_domains_blacklist'); } return $errors; } //========================================= // 2 Factor Auth / Question based security //========================================= // Handle the users secondary login i.e. 2fa / question, etc. function loginizer_user_redirect($user, $username, $password){ global $loginizer; // Is the post set ? if(count($_POST) < 1){ return $user; } //print_r($user);die(); // Is it a valid user ? if(!is_a($user, 'WP_User')){ return $user; } // The user has given correct details // Now does the user have any of our features enabled ? $settings = get_user_meta($user->ID, 'loginizer_user_settings', true); //print_r($settings);die(); // Is it applicable as per role if(!loginizer_is_2fa_applicable($user)){ return $user; } if(loginizer_is_whitelisted_2fa()){ return $user; } // Set the default return to the user only $ret = $user; // Is it a secondary question ? if(!empty($settings['pref']) && $settings['pref'] == 'question'){ // Is there a question and answer if(!empty($settings['question']) && !empty($settings['answer'])){ $save = 1; } } // Is it a 2fa via App ? if(!empty($settings['pref']) && $settings['pref'] == '2fa_app'){ if(!empty($settings['app_enable'])){ $save = 1; } } // Is it a 2fa via email ? if((!empty($settings['pref']) && $settings['pref'] == '2fa_email') || ((empty($settings['pref']) || @$settings['pref'] == 'none') && !empty($loginizer['2fa_email_force'])) ){ // Generate a 6 digit code $otp = wp_rand(100000, 999999); $r['code'] = base64_encode($otp); // Email them $site_name = get_bloginfo('name'); $first_name = get_user_meta($user->ID, 'first_name', true); $last_name = get_user_meta($user->ID, 'last_name', true); if(empty($first_name)){ $first_name = $user->data->display_name; } $vars = array('email' => $user->data->user_email, 'otp' => $otp, 'site_name' => $site_name, 'site_url' => get_site_url(), 'display_name' => $user->data->display_name, 'user_login' => $user->data->user_login, 'first_name' => $first_name, 'last_name' => $last_name); $subject = lz_lang_vars_name($loginizer['2fa_email_sub'], $vars); $message = lz_lang_vars_name($loginizer['2fa_email_msg'], $vars); //echo $user->data->user_email.'
'.$message;die(); $sent = wp_mail($user->data->user_email, $subject, $message); if(empty($sent)){ // For plugins that login using AJAX if(!empty(get_transient('loginizer_2fa_'. $user->ID))){ return array('message' => esc_html__('There was a problem sending your email with the OTP. Please try again or contact an admin.', 'loginizer')); } return new WP_Error('email_not_sent', 'There was a problem sending your email with the OTP. Please try again or contact an admin.', 'loginizer_2fa_email'); }else{ $save = 1; } } // Are we to create and save a token ? if(!empty($save)){ // Are we to be remembered ? $r['rememberme'] = lz_optreq('rememberme'); // Create a token $token = loginizer_user_token($user->ID, $r); // For custom redirect on Login when 2FA is enabled $custom_redirects = get_option('loginizer_2fa_custom_redirect'); if(!empty($custom_redirects) && !empty($user->roles)){ foreach($user->roles as $role){ if(!empty($custom_redirects[$role]) && wp_validate_redirect($custom_redirects[$role])){ $_REQUEST['redirect_to'] = wp_validate_redirect($custom_redirects[$role]); break; } } } $redirect_to = ''; if(!empty($_REQUEST['redirect_to'])){ $redirect_to = '&redirect_to='.urlencode($_REQUEST['redirect_to']); } elseif(!empty($_REQUEST['redirect'])){ $redirect_to = '&redirect_to='.urlencode($_REQUEST['redirect']); } elseif(!empty(wp_validate_redirect($_SERVER['HTTP_REFERER']))){ $redirect_to = '&redirect_to='.urlencode(wp_validate_redirect($_SERVER['HTTP_REFERER'])); } // Form the URL $url = wp_login_url().'?action=loginizer_security&uid='.$user->ID.'&lutoken='.$token.$redirect_to.(isset( $_REQUEST['interim-login'] ) ? '&interim-login=1' : '').(!empty($_SERVER['IS_WPE']) ? '&wpe-login=true' : ''); // For plugins that login using AJAX if(!empty(get_transient('loginizer_2fa_'. $user->ID))){ return $url; } loginizer_update_attempt_stats(1); // Lets redirect wp_safe_redirect($url); die(); } return $ret; } // Creates a one time token function loginizer_user_token($uid = 0, $r = array()){ global $loginizer; // Variables $time = time(); $expires = ($time + 600); $action = 'loginizer_user_token'; include_once( ABSPATH.'/'.$loginizer['wp-includes'].'/class-phpass.php'); $wp_hasher = new PasswordHash(8, TRUE); // Create the token with a random salt and the time $token = wp_hash(wp_generate_password(20, false).$action.$time); // Create a hash of the token $r['stored_hash'] = $wp_hasher->HashPassword($expires.$token); $r['expires'] = $expires; // Store the hash and when it expires update_user_meta($uid, $action, $r); return $token; } // Process the secondary form i.e. question / 2fa, etc. function loginizer_user_security(){ global $loginizer, $lz, $lz_error, $interim_login; if(empty($_GET['uid']) || empty($_GET['lutoken'])){ return false; } $uid = (int) sanitize_key($_GET['uid']); $token = sanitize_key($_GET['lutoken']); $action = 'loginizer_user_token'; $meta = get_user_meta($uid, $action, true); $hash = !empty($meta['stored_hash']) ? $meta['stored_hash'] : ''; $expires = !empty($meta['expires']) ? $meta['expires'] : ''; include_once(ABSPATH.'/'.$loginizer['wp-includes'].'/class-phpass.php'); $wp_hasher = new PasswordHash(8, TRUE); $time = time(); if(!$wp_hasher->CheckPassword($expires.$token, $hash) || $expires < $time){ // Throw an error $lz['error'] = sprintf(__('The token is invalid or has expired. Please provide your user details by clicking here', 'loginizer'), wp_login_url()); loginizer_user_security_form(); } // Get the username $userdata = get_userdata($uid); $username = $userdata->data->user_login; // Load the settings $lz['settings'] = get_user_meta($uid, 'loginizer_user_settings', true); // If the user was just created and the settings is empty if(empty($lz['settings'])){ $lz['settings'] = array(); } if((empty($lz['settings']['pref']) || $lz['settings']['pref'] == 'none') && !empty($loginizer['2fa_email_force'])){ $lz['settings']['pref'] = '2fa_email'; } /* Make sure post was from this page */ if(count($_POST) > 0 && !check_admin_referer('loginizer-enduser')){ $lz['error'] = __('The form security was compromised !', 'loginizer'); loginizer_user_security_form(); } // Has the user reached max attempts ? if(!loginizer_can_login()){ $lz['error'] = $lz_error; loginizer_user_security_form(); } $interim_login = isset( $_REQUEST['interim-login'] ); // Process the post if(!empty($_POST['lus_submit'])){ if(@$lz['settings']['pref'] == 'question'){ // Is there an answer ? $answer = lz_optpost('lus_value'); // Is the answer correct ? if($answer != @base64_decode($lz['settings']['answer'])){ loginizer_login_failed($username.' | 2FA-Answer', 1); $lz['error'][] = __('The answer is wrong !', 'loginizer'); $lz['error'][] = loginizer_retries_left(); loginizer_user_security_form(); // Login the user }else{ $do_login = 1; } } if(@$lz['settings']['pref'] == '2fa_email'){ // Is there an OTP ? $otp = lz_optpost('lus_value'); // Is the answer correct ? if($otp != @base64_decode($meta['code'])){ loginizer_login_failed($username.' | 2FA-Email', 1); $lz['error'][] = __('The OTP is wrong !', 'loginizer'); $lz['error'][] = loginizer_retries_left(); loginizer_user_security_form(); // Login the user }else{ $do_login = 1; } } // App based login if(@$lz['settings']['pref'] == '2fa_app'){ // Is there an OTP ? $otp = lz_optpost('lus_value'); $app2fa = loginizer_2fa_app($uid); // Is the answer correct ? if($otp != $app2fa['2fa_otp']){ // Maybe its an Emergency OTP if(empty($lz['settings']['2fa_emergency']) || !@in_array($otp, $lz['settings']['2fa_emergency'])){ loginizer_login_failed($username.' | 2FA-APP', 1); $lz['error'][] = __('The OTP is wrong !', 'loginizer'); $lz['error'][] = loginizer_retries_left(); loginizer_user_security_form(); }else{ // Remove the Emergency used and save the rest unset($lz['settings']['2fa_emergency'][$otp]); // Save it update_user_meta($uid, 'loginizer_user_settings', $lz['settings']); $do_login = 1; } // Login the user }else{ $do_login = 1; } } // Are we to login ? if(!empty($do_login)){ $remember_me = !empty($meta['rememberme']) ? true : false; // Login the User wp_set_auth_cookie($uid, $remember_me); // Delete the meta delete_user_meta($uid, $action); delete_transient('loginizer_2fa_' . $uid); // it is generated when the user logins through some ajax form $redirect_to = !empty($_REQUEST['redirect_to']) ? $_REQUEST['redirect_to'] : admin_url(); // Redirect and exit // Interim Login is used when session times out due to inactivity and login form is opened in a popup iframe if ( $interim_login ) { $message = '

' . __( 'You have logged in successfully.', 'loginizer' ) . '

'; $interim_login = 'success'; login_header( '', $message ); ?>
'; foreach($lz['error'] as $ek => $error_txt){ echo wp_kses($error_txt, NULL).'
'; } echo ''; } if(!empty($lz['settings'])){ echo '
'; wp_nonce_field('loginizer-enduser'); // Are we to ask a question if(@$lz['settings']['pref'] == 'question'){ echo '

'.$loginizer['2fa_msg']['otp_question'].' :

'.$lz['settings']['question'].'


'; } // Its a 2fa email if(@$lz['settings']['pref'] == '2fa_email'){ echo '

'.$loginizer['2fa_msg']['otp_email'].'


'; } // Its a 2fa app ? if(@$lz['settings']['pref'] == '2fa_app'){ echo '

'.$loginizer['2fa_msg']['otp_app'].'


'; } echo '

'; } // Focus on the field login_footer('lus_value'); exit(); } // Show the 2fa Notice function loginizer_2fa_notice(){ echo '
Dismiss Forever

'.__('The site admin has enabled Two Factor Authentication features to secure your account.
For your safety, you must setup your login security preferences.', 'loginizer').'

'.__('Setup My Security Settings', 'loginizer').' '.__('Remind me later', 'loginizer').'

'; } // Shows the user menu to all users function loginizer_user_menu(){ add_menu_page(__('My Loginizer Security Settings', 'loginizer'), __('My Security', 'loginizer'), 'read', 'loginizer_user', 'loginizer_user_page', '', 72); } // Generates the 2FA as seen in the APP function loginizer_2fa_app_key($settings, $length = 6, $counter = 0){ $key = $settings['2fa_key']; $type = (empty($settings['2fa_type']) ? 'totp' : $settings['2fa_type']); if($type == 'hotp'){ $stored_in_db = 1; $counter = !empty($counter) ? $counter : $stored_in_db; $res = \Loginizer\HOTP::generateByCounter($key, $counter); }else{ $time = !empty($counter) ? $counter : time(); $res = \Loginizer\HOTP::generateByTime($key, 30, $time); } return $res->toHotp($length); } // Returns the 2fa_app data. Is also used during ajax function loginizer_2fa_app($uid = 0){ // Include necessary stuff include_once(LOGINIZER_PRO_DIR.'/lib/hotp.php'); include_once(LOGINIZER_PRO_DIR.'/lib/Base32.php'); $uid = empty($uid) ? get_current_user_id() : $uid; $user = get_user_by('id', $uid); // For 2fa_app we must be prepared $tmpkey = get_user_meta($uid, 'loginizer_user_2fa_tmpkey', true); $settings['2fa_key'] = empty($tmpkey) ? '' : base64_decode($tmpkey);// Just decode it // We might need to create a 10 char secret KEY for 2fa App based if(empty($settings['2fa_key']) || isset($_REQUEST['reset_2fa_key'])){ // Generate $settings['2fa_key'] = strtoupper(lz_RandomString(10)); // Save the new one update_user_meta($uid, 'loginizer_user_2fa_tmpkey', base64_encode($settings['2fa_key'])); } // Base32 Key $settings['2fa_key_32'] = \Loginizer\Base32::encode($settings['2fa_key']); // The QR Code text $url = preg_replace('/^https?:\/\//', '', site_url()); $site_name = get_bloginfo('name'); $settings['2fa_qr'] = 'otpauth://'.(empty($settings['2fa_type']) ? 'totp' : $settings['2fa_type']).'/'.rawurlencode($url).':'.@$user->user_login.'?secret='.\Loginizer\Base32::encode($settings['2fa_key']).'&issuer='.rawurlencode($site_name).'&counter='; // Time now $settings['2fa_server_time'] = get_date_from_gmt(gmdate('Y-m-d H:i:s'), 'Y-m-d H:i:s'); // Current OTP $settings['2fa_otp'] = loginizer_2fa_app_key($settings); return $settings; } // Handles the users choice page POST function loginizer_user_page_post(&$error = array()){ global $loginizer, $loginizer_allowed; /* Make sure post was from this page */ if(count($_POST) > 0){ check_admin_referer('loginizer-user'); } $uid = get_current_user_id(); if(!empty($_POST['submit'])){ // What has the user selected ? $Nsettings['pref'] = lz_optpost('loginizer_user_choice'); if(empty($loginizer_allowed[$Nsettings['pref']])){ $error['lz_not_allowed'] = __('You have submitted an invalid preference', 'loginizer'); return 0; } // Process security question if($Nsettings['pref'] == 'question'){ $Nsettings['question'] = lz_optpost('lz_question'); $Nsettings['answer'] = lz_optpost('lz_answer'); // Was there a question ? if(empty($Nsettings['question'])){ $error['lz_no_question'] = __('No question was submitted', 'loginizer'); } // Question too long ? if(strlen($Nsettings['question']) > 255){ $error['lz_question_long'] = __('The question is too long', 'loginizer'); } // Was there an answer ? if(empty($Nsettings['answer'])){ $error['lz_no_answer'] = __('No answer was submitted', 'loginizer'); } // Question too long ? if(strlen($Nsettings['answer']) > 255){ $error['lz_answer_long'] = __('The answer is too long', 'loginizer'); } if(!empty($error)){ return 0; } // Hash the answer $Nsettings['answer'] = base64_encode($Nsettings['answer']); } // Process 2fa via Email if($Nsettings['pref'] == '2fa_email'){ // Actually nothing to store ! } // Process 2fa via App if($Nsettings['pref'] == '2fa_app'){ // Enable APP $Nsettings['app_enable'] = (int) lz_optpost('lz_app_enable'); // Any one time passwords ? $emergency = lz_optpost('lz_2fa_emergency'); // Is there any Emergency OTP if(!empty($emergency)){ $emergency = explode(',', $emergency); // Loop through and correct foreach($emergency as $ek => $ev){ $orig = $ev; $ev = (int) $ev; $_emergency[$ev] = $ev; if(strlen($ev) != 6){ $incorrect[] = $orig; } } if(!empty($incorrect)){ $error['lz_emergency'] = __('The emergency code(s) are incorrect', 'loginizer').' : '.implode(', ', $incorrect); } $Nsettings['2fa_emergency'] = $_emergency; } if(!empty($error)){ return 0; } } // Lets save the settings update_user_meta($uid, 'loginizer_user_settings', $Nsettings); return 1; } } // Loginizer 2FA User settings loader function loginizer_load_user_settings(&$uid, &$user, &$settings, &$current_pref){ $uid = get_current_user_id(); $user = wp_get_current_user();//print_r($user); $settings = get_user_meta($uid, 'loginizer_user_settings', true); $settings = empty($settings) ? array() : $settings; $current_pref = !empty($settings['pref']) ? $settings['pref'] : ''; $current_pref = empty($current_pref) ? '' : $current_pref; } function loginizer_is_whitelisted_2fa(){ global $wpdb, $loginizer, $lz_error; $whitelist = $loginizer['2fa_whitelist']; foreach($whitelist as $k => $v){ // Is the IP in the blacklist ? if(inet_ptoi($v['start']) <= inet_ptoi($loginizer['current_ip']) && inet_ptoi($loginizer['current_ip']) <= inet_ptoi($v['end'])){ $result = 1; break; } // Is it in a wider range ? if(inet_ptoi($v['start']) >= 0 && inet_ptoi($v['end']) < 0){ // Since the end of the RANGE (i.e. current IP range) is beyond the +ve value of inet_ptoi, // if the current IP is <= than the start of the range, it is within the range // OR // if the current IP is <= than the end of the range, it is within the range if(inet_ptoi($v['start']) <= inet_ptoi($loginizer['current_ip']) || inet_ptoi($loginizer['current_ip']) <= inet_ptoi($v['end'])){ $result = 1; break; } } } // You are whitelisted if(!empty($result)){ return true; } return false; } // If 2FA is ON and there are roles, then is 2FA applicable to the user function loginizer_is_2fa_applicable($user = array()){ global $loginizer; // If roles is empty then its applicable to all if(empty($loginizer['2fa_roles'])){ return true; } // Are there any roles we need to check if(!empty($loginizer['2fa_roles'])){ foreach($loginizer['2fa_roles'] as $role => $v){ if(in_array($role, $user->roles)){ return true; } } } return false; } // The settings page shown to users function loginizer_user_page(){ global $loginizer, $loginizer_allowed; $loginizer_allowed = array(); $loginizer_allowed['none'] = 1; if(!empty($loginizer['2fa_app'])){ $loginizer_allowed['2fa_app'] = 1; } if(!empty($loginizer['2fa_email'])){ $loginizer_allowed['2fa_email'] = 1; } if(!empty($loginizer['2fa_sms'])){ $loginizer_allowed['2fa_sms'] = 1; } if(!empty($loginizer['question'])){ $loginizer_allowed['question'] = 1; } //------------------ // Process the form //------------------ $error = array(); $saved = loginizer_user_page_post($error); //------------------ // Load Settings //------------------ loginizer_load_user_settings($uid, $user, $settings, $current_pref); $app2fa = loginizer_2fa_app(); //------------------ // Show the Page //------------------ echo '

'.__('Loginizer Security Settings', 'loginizer').'

'; if(!empty($saved)){ echo '

'.__('Settings saved.', 'loginizer').'

'; } if(!empty($error)){ lz_report_error($error); } if(is_admin()){ echo '

'.__('These are your personal security and login settings and will not affect other users.', 'loginizer').'

'; }else{ if(class_exists('WooCommerce')){ echo ''; } } if (current_user_can('manage_options')) { echo '

'._e('You should also bookmark the FAQs, which explain how to de-activate the plugin even if you cannot log in.', 'loginizer').'

'; } wp_enqueue_script('jquery-qrcode', LOGINIZER_PRO_DIR_URL.'/assets/js/jquery.qrcode.min.js', array('jquery'), '0.12.0'); // Give the user the drop down to choose the settings echo 'Choose Preference :
'.wp_nonce_field('loginizer-user').'

'.__('App based Two Factor Auth Code Settings', 'loginizer').'

'.__('NOTE : Generating two-factor codes depends upon your web-server and your device agreeing upon the time.', 'loginizer').'
'.__('The current UTC time according to this server when this page loaded', 'loginizer').': '.$app2fa['2fa_server_time'].'

Enable :
Secret Key :
Reset Secret Key
Secret Key (Base32) :
Used by Google Authenticator, Authy, etc.
'.__('One Time Emergency Codes', 'loginizer').' :
'.__('(Optional) You can specify 6 digit emergency codes seperated by a comma. Each can be used only once. You can specify upto 10.', 'loginizer').'


'.__('If you enable app based Two Factor Auth, then verify that your application is showing the same One Time Password (OTP) as shown on this page before you log out.', 'loginizer').'
'.__('Current OTP', 'loginizer').' :
'.__('Refresh', 'loginizer').'

'.loginizer_2fa_app_key($app2fa).'

QR Code :

'.__('Security Question Settings', 'loginizer').'

'.__('A secondary question set by you will be asked on a successful login', 'loginizer').'

Question :
Answer :
Is case sensitive

'.__('Email Two Factor Auth Code Settings', 'loginizer').'

'.__('A One Time Password (OTP) will be asked on a successful login. The OTP will be emailed to your email address', 'loginizer').' :

'.$user->data->user_email.'

'.__('SMS Two Factor Auth Code Settings', 'loginizer').'

'.__('A One Time Password (OTP) will be asked on a successful login. The OTP will be sent via SMS to your mobile.', 'loginizer').'

'.__('Mobile Number', 'loginizer').' :
'; //WooCommerce Loginizer Security Settings - Switch between the normal submit button and the submit button that's only available in the admin panel if ( ! is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) { echo '
'; }else{ submit_button(); } echo '
'; } // Do the checksum of the core function loginizer_checksums(){ global $loginizer, $loginizer_allowed, $wp_local_package; // Update the time update_option('loginizer_checksums_last_run', time()); // Get the locale and version $version = $GLOBALS['wp_version']; $locale = 'en_US'; if(!empty($wp_local_package)){ $locale = $wp_local_package; } //echo $version.' - '.$locale; // Load the checksums $resp = wp_remote_get('https://api.wordpress.org/core/checksums/1.0/?version='.$version.'&locale='.$locale, array('timeout' => 10)); //lz_print($resp); if(!is_array($resp)){ return false; } $checksums = json_decode($resp['body'], true);//lz_print($checksums); $checksums = $checksums['checksums']; //lz_print($checksums); // WP-content could be renamed ! $wp_content = basename(dirname(dirname(dirname(__FILE__)))); $diffed = array(); // Loop through and check foreach($checksums as $ck => $md5){ // Do not check for plugins and themes as some user might not have updated files if(substr($ck, 0, 18) == 'wp-content/plugins'){ continue; } if(substr($ck, 0, 17) == 'wp-content/themes'){ continue; } if(substr($ck, 0, 20) == 'wp-content/languages'){ continue; } if(in_array($ck, array('readme.html', 'license.txt', 'wp-config-sample.php'))){ continue; } if(substr($ck, 0, 10) == 'wp-content'){ $ck = substr_replace($ck, $wp_content, 0, 10); //echo $ck."\n"; } $path = lz_cleanpath(ABSPATH.'/'.$ck); // Skip checksum for the file that does not exists, it is possible that the theme/plugin is deleted by the user if(!file_exists($path) && preg_match('#/(themes|plugins)#is', $path)){ continue; } $cur_md5 = @md5_file($path); if($cur_md5 != $md5){ $diffed[$ck]['cur_md5'] = $cur_md5; $diffed[$ck]['md5'] = $md5; } } //lz_print($diffed); // Store the diffed ones update_option('loginizer_checksums_diff', $diffed); // Load any ignored files $ignores = get_option('loginizer_checksums_ignore'); // Create a final diff list to email the admin if(is_array($ignores)){ foreach($ignores as $ck => $path){ unset($diffed[$path]); } } // Send an email to the admin, IF we are to email if(!empty($diffed) && is_array($diffed) && count($diffed) > 0 && empty($loginizer['no_checksum_email'])){ // Send the email $site_name = get_bloginfo('name'); $email = lz_is_multisite() ? get_site_option('admin_email') : get_option('admin_email'); $subject = __("File Checksum Mismatch - $site_name", 'loginizer'); $message = "Hi, Loginizer has just completed checking the MD5 checksums of your WordPress site : $site_name - ".get_site_url()." The following files have been found that do not match the MD5 checksums as per your version : "; foreach($diffed as $path => $val){ $message .= "Path: ".$path." Expected MD5: ".$val['md5']." Found MD5: ".$val['cur_md5']." "; } $message .= " It is recommended you check this ASAP and download the files again to replace them. If you are aware of modifications made to the above files, please update the Ignored files list in Loginizer. Regards, $site_name"; //echo $message; $sent = wp_mail($email, $subject, $message); } } // Is the Username blacklisted function loginizer_user_blacklisted($username){ global $wpdb, $loginizer, $lz_error; $username_blacklist = $loginizer['username_blacklist']; $username_blacklist = is_array($username_blacklist) ? $username_blacklist : array(); // Are you blacklisted ? foreach($username_blacklist as $user_to_match){ $user_to_match = str_replace('*', '(.*?)', $user_to_match); if(preg_match('/^'.$user_to_match.'$/is', $username)){ $match_found = 1; } } // Did we get a match ? if(empty($match_found)){ return false; } // Lets make sure there is no username in the database by that name $user_search = get_user_by('login', $username); // If not found then search by email if(!empty($user_search)){ return false; } $blacklist = get_option('loginizer_blacklist'); $newid = ( empty($blacklist) ? 0 : max(array_keys($blacklist)) ) + 1; // Add to the blacklist $blacklist[$newid]['start'] = lz_getip(); $blacklist[$newid]['end'] = lz_getip(); $blacklist[$newid]['time'] = time(); // Update the database update_option('loginizer_blacklist', $blacklist); // Reload $loginizer['blacklist'] = get_option('loginizer_blacklist'); // Show the error $lz_error['user_blacklisted'] = __('This username has been blacklisted, and so have you been blacklisted !', 'loginizer'); return true; } function loginizer_plugin_row_links($pluginMeta, $pluginFile){ global $loginizer; $isRelevant = $pluginFile == 'loginizer-security/loginizer-security.php'; if (!empty($isRelevant) && current_user_can('update_plugins')){ // Show the Renew License link if the license is expired if(!empty($loginizer['license']['expires']) && ($loginizer['license']['expires'] <= date('Ymd'))){ $linkUrl = 'https://www.softaculous.com/clients?ca=loginizer_buy&plan='.$loginizer['license']['plan'].'&license='.$loginizer['license']['license']; $linkText = __('Renew License', 'loginizer'); $pluginMeta[] = sprintf('%s', esc_attr($linkUrl), $linkText); } } return $pluginMeta; } // Check if any one 2FA option is enabled by admin function loginizer_is_2fa_enabled(){ global $loginizer; return (!empty($loginizer['2fa_app']) || !empty($loginizer['2fa_email']) || !empty($loginizer['2fa_sms']) || !empty($loginizer['question'])); } // Add TFA Settings Column to the Users Table function loginizer_2fa_columns_users($column){ if(loginizer_is_2fa_enabled()){ $column['tfa'] = __('Loginizer 2FA', 'loginizer'); } return $column; } // Update the users' selected 2FA preference function loginizer_2fa_column_data($val, $column_name, $user_id){ if(loginizer_is_2fa_enabled()){ switch($column_name){ case 'tfa' : $settings = get_user_meta($user_id, 'loginizer_user_settings', true); if(empty($settings) || empty($settings['pref']) || ($settings['pref'] == 'none')){ return __('None', 'loginizer'); } switch($settings['pref']){ case 'question': return __('Security Questions', 'loginizer'); break; case '2fa_app': return __('Google Authenticator / Authy', 'loginizer'); break; case '2fa_email': return __('Email Auth Code', 'loginizer'); break; case '2fa_sms': return __('SMS Auth Code', 'loginizer'); break; } default: } } return $val; } function loginizer_2fa_ajax_redirect(){ $url = get_transient('loginizer_2fa_' . get_current_user_id()); set_transient('loginizer_2fa_' . get_current_user_id(), '2fa', 600); wp_safe_redirect($url); die('Didn\'t redirect'); } // WooCommerce Loginizer My Security Page Functions - Start // These functions are required in order for the My Security page to be created in WooCommerce User section function loginizer_add_premium_security_endpoint(){ add_rewrite_endpoint( 'loginizer-security', EP_ROOT | EP_PAGES ); } // Add new query var function loginizer_premium_security_query_vars( $vars ) { $vars[] = 'loginizer-security'; return $vars; } // Insert the link into the My Account menu function loginizer_add_premium_security_link_my_account( $items ) { $items['loginizer-security'] = 'Security'; return $items; } // Rewrite rules function loginizer_woocommerce_rewrite_rule(){ add_rewrite_rule( 'loginizer-security(/(.*))?/?$', 'index.php?&loginizer-security=$matches[2]', 'top' ); add_rewrite_rule( '(.?.+?)/loginizer-security(/(.*))?/?$', 'index.php?pagename=$matches[1]&loginizer-security=$matches[3]', 'top' ); flush_rewrite_rules(); } //WooCommerce Loginizer Security Page Functions - End /********************************************* * CSRF Protection Session Functions - Starts **********************************************/ function loginizer_csrf_sess_init(){ global $loginizer; if(!is_user_logged_in()){ return; } $login_slug = 'wp-login.php'; if($loginizer['login_slug']){ $login_slug = $loginizer['login_slug']; } if(loginizer_cur_page() == $login_slug && strpos($_SERVER['REQUEST_URI'], 'action') === FALSE){ wp_logout(); return; } if(!is_admin()){ return; } preg_match('/(?:lzs.{20})/U', esc_url_raw($_SERVER['REQUEST_URI']), $matches); if(empty($matches)){ wp_redirect(wp_login_url()); die('Didn\'t had security protection'); } loginizer_csrf_verify_session($matches[0]); } // Destroys session for CSRF protection function loginizer_destroy_csrf_session($user_id){ delete_user_meta($user_id, 'loginizer_csrf_session'); $sess_key = get_user_meta($user_id, 'loginizer_csrf_session', true); @setCookie('lz_csrf_sess', $sess_key, time() - 3600, COOKIEPATH, COOKIE_DOMAIN, true); // deleting the cookie by setting past time } // Creates session for CSRF protection function loginizer_csrf_create_session($user_id){ if(!empty($_COOKIE['lz_csrf_sess']) && sanitize_key($_COOKIE['lz_csrf_sess']) == get_option('loginizer_session_'. $user_id) && !empty($_REQUEST['interim-login'])){ return; } $sess_key = loginizer_sess_key(); update_user_meta($user_id, 'loginizer_csrf_session', $sess_key); @setCookie('lz_csrf_sess', $sess_key, time() + DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, true); } // Verifies that the session is active function loginizer_csrf_verify_session($key) { $key = trim($key, '/'); $user_id = get_current_user_id(); $session_key = get_user_meta($user_id, 'loginizer_csrf_session', true); if(trim($session_key) != trim($key)){ wp_logout(); wp_redirect(wp_login_url()); die('Died'); } } // Checks if .htaccess file have the mod rewrite for CSRF protection function loginizer_is_csrf_prot_mod_set(){ $is_mod_set = get_transient('loginizer_csrf_mod_rewrite'); if(!empty($is_mod_set)){ return true; } $htaccess = file_get_contents(ABSPATH . '/.htaccess'); preg_match('/^# BEGIN Loginizer/m', $htaccess, $matches); if(empty($matches)){ return false; } // set transient to expire after 1 hour set_transient('loginizer_csrf_mod_rewrite', true, 3600); return true; } /* If the user is logged in and its session ends then if the user is redirected to login page then it has a redirect_to url, which has the old csrf session key in the redirect URL, this function removes that session key from the redirect_to parameter in the url */ function loginizer_csrf_wp_redirects($location){ global $loginizer; $admin_slug = 'wp-admin'; /* WordPress redirects to https://host/wp-login?redirect_to=VALUES if you are not logged in and enter the URL https://host/wp-admin so then if I tried to login, I was not able to get user id in anyway hence removing the redirect_to was the possible solution bcoz this dosent cause the same issue */ if(strpos($location, '?redirect_to') !== FALSE && strpos($location, 'lzs') === FALSE){ $location = preg_replace('/\?redirect_to=.*/', '', $location); return $location; } if(!empty($loginizer['admin_slug'])){ $admin_slug = $loginizer['admin_slug']; } // Return if it does not have CSRF key or is not admin url if(strpos($location, 'lzs') === FALSE || strpos($location, $admin_slug) === FALSE){ return $location; } // If the query with redirect_to has csrf key then remove that string $url = parse_url($location); if(empty($url['query'])){ return $location; } if(strpos($url['query'], 'redirect_to') === FALSE){ $location = preg_replace('/\?redirect_to=.*/', '', $location); return $location; } $query = rawurldecode($url['query']); $query = preg_replace('/'.$admin_slug.'-lzs.{20}/U', $admin_slug . '/', $query); $location = preg_replace('/\?.+/', '?' . rawurlencode($query), $location); return $location; } // Generates CSRF session string function loginizer_sess_key(){ return 'lzs' . wp_generate_password(20, false); } // Updates redirects with session string function loginizer_csrf_admin_redirects($url, $path, $scheme){ global $loginizer; $admin_slug = 'wp-admin'; if(!is_user_logged_in()){ return $url; } $user_id = get_current_user_id(); $session_key = get_user_meta($user_id, 'loginizer_csrf_session', true); if(strpos($url, 'lzs') !== FALSE){ return $url; } if(!empty($loginizer['admin_slug'])){ $admin_slug = $loginizer['admin_slug']; } $url = preg_replace('/(?:-?lzs.{20})/U', '/', $url); $url = str_replace($admin_slug, $admin_slug . '-' . $session_key, $url); return $url; } // Sets session on login redirect URL function loginizer_login_csrf_redirect($redirect, $request, $user){ global $loginizer; $admin_slug = 'wp-admin'; if(isset($user->roles) && is_array($user->roles)){ $session_key = get_user_meta($user->ID, 'loginizer_csrf_session', true); if(!empty($loginizer['admin_slug'])){ $admin_slug = $loginizer['admin_slug']; } $redirect = preg_replace('/(?:-?lzs.{20})/U', '/', $redirect); $redirect = str_replace($admin_slug, $admin_slug . '-' . $session_key, $redirect); } return $redirect; } // Updates the url if required with the CSRF string function loginizer_csrf_change_url($url, $uid){ global $loginizer; if(!empty($loginizer['enable_csrf_protection']) && loginizer_is_csrf_prot_mod_set()){ $admin_slug = 'wp-admin'; if(!empty($loginizer['admin_slug'])){ $admin_slug = $loginizer['admin_slug']; } $session_key = get_user_meta($uid, 'loginizer_csrf_session', true); if(strpos($url, $session_key) !== FALSE){ return $url; } $url = str_replace($admin_slug, $admin_slug . '-' . $session_key, $url); } return $url; } function loginizer_csrf_admin_bar_shortcut($admin_bar){ $admin_bar->add_node( array( 'id' => 'loginizer-admin-shortcut', 'parent' => null, 'group' => null, 'title' => esc_html__('Open New Tab', 'loginizer'), 'href' => admin_url(), 'meta' => [ 'title' => esc_html__('Opens Dashboard in new tab', 'loginizer'), 'target' => '_blank' ] ) ); } // CSRF Protection Session Functions - End // Restricts user from logging in function loginizer_limit_sessions($user){ if(is_wp_error($user)){ return $user; } $concurrent_sessions = get_option('loginizer_limit_session'); if(empty($concurrent_sessions) || empty($concurrent_sessions['enable']) || !class_exists('WP_Session_Tokens')){ return $user; } // Checks if we have excluded the role if(!empty($concurrent_sessions['roles']) && !empty($user->roles) && is_array($user->roles)){ if(!empty(array_intersect($concurrent_sessions['roles'], $user->roles))){ return $user; } } $concurrent_sessions['count'] = empty($concurrent_sessions['count']) ? $concurrent_sessions['count'] : 1; // if limit is not set then default it to 1 if(!empty($concurrent_sessions['type']) && $concurrent_sessions['type'] == 'block'){ $session = WP_Session_Tokens::get_instance($user->ID); $count = count($session->get_all()); if($count >= $concurrent_sessions['count']){ return new WP_Error('loginizer_session_limit', __('You have reached maximum number of concurrent logins please logout from other devices to access', 'loginizer')); } return $user; } return $user; } // Destroys session when concurrent limit is reached function loginizer_limit_destroy_sessions_handler($check, $password, $hash, $user_id){ if(empty($check)){ return false; } return loginizer_limit_destroy_sessions($user_id); } function loginizer_limit_destroy_sessions($user_id){ if(empty($user_id)){ return false; } if(!class_exists('WP_Session_Tokens')){ return true; } $user = get_userdata($user_id); $concurrent_sessions = get_option('loginizer_limit_session'); if(empty($concurrent_sessions) || empty($concurrent_sessions['enable'])){ return true; } // Checks if we have excluded the role if(!empty($concurrent_sessions['roles']) && !empty($user->roles) && is_array($user->roles)){ if(!empty(array_intersect($concurrent_sessions['roles'], $user->roles))){ return true; } } if(!empty($concurrent_sessions['type']) && $concurrent_sessions['type'] == 'destroy'){ $session = WP_Session_Tokens::get_instance($user_id); $count = count($session->get_all()); if($count >= $concurrent_sessions['count']){ $session->destroy_all(); } return true; } return true; } ////////////////////////// // BEGIN MasterStudy LMS ///////////////////////// // Logins user when it happens via MasterStudy LMS function loginizer_handle_stm_lms_login($res){ global $loginizer; if($res['status'] != 'success'){ return $res; } if(empty(loginizer_is_2fa_enabled())){ return $res; } $request_body = file_get_contents('php://input'); $data = json_decode($request_body, true); // wp_signon sanatizes the data so we don't need to do it here // Login the user again as this is the only way we can get user data which we need to set cookie and transient $user = wp_signon($data, is_ssl()); if(is_wp_error($user)){ wp_logout(); return array('status' => 'error', 'message' => 'Login Failed!'); } if(empty(loginizer_is_2fa_applicable($user))){ return $res; } $user_pref = get_user_meta($user->ID, 'loginizer_user_settings'); // If the user dosent have 2FA method set in My Security then we just let MasterStudy Work normally. if(empty($loginizer['2fa_email_force']) && (empty($user_pref) || (!empty($user_pref[0]['pref']) && $user_pref[0]['pref'] == 'none'))){ return $res; } $admin_slug = 'wp-admin'; if($loginizer['admin_slug']){ $admin_slug = $loginizer['admin_slug']; } @setCookie('loginizer_2fa_' . $user->ID, time(), time() + DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, true); set_transient('loginizer_2fa_' . $user->ID, time(), 600); $message = __('Redirecting you to verify 2FA', 'loginizer'); $_POST['redirect_to'] = class_exists('STM_LMS_User') ? STM_LMS_User::user_page_url($user->ID, true) : admin_url(); $_REQUEST['redirect_to'] = $_POST['redirect_to']; $url = loginizer_user_redirect($user, '', ''); if(empty($url) || is_a($url, 'WP_User') || filter_var($url, FILTER_VALIDATE_URL) === FALSE){ $message = esc_html__('Something went wrong unable to redirect you to verify your login via 2fa', 'loginizer'); if(is_array($url) && !empty($url['message'])){ $message = $url['message']; } $url = ''; $res['status'] = 'error'; wp_logout(); } else { set_transient('loginizer_2fa_' . $user->ID, $url, 600); } $res['message'] = $message; $res['user_page'] = $url; return $res; } // END MasterStudy LMS // Single Sign ON function loginizer_create_sso($uid, $ttl = 600, $attempts = 1){ $token = loginizer_generate_ssotoken($uid, $ttl, $attempts); $url = wp_login_url().'?uid='.$uid.'&ssotoken='.$token; $sso_links = get_option('loginizer_sso_links', []); $sso_links[$uid] = $url; update_option('loginizer_sso_links', $sso_links); if(!empty($_POST['sso_email'])){ loginizer_sso_send_mail($url, sanitize_email($_POST['sso_email'])); } return $url; } function loginizer_sso_send_mail($url, $email){ $site_name = get_bloginfo('name'); $sub = sprintf(__('Login at %s', 'loginizer'), $site_name); $msg = sprintf(__('Hi, Please find below the Single Sign-On (SSO) link for our platform: %s This link will allow you to access the platform securely without needing to enter your login credentials, this link is valid for 10 minutes. Thank you for using our platform. Best regards, %s', 'loginizer'), $url, $site_name); wp_mail($email, $sub, $msg); } function loginizer_generate_ssotoken($uid = 0, $ttl = 600, $attempts = 1){ global $loginizer; // Variables $time = time(); $expires = ($time + $ttl); $action = 'loginizer_sso_'.$uid; include_once(ABSPATH . '/'.$loginizer['wp-includes'].'/class-phpass.php'); $wp_hasher = new \PasswordHash(8, TRUE); // Create the token with a random salt and the time $token = wp_hash(wp_generate_password(20, false).$action.$time); // Create a hash of the token $stored_hash = $wp_hasher->HashPassword($expires.$token); // Store the hash and when it expires update_user_meta($uid, $action, $stored_hash); update_user_meta($uid, $action.'_expires', $expires); update_user_meta($uid, $action.'_attempts', $attempts); return $token; } function loginizer_verify_sso(){ global $loginizer; if(empty($_GET['uid']) || empty($_GET['ssotoken'])){ return false; } $uid = (int) sanitize_key($_GET['uid']); $token = sanitize_key($_GET['ssotoken']); $action = 'loginizer_sso_'.$uid; $hash = get_user_meta($uid, $action, true); $expires = get_user_meta($uid, $action.'_expires', true); $attempts = (int) get_user_meta($uid, $action.'_attempts', true); include_once(ABSPATH.'/'.$loginizer['wp-includes'].'/class-phpass.php'); $wp_hasher = new \PasswordHash(8, TRUE); $time = time(); if(!$wp_hasher->CheckPassword($expires.$token, $hash) || $expires < $time || $attempts > 15 || $attempts <= 0){ $token_error_msg = __('The token is invalid or has expired.', 'loginizer'); loginizer_update_attempt_stats(0); // Throw an error return new \WP_Error('token_invalid', $token_error_msg, 'loginizer_sso'); }else{ if(!empty($loginizer['limit_session']) && !empty($loginizer['limit_session']['enable'])){ $limit_session = loginizer_limit_destroy_sessions($uid); if(empty($limit_session)){ return new \WP_Error('loginizer_session_limit', __('User ID not found so can not proceed', 'loginizer'), 'loginizer_epl'); } } // Deducting the count by 1 if(!empty($attempts)){ $attempts = $attempts - 1; update_user_meta($uid, $action.'_attempts', $attempts); } // Login the User wp_set_auth_cookie($uid); if(empty($attempts) || $expires < $time){ // Delete the meta delete_user_meta($uid, $action); delete_user_meta($uid, $action.'_expires'); delete_user_meta($uid, $action.'_attempts'); } $redirect_to = loginizer_csrf_change_url(admin_url(), $uid); loginizer_update_attempt_stats(1); // Redirect and exit wp_safe_redirect($redirect_to); exit; } return false; } function loginizer_sso_authenticate($user, $username, $password){ if(is_wp_error($user)){ // Ignore certain codes $ignore_codes = array('empty_username', 'empty_password'); if(is_wp_error($user) && !in_array($user->get_error_code(), $ignore_codes)) { return $user; } } $verified = loginizer_verify_sso(); if(is_wp_error($verified)){ return $verified; } } function loginizer_social_btn_woocommerce($return = false, $id = ''){ loginizer_social_btn($return, 'woocommerce'); } function loginizer_social_btn_comment($post_id){ loginizer_social_btn(false, 'comment'); } function loginizer_social_shortcode($atts){ global $loginizer; if(is_user_logged_in()){ return; } $atts = shortcode_atts([ 'type' => 'icon', 'divider' => 'above', 'shape' => 'square' ], $atts); $errors = loginizer_social_login_error_handler(); if(!empty($errors) || is_wp_error($errors)){ $error = '
'; $args = [ 'type' => 'error', ]; // Add the number of retires left as well if(count($errors->get_error_codes()) > 0 && isset($loginizer['retries_left'])){ $errors->add('retries_left', loginizer_retries_left()); } $messages = $errors->get_error_messages(); $notice = ''; if(count($messages) == 1){ $notice .= '

'.wp_kses_post($messages[0]).'

'; } else { $notice .= ''; } $error .= wp_get_admin_notice($notice, $args); $error .= '
'; } if(!empty($error)){ return $error . loginizer_social_btn(true, 'login', $atts); } return loginizer_social_btn(true, 'login', $atts); } function loginizer_social_update_avatar($avatar, $id_or_email, $size, $default, $alt){ global $wpdb, $blog_id; $user = false; if(empty($id_or_email)){ return $avatar; } if(is_numeric($id_or_email)){ $id = (int) $id_or_email; $user = get_user_by('id' , $id); } elseif(is_object($id_or_email)){ if(!empty($id_or_email->user_id)){ $id = (int) $id_or_email->user_id; $user = get_user_by('id' , $id); } } else { $user = get_user_by('email', $id_or_email); } if(empty($user) || !is_object($user) || empty($user->ID)){ return $avatar; } // Fetching the Image now $avatar_id = get_user_meta($user->ID, $wpdb->get_blog_prefix($blog_id) . 'lz_avatar', true); if(!wp_attachment_is_image($avatar_id)){ return $avatar; } $avatar_size = 'thumbnail'; if(!empty($size)){ $avatar_size = is_numeric($size) ? [$size, $size] : $size; } $avatar_url = wp_get_attachment_image_src($avatar_id, $avatar_size); if(empty($avatar_url) || empty($avatar_url[0])){ return $avatar; } $avatar = $avatar_url[0]; $avatar = ''.esc_attr($alt).''; return $avatar; } function loginizer_security_load_translation_vars(){ global $loginizer; $loginizer['pl_d_sub'] = __('Login at $site_name', 'loginizer'); $loginizer['pl_d_msg'] = __('Hi, A login request was submitted for your account $email at : $site_name - $site_url Login at $site_name by visiting this url : $login_url If you have not requested for the Login URL, please ignore this email. Regards, $site_name', 'loginizer'); if(empty($loginizer['passwordless_sub'])){ $loginizer['passwordless_sub'] = $loginizer['pl_d_sub']; } if(empty($loginizer['passwordless_msg'])){ $loginizer['passwordless_msg'] = $loginizer['pl_d_msg']; } $loginizer['wp_admin_d_msg'] = __('LZ : Not allowed via WP-ADMIN. Please access over the new Admin URL', 'loginizer'); if(empty($loginizer['captcha_text'])){ $loginizer['captcha_text'] = __('Math Captcha', 'loginizer'); } }license.txt000064400000063642147577721060006762 0ustar00 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! loginizer-security.php000064400000004677147577721060011162 0ustar00. */ if(!function_exists('add_action')){ echo 'You are not allowed to access this page directly.'; exit; } // Plugin already loaded if(defined('LOGINIZER_PREMIUM')){ return; } define('LOGINIZER_PRO_VERSION', '1.9.4'); define('LOGINIZER_PRO_FILE', __FILE__); define('LOGINIZER_API', 'https://api.loginizer.com/'); define('LOGINIZER_PRO_DIR', plugin_dir_path(__FILE__)); define('LOGINIZER_PRO_DIR_URL', plugin_dir_url(__FILE__)); include_once LOGINIZER_PRO_DIR . 'functions.php'; // TODO:: Add Require Plugins in the WordPress plugin comment, to make loginizer-security dependent on the free version, do it when 1.9.0+ reaches 90% adoption. $lz_tmp_plugins = get_option('active_plugins', []); $_lz_version = get_option('loginizer_version'); if( !defined('SITEPAD') && ( !(in_array('loginizer/loginizer.php', $lz_tmp_plugins) || loginizer_pro_is_network_active('loginizer')) || !file_exists(WP_PLUGIN_DIR . '/loginizer/loginizer.php') || (!empty($_lz_version) && version_compare($_lz_version, '1.8.9', '<'))) ){ include_once LOGINIZER_PRO_DIR . 'main/upgrader.php'; return; } function loginizer_security_load_plugin_textdomain(){ load_plugin_textdomain( 'loginizer', FALSE, basename( dirname( __FILE__ ) ) . '/languages/' ); } add_action('init', 'loginizer_security_load_plugin_textdomain', 0); define('LOGINIZER_PREMIUM', __FILE__); include_once(dirname(__FILE__).'/init.php');readme.txt000064400000061776147577721060006603 0ustar00=== Loginizer Security === Contributors: softaculous, loginizer, pagelayer Tags: security, access, admin, Loginizer, login, logs, ban ip, failed login, ip, whitelist ip, blacklist ip, failed attempts, lockouts, hack, authentication, login, security, rename login url, rename login, rename wp-admin, secure wp-admin, rename admin url, secure admin, brute force protection Requires at least: 3.0 Tested up to: 6.7 Requires PHP: 5.5 Stable tag: 1.9.4 License: LGPLv2.1 License URI: http://www.gnu.org/licenses/lgpl-2.1.html Loginizer is a WordPress security plugin which helps you fight against bruteforce attacks. == Description == Loginizer is a WordPress plugin which helps you fight against bruteforce attack by blocking login for the IP after it reaches maximum retries allowed. You can blacklist or whitelist IPs for login using Loginizer. You can use various other features like Two Factor Auth, reCAPTCHA, PasswordLess Login, etc. to improve security of your website. Loginizer is actively used by more than 1000000+ WordPress websites. You can find our official documentation at https://loginizer.com/docs. We are also active in our community support forums on wordpress.org if you are one of our free users. Our Premium Support Ticket System is at https://loginizer.deskuss.com Free Features : * Brute force protection. IPs trying to brute force your website will be blocked for 15 minutes after 3 failed login attempts. After multiple lockouts the IP is blocked for 24 hours. This is the default configuration and can be changed from Loginizer -> Brute force page in WordPress admin panel. * Failed login attempts logs. * Blacklist IPs * Whitelist IPs * Custom error messages on failed login. * Permission check for important files and folders. * Allow only Trusted IP. * Blocked Screen in place of the Login page. * Email Notification on successful login. * Let users login with LinkedIn = Get Support and Pro Features = Get professional support from our experts and pro features to take your site's security to the next level with Loginizer-Security. Pro Features : * MD5 Checksum - of Core WordPress Files. The admin can check and ignore files as well. * PasswordLess Login - At the time of Login, the username / email address will be asked and an email will be sent to the email address of that account with a temporary link to login. * Two Factor Auth via Email - On login, an email will be sent to the email address of that account with a temporary 6 digit code to complete the login. * Two Factor Auth via App - The user can configure the account with a 2FA App like Google Authenticator, Authy, etc. * Login Challenge Question - The user can setup a Challenge Question and Answer as an additional security layer. After Login, the user will need to answer the question to complete the login. * reCAPTCHA - Google's reCAPTCHA v3/v2, Cloudflare Turnstile, hCAPTCHA can be configured for the Login screen, Comments Section, Registration Form, etc. to prevent automated brute force attacks. Supports WooCommerce as well. * Rename Login Page - The Admin can rename the login URL (slug) to something different from wp-login.php to prevent automated brute force attacks. * Rename WP-Admin URL - The Admin area in WordPress is accessed via wp-admin. With loginizer you can change it to anything e.g. site-admin * CSRF Protection - This helps in preventing CSRF attacks as it updates the admin URL with a session string which makes it difficult and nearly impossible for the attacker to predict the URL. * Rename Login with Secrecy - If set, then all Login URL's will still point to wp-login.php and users will have to access the New Login Slug by typing it in the browser. * Disable XML-RPC - An option to simply disable XML-RPC in WordPress. Most of the WordPress users don't need XML-RPC and can disable it to prevent automated brute force attacks. * Rename XML-RPC - The Admin can rename the XML-RPC to something different from xmlrpc.php to prevent automated brute force attacks. * Username Auto Blacklist - Attackers generally use common usernames like admin, administrator, or variations of your domain name / business name. You can specify such username here and Loginizer will auto-blacklist the IP Address(s) of clients who try to use such username(s). * New Registration Domain Blacklist - If you would like to ban new registrations from a particular domain, you can use this utility to do so. * Change the Admin Username - The Admin can rename the admin username to something more difficult. * Auto Blacklist IPs - IPs will be auto blacklisted, if certain usernames saved by the Admin are used to login by malicious bots / users. * Disable Pingbacks - Simple way to disable PingBacks. * SSO - Single Sign-on, let any user access to your WordPress Dashboard without the need to share username or password. * Limit Concurrent Logins - It prevents user to login from different devices concurrently, you can define how many devices you want to allow, and how you want to restrict the user when concurrent limit is reached. * Social Login - Users can login or register with their Google, Github, Facebook, X (Twitter), Discord, Twitch, LinkedIn with support for WooCommerce. Features in Loginizer include: * Blocks IP after maximum retries allowed * Extended Lockout after maximum lockouts allowed * Email notification to admin after max lockouts * Blacklist IP/IP range * Whitelist IP/IP range * Check logs of failed attempts * Create IP ranges * Delete IP ranges * Licensed under LGPLv2.1 * Safe & Secure == Installation == Upload the Loginizer plugin to your blog, Activate it. That's it. You're done! == Screenshots == 1. Login Failed Error message 2. Loginizer Dashboard page 3. Loginizer Brute Force Settings page == Changelog == = 1.9.4 = * [Task] Tested with WordPress 6.7, and fixed the translation load Notice issue. * [Bug-Fix] HOTP and Base32 caused conflict with some plugins that has been fixed. = 1.9.3 = * [Task] Improved Compatibility with Softaculous Plugin. = 1.9.2 = * [Task] Improved license handling. = 1.9.1 = * [Bug-Fix] Social Login buttons not showing on WooCommerce login page. * [Bug-Fix] A PHP warning has been fixed. = 1.9.0 = * [Bug-Fix] For some users there was an issue in updating that has been fixed. = 1.8.9 = * [Task] Structural changes. * [Task] Tested with WordPress 6.6. = 1.8.8 = * [Bug-Fix] Verison in one file was not updated, this has been fixed. = 1.8.7 = * [Feature] Social Login: Now you can let the users login through LinkedIn Login. * [Feature] Send Login Notification as HTML email. * [Pro Feature] Supports social login with Google, GitHub, Facebook, X(Formly Twitter) and more Login Providers. = 1.8.6 = * [Bug-Fix] There was an issue with Login Notification body and subject, it was adding \(slashes) if "(double-quotes) where being used. This has been fixed. * [Task] Removal of unwanted code. = 1.8.5 = * [Feature] Added Option to disable Login notification for whitelisted IPs. * [Improvement] We have added variables for custom subject in Login notification. * [Bug-Fix] Now the time shown in the Login Notification email, will respect the timezone set in the WordPress settings. * [Bug-Fix] Error notice when 2FA fails had some CSS issue which has been fixed. * [Task] We have remove unwanted code in reCAPTCHA. = 1.8.4 = * [Feature] Block Page, now instead of showing error on the Login page of user being blacklisted, you can just show a page with error, reducing the resource being used to show the error. * [Feature] Email notification on successful login and you can enforce this on your users too. * [Pro Feature] Added Cloudflare Turnstile, and hCaptcha. * [Task] Tested with WordPress 6.5. = 1.8.3 = * [Task] We have removed unwanted code. = 1.8.2 = * [Task] Tested on WordPress 6.4. * [Improvement] Now SSO can live for multiple Login attempts, default being 1 and maximum is 15 Login access. * [Imrpovement] Now SSO can live longer for upto 2 days. * [Bug-Fixes] A few Warning related to PHP 8.2 has been fixed = 1.8.1 = *[Bug-Fix] There was an issue while checking checksum, if the WordPress install was in en_US but the language was set to some other languages from the settings, then the checksum was comparing the checksums from the language selected in WordPress settings which is now always the language of the install, this has been fixed. = 1.8.0 = * [Feature][Pro] We have added Single Sign-on for you to create temporary login to share to let other login to your account without sharing password. * [Refactor] We have reduced the amount of code that was being loaded when a login attempt was made by around 150KB. * [Refactor] Screenshots of Loginizer were included in the plugin, we have shifted that to assets of WordPress.org, reducing the overall size of plugin by more than 100KB. = 1.7.9 = * [Bug-Fix] Users were getting PHP notice in init.php file that has been fixed. * [Bug-Fix] Math cookie has been set as secure now. * [Security] We were sanitizing an output in place of escaping it, that has been fixed [Reported by Erwan Le Rousseau from WPScan] = 1.7.8 = * [Task] Tested with WordPress 6.2 * [Feature] [Pro] Limit Concurrent user login, you can either block login attempt or revoke when limit of concurrent user is reached. * [Feature] Login attempts stats chart on Loginizer Dashboard. = 1.7.7 = * [Feature] Ability to allow only Whitelisted IP's to be able to login with Trusted IP's. * [Feature] [Pro] Option to add custom redirect on 2FA Login based on user role. * [Bug-Fix] [Pro] User's were getting redirected to WP Admin when logging in from Checkout page in Passwordless and 2FA options that has been fixed. * [Bug-Fix] Some users were getting PHP Warnings that has been fixed. = 1.7.6 = * [Security] Minor security issues reported by patchstack have been fixed with in 24 hours of reporting. * [Bug-Fix] For some themes the Maths capatch input was invisible that has been fixed. = 1.7.5 = * [Task] Tested compatibility with WordPress 6.1 * [Bug Fix] There was an issue with sanitizing URL that has been fixed. = 1.7.4 = * [Feature] CSRF Protection adds a unique session key in your admin URL when you login to it, which adds another layer of security to your WordPress website as it makes it difficult to predict the URL hence making it difficult and nearly impossible to do CSRF attacks on your WordPress admin panel. * [Task] 2FA Support for MasterStudy Custom Login * [Bug Fix] Some users were facing an error when using 2FA App verification that has been fixed. = 1.7.3 = * [Bug Fix] Added validation not to allow values less than 0 for all Brute Force admin settings. = 1.7.2 = * [Improvement] [Pro] Allowed HTML characters in Passwordless email. * [Bug Fix] Improved performance on sites running Loginizer with WooCommerce. * [Bug Fix] Added validation not to allow values less than 0 in Brute Force admin settings. * [Bug Fix] Some language strings were hardcoded in English and could not be translated. This is fixed and all strings can now be translated. * [Bug Fix] Resolved PHP Warnings and Notices on latest PHP versions. = 1.7.1 = * [Improvement] [Pro] Added error message to not allow using same slug for wp-login.php and wp-admin as it causes conflict. * [Improvement] [Pro] Added exception for readme.html, license.txt and wp-config-sample.php while checking the checksum to avoid false alarm about checksum mismatch. * [Bug Fix] [Pro] In WordPress Multisite, on changing the admin username the super admins list was not updated. This is fixed now. * [Task] Compatibility with WordPress 6.0 = 1.7.0 = * Compatible with WordPress 5.9 * [Feature] [Pro] Added option to choose recaptcha.net instead of google.com for countries that do not support google * [Bug Fix] [Pro] Fix to email the correct unblock time when an IP is blocked for extended hours. = 1.6.9 = * [Bug Fix] [Pro] Fix to not show Loginizer 2FA Security Settings in Edit Account page in WooCommerce Customer area. It will be shown in Security (registered by Loginizer) tab instead. = 1.6.8 = * [Feature] Added option to export failed login attempts to CSV file. * [Improvement] Added option to send failed login notifications to a custom email. * [Improvement] [Pro] Added support for 2FA for WooCommerce customers * [Bug Fix] [Pro] On WooCommerce customer login page the password field was not hidden when Passwordless login was enabled in Loginizer. * [Bug Fix] [Pro] Autofill enabled in the browser caused the OTP field on 2FA login to be prefilled. = 1.6.7 = * [Feature] Added Bulk Export/Import Blacklist and Whitelist IPs via CSV. * [Improvement] Added option to Blacklist selected IPs from Failed Login Attempts Logs. * [Improvement] Added external link in Brute Force logs for IP information of the IPs attempting brute force. * [Improvement] [Pro] Added Loginizer 2FA status column on Users list page to show 2FA preferences selected by users. * [Improvement] [Pro] Added Show/Hide button for OTP field on 2FA login page. * [Bug Fix] [Pro] Two Factor Authentication lead to 502 Bad Gateway error on WP Engine instances. This is resolved now. = 1.6.6 = * [Improvement] For new installs, the loginizer_logs table will now use the server default MySQL Engine. * [Improvement] For the login attempts blocked by Loginizer, some other Activity Logs plugin still reported such blocked attempt as a failed login attempt. * [Bug Fix] In rare cases when the username received in failed login attempt was blank, Loginizer failed to save such requests in the failed login logs table. This is fixed now. = 1.6.5 = * [Bug Fix] After Interim Login due to session timeout, the popup for login was not closed. This is fixed now. * [Bug Fix] reCAPTCHA was not working on registration page with BuddyPress plugin. This is fixed now. = 1.6.4 = This version includes a security fix and we recommend all users to upgrade to 1.6.4 or higher immediately. * [Security Fix] : A properly crafted username used to login could lead to SQL injection. This has been fixed by using the prepare function in PHP which prepares the SQL query for safe execution. * [Security Fix] : If the IP HTTP header was modified to have a null byte it could lead to stored XSS. This has been fixed by properly sanitizing the IP HTTP header before using the same. = 1.6.3 = * [Fix] Fixed a PHP Notice that was caused by a change released yesterday. = 1.6.2 = * [Feature] Added option to send Password Less Login email as HTML. * [Fix] When reCAPTCHA was disabled on Woocommerce checkout page, Loginizer reported captcha error if a user tried to register on checkout page. This is fixed now. * [Fix] The email sent to admin for brute force login attempts will now contain the site url as well. * [Fix] Fixed PHP Notice on Two Factor Authentication page. = 1.6.1 = * [Fix] The captcha on Registration form when using WooCommerce was not being rendered if the "WooCommerce Checkout" captcha setting was disabled in Loginizer. This is fixed now and this captcha can be disabled with "Registration Form" captcha setting in Loginizer. * [Fix] Minor checkbox pre-filling UI fix on Two Factor Authentication page. = 1.6.0 = * [Feature] Admin can white list an IP or an IP range for Two Factor Authentication. * [Fix] If the plugins or themes which are included in the default WordPress package were not updated, the Checksum reported that the files for such plugins and themes did not matched. This is fixed now. = 1.5.9 = * [Task] Admins can now customize email template for 2FA OTP via email. * [Task] Admins can now customize the 2FA messages on login screen. * [Fix] Changed the OTP via App field on login page to password type. = 1.5.8 = * [Task] Permission for / folder was suggested as 0755 and 0750 permission which is secure was reported as insecure. This is fixed now. * [Fix] Prevent PHP Deprecated Warning on plugin upgrade page on servers running PHP 7.3+ = 1.5.7 = * [Fix] Prevent PHP Notice on 1st failed login attempt from an IP. = 1.5.6 = * [Task] Admins can now subscribe to our newsletter if they decide to opt-in. = 1.5.5 = * [Bug Fix] Remember me during login was not working with 2FA features. This is fixed. * [Task] Loginizer is now supported for translation via WordPress. * [Task] Added option to fully customize the Lockout error message. = 1.5.4 = * [Task] Added option to customize Lockout Error message. = 1.5.3 = * [Task] Compatible with WordPress 5.5 * [Bug Fix] Due to a conflict with some plugin the upgrade for Loginizer Premium version did not work. This is fixed. = 1.5.2 = * [Task] Some strings were not available in translations. They can now be translated. = 1.5.1 = * [Task] Allowed to change the username of any administrator account. Previously it was supported only for user id 1 * [Bug Fix] Fixed some lines that generated PHP notice = 1.5.0 = * [Task] Admins can now customize "attempt(s) left" error message. = 1.4.9 = * [Bug Fix] Prevent brute force on 2FA pages. = 1.4.8 = * [Premium Feature] Added Google reCAPTCHA v3 and v2 invisible. = 1.4.7 = * [Security Fix] Our team internally conducted a security audit and have fixed couple of security issues. We recommend all users to upgrade to the latest version asap. = 1.4.6 = * [Task] Added Timezone offset in the Brute Force attempts list to get the exact time of the failed login attempt. * [Bug Fix] For HTTP_X_FORWARDED_FOR if the value had multiple IPs including proxied IPs, the user IP detection failed. This is fixed. * [Bug Fix] Undefined variable was used in the title on the dashboard page. This is fixed. = 1.4.5 = * [Announcement] Loginizer has joined forces with Softaculous team. * [Task] Added OTP validity time in the email sent for OTP for login. * [Bug Fix] In the premium version the Math Captcha used to fail in some conditions. This is fixed. = 1.4.4 = * [Task] Made Loginizer compatible with PHP 7.4 * [Bug Fix] The password field was not hidden in some themes for PasswordLess Login. This is fixed. = 1.4.3 = * [Bug Fix] At the time of login if recaptcha or 2FA using OTP was enabled and if you check mark on "Remember me", the login used to go to an invalid redirect URL and did not load anything. This has been fixed now. = 1.4.2 = * [Task] Tested up to: WordPress 5.2.0 * [Bug Fix] Placement of Captcha corrected for WooCommerce at the time of checkout for end users. * [Bug Fix] Checksum check shall now skip for the files which are present in default WordPress package and does not exist in the installation like deleted theme(s)/plugin(s). * [Bug Fix] Grammar correction = 1.4.1 = * [Task] Tested up to: WordPress 5.0.2 * [Task] Refresh license will throw an error if the response received from our server is invalid * [Bug Fix] The OTP input box (with respect to 2FA via SMS) was empty if the user was freshly registered and did not login at all. This is fixed. = 1.4.0 = * [Feature] New Registration Domain Blacklist - If you would like to ban new registrations from a particular domain, you can use this utility to do so. * [Feature] Made Loginizer Security for BuddyPress compatibility. * [Task] Added a method to reset wp-admin rename settings if you get locked out. * [Bug Fix] There is an XSS bug introduced in version 1.3.8. This is fixed. Please upgrade ASAP. * [Bug Fix] In the user 2FA security wizard, the default selected option was wrongly shown when the user had not set any preference for 2FA. This is fixed. = 1.3.9 = * [Feature] Added an option to Enable / Disable Brute Force checks. * [Feature] Added the feature to log the URL of the page from which the brute force attempt is being made. * [Bug Fix] Blanking the login slug used to show the value after submission. This is fixed. * [Bug Fix] Allowed HTML chars in wp_admin_msg for renaming WP-ADMIN. = 1.3.8 = * [Feature] Added Roles selection for Two Factor Authentication. The Admin can now enable 2FA for specific roles. * [Feature] Added a Tester for WP-Admin Slug Renaming feature. Now you can test the new slug before saving it. * [Feature] Added option to customize the Passwordless email being sent to the user. * [Feature] Added a custom WP-Admin restriction message if wp-admin is restricted. * [Feature] Added an option to Delete the entire Blacklist / Whitelist IP Ranges. * [Feature] Custom IP Header added as an option for detecting the IP as per the Proxy settings of a server. * [Task] Added an option to clear reCAPTCHA settings. * [Task] Added Debugger in Updater * [Task] Updater will show "Install License Key to check for Updates" * [Bug Fix] In WooCommerce the number of login retries left was not being shown. This is fixed. = 1.3.7 = * [Bug Fix] Blacklist and Whitelist IPs were not being deleted. This is fixed. = 1.3.6 = * [Feature] Pagination added to the Blacklist and Whitelist IPs * [Bug Fix] There used to be a login issue over SSL when wp-admin area is renamed. This is fixed. * [Bug Fix] SQL Injection fix for X-Forwarded-For. This is fixed. Vulnerability was found by Jonas Lejon of WPScans.com * [Bug Fix] There was a missing referrer check in Blacklist and Whitelist IP Wizard. This is fixed. = 1.3.5 = * [Feature] Added a simple Math Captcha to show Maths Questions, if someone doesn’t want to use Google Captcha * [Feature] Added a wizard for admins to set their own language strings for Brute Force messages * [Bug Fix] In WooCommerce the Lost Password, Reset Password and Comment Form captcha verification failed. This is fixed. * [Bug Fix] Hide Captcha for logged in users was not working. This is fixed. * [Bug Fix] Twitter box shown in Loginizer was not accessed over HTTPS. = 1.3.4 = * [Bug Fix] Fixed the BigInteger Class for PHP 7 compatibility. = 1.3.3 = * [Feature] IPv6 support has been added. * [Feature] The last attempted username will now be shown in the Login Logs. * [Bug Fix] If the login page had been renamed, and wp-login.php was accessed over HTTPS, the login screen was shown instead of 404 not found. This is now fixed. * [Bug Fix] If the user had used a "/" in the rename login slug, the new slug would not work without the "/" in the URL. This is now fixed. * [Bug Fix] The license key could get reset in some cases. This also caused plugin updates to fail. This is now fixed. * [Bug Fix] Wild Cards “*” in the Username Auto Blacklist did not work. This is now fixed. * [Bug Fix] The documentation in the plugin was pointing to a wrong link. This is now fixed. = 1.3.2 = * [Feature] Rename the wp-admin access URL is now possible with Loginizer * [Feature] WooCommerce support has been improved for reCAPTCHA * [Feature] Loginizer will now show a Notification to the Enduser to setup the preferred 2FA settings * [Feature] Added option to choose between REMOTE_ADDR, HTTP_CLIENT_IP and HTTP_X_FORWARDED for websites behind a proxy * [Task] Multiple reCAPTHCA on a single page is now supported * [Task] Added a link to Google's reCAPTCHA website for easy access in our reCAPTCHA wizard = 1.3.1 = * [Feature] Admin's can now remove a user's Two Factor Authentication if needed * [Feature] Added an option to change the Admin Username * [Feature] Auto Blacklist IPs if certain usernames saved by the Admin are used to login by malicious bots / users * [Feature] The Login attempt logs will now be shown as per the last attempt TIME and in Descending Order * [Feature] Added an option to Reset the Login attempts for all or specific IPs = 1.3.0 = * [Feature] Added MD5 File Checksum feature. If any core files are changed, Loginizer will log the differences and notify the Admin * [Feature] Added an option to make Email OTP as the default Two Factor Auth when a user has not set the OTP method of their choice * [Feature] Added WooCommerce support for Captcha forms * [Feature] Added pagination in the Brute Force Logs Wizard * [Bug Fix] Disabling and Re-Enabling Loginizer caused an SQL error = 1.2.0 = * [Feature] Rename Login with Secrecy : If set, then all Login URL's will still point to wp-login.php and users will have to access the New Login Slug by typing it in the browser. * [Task] The brute force logs will now be sorted as per the time of failed login attempts * [Bug Fix] Dashboard showed wrong permissions if wp-content path had been changed * [Bug Fix] Added Directory path to include files which caused issues with some plugins = 1.1.1 = * [Bug Fix] Added ABSPATH instead of get_home_path() = 1.1.0 = * [Feature] PasswordLess Login * [Feature] Two Factor Auth - Email * [Feature] Two Factor Auth - App * [Feature] Login Challenge Question * [Feature] reCAPTCHA * [Feature] Rename Login Page * [Feature] Disable XML-RPC * [Feature] Rename XML-RPC * [Feature] Disable Pingbacks * [Feature] New Dashboard * [Feature] System Information added in the new Dashboard * [Feature] File Permissions added in the new Dashboard * [Feature] New UI * [Bug Fix] Fixed bug to add IP Range from 0.0.0.1 - 255.255.255.255 * [Bug Fix] Removed /e from preg_replace causing warnings in PHP = 1.0.2 = * Fixed Extended Lockout bug * Fixed Lockout bug * Handle login attempts via XML-RPC = 1.0.1 = * Database structure changes to make the plugin work faster * Minor fixes = 1.0 = * Blocks IP after maximum retries allowed * Extended Lockout after maximum lockouts allowed * Email notification to admin after max lockouts * Blacklist IP/IP range * Whitelist IP/IP range * Check logs of failed attempts * Create IP ranges * Delete IP ranges * Licensed under LGPLv2.1 * Safe & Secure