├── .gitignore ├── LICENSE ├── README.md ├── assets ├── js │ └── contact-form.js └── vender │ ├── bootstrap │ └── assets │ │ └── js │ │ ├── html5shiv.js │ │ └── respond.min.js │ └── intl-tel-input │ ├── css │ └── intlTelInput.css │ ├── img │ └── flags.png │ └── js │ ├── intlTelInput.min.js │ └── isValidNumber.js ├── index.html └── library ├── sendmail.php └── vender └── php_mailer ├── PHPMailerAutoload.php ├── class.phpmailer.php ├── class.pop3.php └── class.smtp.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | /nbproject 3 | .idea 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jon Bake 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ======================= 3 | 4 | Bootstrap 3 Contact Form with Google's reCaptcha 5 | 6 | **Demo:** http://jonmbake.github.io/bootstrap3-contact-form/ 7 | 8 | **Blog Post:** [Bootstrap 3 Contact Form with Captcha](http://jonbake.com/blog/2013/10/17/bootstrap-3-contact-form-with-captcha.html) 9 | 10 | **Follow-Up Blog Post:** [A Better Contact Form](http://jonbake.com/blog/2015/03/22/a-better-contact-form.html) 11 | 12 | ======================= 13 | 14 | A simple bootstrap 3 contact form using [Google's reCAPTCHA](https://developers.google.com/recaptcha/). Submitted messages are sent to a specified email address using SMTP with support for SSL or TLS transport. 15 | 16 | **A Note On Security:** [PHPMailer](https://github.com/PHPMailer/PHPMailer), which this contact form is dependent on, had a major security vulnerability fixed in 5.2.20. **It is recommended to update to 1.4.** 17 | 18 | ## Version History 19 | 20 | | Versions | Major Enhancement | 21 | | -------- | ----------------- | 22 | | 1.4.1 | Bumped [PHPMailer](https://github.com/PHPMailer/PHPMailer) version to 5.2.21. | 23 | | 1.4 | Add support for [cURL](http://php.net/manual/en/book.curl.php) when POSTing to verify reCAPTCHA. | 24 | | 1.3 | Add support for [Bootstrap Validator](https://github.com/1000hz/bootstrap-validator). If provided, will use it to validate contact form. | 25 | | 1.2 | Replaced PHP SecureImage Captcha with Google's reCAPTCHA. | 26 | | 1.1 | Used PHPMailer. Support for SSL/TLS transport. Extracted configuration values to environment variables. | 27 | | 1.0 | First Version - Used PHP SecureImage and PHP mail function | 28 | 29 | ## Dependencies 30 | 31 | ### PHP 32 | * version > 5.2.0 33 | * [PHPMailer](https://github.com/PHPMailer/PHPMailer) (included in [library/vender/php_mailer/**](https://github.com/jonmbake/bootstrap3-contact-form/tree/master/library/vender/php_mailer)) 34 | 35 | ### HTML/JS 36 | * [Bootstrap 3](https://github.com/twbs/bootstrap) version >3.1 37 | * jQuery 38 | * **Optional** [International Telephone Input](https://github.com/Bluefieldscom/intl-tel-input) (included in [assets/vender/intl-tel-input/**](https://github.com/jonmbake/bootstrap3-contact-form/tree/master/assets/vender/intl-tel-input))- This is used to validate and format the phone input field. Only need this if the phone field is present. 39 | 40 | ## Setting up reCAPTCHA 41 | 42 | You must obtain a [Site Key and Secret Key from Google](http://www.google.com/recaptcha/admin). The *Site Key* must be entered into the [Contact Form HTML](https://github.com/jonmbake/bootstrap3-contact-form/blob/master/index.html) in the place of the text *your_site_key*. The *Secret Key* should be entered as a configuration value (see next section). 43 | 44 | **Note:** Many web servers now force `allow_url_fopen=0` and `allow_url_include=0` due to security concerns (see: [Issue 26](https://github.com/jonmbake/bootstrap3-contact-form/issues/26)). reCAPTCHA verifying will use [cURL](http://php.net/manual/en/book.curl.php) is if it is installed. If you are having issues verifying reCAPTCHA, most likely you need to install [cURL](http://php.net/manual/en/book.curl.php). 45 | 46 | ## Configuration 47 | 48 | Configuration values to the contact form are passed in via *Environment Variables*. The following variables need to be defined: 49 | 50 | | Name | Description | 51 | |-------------------- | -------------------------------------------------------------------- | 52 | | FEEDBACK_HOSTNAME | Host name for SMTP server | 53 | | FEEDBACK_EMAIL | Email address to authenticate to SMTP server with | 54 | | FEEDBACK_PASSWORD | Password to authenticate to SMTP server with | 55 | | FEEDBACK_ENCRYPTION | If specified will use encryption. Valid values: TLS or SSL | 56 | | RECAPTCHA_SECRET_KEY | reCAPTCHA secret key. | 57 | | FEEDBACK_SKIP_AUTH | **Optional** If specified, will not authenticate with email/password | 58 | 59 | Environment variables can be specified in a variety of ways. For example, if using *Apache* (and *mod_env* is enabled), they can be specified in *.htaccess*: 60 | 61 | ``` 62 | SetEnv FEEDBACK_HOSTNAME smtp.gmail.com 63 | SetEnv FEEDBACK_EMAIL me@gmail.com 64 | SetEnv FEEDBACK_PASSWORD my!password! 65 | SetEnv FEEDBACK_ENCRYPTION TLS 66 | SetEnv RECAPTCHA_SECRET_KEY 7823skdgjksd828sjdgkn 67 | ``` 68 | 69 | **Tip:** *Environment Variables* are used in [Sendmail.php](https://github.com/jonmbake/bootstrap3-contact-form/blob/master/library/sendmail.php). **If you don't want to use Environment Variables**, you can edit *sendmail.php*, replacing calls to #getenv with the corresponding configuration value like in this [Example Gist](https://gist.github.com/jonmbake/0e5b175a72ad9ba64167). 70 | 71 | ## What If I Don't Want CAPTCHA? 72 | 73 | There's a branch for that! Check out the branch: [Contact Form without CAPTCHA](https://github.com/jonmbake/bootstrap3-contact-form/tree/no-captcha). 74 | -------------------------------------------------------------------------------- /assets/js/contact-form.js: -------------------------------------------------------------------------------- 1 | (function (window) { 2 | //using regular expressions, validate email 3 | var contactFormUtils = window.contactFormUtils = { 4 | isValidEmail: function (email) { 5 | var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/; 6 | return regex.test(email); 7 | }, 8 | //if no form errors, remove or hide error messages 9 | clearErrors: function () { 10 | $('#emailAlert').remove(); 11 | $('#feedbackForm .help-block').hide(); 12 | $('#feedbackForm .form-group').removeClass('has-error'); 13 | }, 14 | //upon form clear remove the checked class and replace with unchecked class. Also reset Google ReCaptcha 15 | clearForm: function () { 16 | $('#feedbackForm .glyphicon').removeClass('glyphicon-check').addClass('glyphicon-unchecked').css({color: ''}); 17 | $('#feedbackForm input,textarea').val(""); 18 | grecaptcha.reset(); 19 | }, 20 | //when error, show error messages and track that error exists 21 | addError: function ($input) { 22 | var parentFormGroup = $input.parents('.form-group'); 23 | parentFormGroup.children('.help-block').show(); 24 | parentFormGroup.addClass('has-error'); 25 | }, 26 | addAjaxMessage: function(msg, isError) { 27 | $("#feedbackSubmit").after('
' + $('
').text(msg).html() + '
'); 28 | } 29 | }; 30 | 31 | $(document).ready(function() { 32 | if ($("#phone").intlTelInput) { 33 | $("#phone").intlTelInput({validationScript: "assets/vender/intl-tel-input/js/isValidNumber.js"}); 34 | $(".intl-tel-input.inside").css('width', '100%'); 35 | } 36 | 37 | $("#feedbackSubmit").click(function() { 38 | var $btn = $(this); 39 | $btn.button('loading'); 40 | contactFormUtils.clearErrors(); 41 | 42 | //do a little client-side validation -- check that each field has a value and e-mail field is in proper format 43 | //use bootstrap validator (https://github.com/1000hz/bootstrap-validator) if provided, otherwise a bit of custom validation 44 | var $form = $("#feedbackForm"), 45 | hasErrors = false; 46 | if ($form.validator) { 47 | hasErrors = $form.validator('validate').hasErrors; 48 | } else { 49 | $('#feedbackForm input,#feedbackForm textarea').not('.optional').each(function() { 50 | var $this = $(this); 51 | if (($this.is(':checkbox') && !$this.is(':checked')) || !$this.val()) { 52 | hasErrors = true; 53 | contactFormUtils.addError($(this)); 54 | } 55 | }); 56 | var $email = $('#email'); 57 | if (!contactFormUtils.isValidEmail($email.val())) { 58 | hasErrors = true; 59 | contactFormUtils.addError($email); 60 | } 61 | var $phone = $('#phone'); 62 | if ($phone.val() && $phone.intlTelInput && !$phone.intlTelInput("isValidNumber")) { 63 | hasErrors = true; 64 | contactFormUtils.addError($phone.parent()); 65 | } 66 | } 67 | //if there are any errors return without sending e-mail 68 | if (hasErrors) { 69 | $btn.button('reset'); 70 | return false; 71 | } 72 | //send the feedback e-mail 73 | $.ajax({ 74 | type: "POST", 75 | url: "library/sendmail.php", 76 | data: $form.serialize(), 77 | success: function(data) { 78 | contactFormUtils.addAjaxMessage(data.message, false); 79 | contactFormUtils.clearForm(); 80 | }, 81 | error: function(response) { 82 | contactFormUtils.addAjaxMessage(response.responseJSON.message, true); 83 | }, 84 | complete: function() { 85 | $btn.button('reset'); 86 | } 87 | }); 88 | return false; 89 | }); 90 | $('#feedbackForm input, #feedbackForm textarea').change(function () { 91 | var checkBox = $(this).siblings('span.input-group-addon').children('.glyphicon'); 92 | if ($(this).val()) { 93 | checkBox.removeClass('glyphicon-unchecked').addClass('glyphicon-check').css({color: 'green'}); 94 | } else { 95 | checkBox.removeClass('glyphicon-check').addClass('glyphicon-unchecked').css({color: ''}); 96 | } 97 | }); 98 | }); 99 | })(window); 100 | -------------------------------------------------------------------------------- /assets/vender/bootstrap/assets/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.6.2pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",version:"3.6.2pre",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d #mq-test-1 { width: 42px; }',d.insertBefore(f,e),c=42===g.offsetWidth,d.removeChild(f),{matches:c,media:a}}}(document); 4 | 5 | /*! Respond.js v1.1.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 6 | (function(a){"use strict";function x(){u(!0)}var b={};a.respond=b,b.update=function(){},b.mediaQueriesSupported=a.matchMedia&&a.matchMedia("only all").matches,b.mediaQueriesSupported;var q,r,t,c=a.document,d=c.documentElement,e=[],f=[],g=[],h={},i=30,j=c.getElementsByTagName("head")[0]||d,k=c.getElementsByTagName("base")[0],l=j.getElementsByTagName("link"),m=[],n=function(){for(var b=0;l.length>b;b++){var c=l[b],d=c.href,e=c.media,f=c.rel&&"stylesheet"===c.rel.toLowerCase();d&&f&&!h[d]&&(c.styleSheet&&c.styleSheet.rawCssText?(p(c.styleSheet.rawCssText,d,e),h[d]=!0):(!/^([a-zA-Z:]*\/\/)/.test(d)&&!k||d.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&m.push({href:d,media:e}))}o()},o=function(){if(m.length){var a=m.shift();v(a.href,function(b){p(b,a.href,a.media),h[a.href]=!0,setTimeout(function(){o()},0)})}},p=function(a,b,c){var d=a.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),g=d&&d.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+b+"$2$3")},i=!g&&c;b.length&&(b+="/"),i&&(g=1);for(var j=0;g>j;j++){var k,l,m,n;i?(k=c,f.push(h(a))):(k=d[j].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1,f.push(RegExp.$2&&h(RegExp.$2))),m=k.split(","),n=m.length;for(var o=0;n>o;o++)l=m[o],e.push({media:l.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:f.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},s=function(){var a,b=c.createElement("div"),e=c.body,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",e||(e=f=c.createElement("body"),e.style.background="none"),e.appendChild(b),d.insertBefore(e,d.firstChild),a=b.offsetWidth,f?d.removeChild(e):e.removeChild(b),a=t=parseFloat(a)},u=function(a){var b="clientWidth",h=d[b],k="CSS1Compat"===c.compatMode&&h||c.body[b]||h,m={},n=l[l.length-1],o=(new Date).getTime();if(a&&q&&i>o-q)return clearTimeout(r),r=setTimeout(u,i),void 0;q=o;for(var p in e)if(e.hasOwnProperty(p)){var v=e[p],w=v.minw,x=v.maxw,y=null===w,z=null===x,A="em";w&&(w=parseFloat(w)*(w.indexOf(A)>-1?t||s():1)),x&&(x=parseFloat(x)*(x.indexOf(A)>-1?t||s():1)),v.hasquery&&(y&&z||!(y||k>=w)||!(z||x>=k))||(m[v.media]||(m[v.media]=[]),m[v.media].push(f[v.rules]))}for(var B in g)g.hasOwnProperty(B)&&g[B]&&g[B].parentNode===j&&j.removeChild(g[B]);for(var C in m)if(m.hasOwnProperty(C)){var D=c.createElement("style"),E=m[C].join("\n");D.type="text/css",D.media=C,j.insertBefore(D,n.nextSibling),D.styleSheet?D.styleSheet.cssText=E:D.appendChild(c.createTextNode(E)),g.push(D)}},v=function(a,b){var c=w();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},w=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}();n(),b.update=n,a.addEventListener?a.addEventListener("resize",x,!1):a.attachEvent&&a.attachEvent("onresize",x)})(this); 7 | -------------------------------------------------------------------------------- /assets/vender/intl-tel-input/css/intlTelInput.css: -------------------------------------------------------------------------------- 1 | .intl-tel-input .flag{width:16px;height:11px;background:url("../img/flags.png")}.intl-tel-input .zw{background-position:0px 0px}.intl-tel-input .zm{background-position:-16px 0px}.intl-tel-input .za{background-position:0px -11px}.intl-tel-input .yt{background-position:-16px -11px}.intl-tel-input .ye{background-position:-32px 0px}.intl-tel-input .ws{background-position:-32px -11px}.intl-tel-input .wf{background-position:0px -22px}.intl-tel-input .vu{background-position:-32px -22px}.intl-tel-input .vn{background-position:0px -33px}.intl-tel-input .vi{background-position:-16px -33px}.intl-tel-input .vg{background-position:-32px -33px}.intl-tel-input .ve{background-position:-48px 0px}.intl-tel-input .vc{background-position:-48px -11px}.intl-tel-input .va{background-position:-48px -22px}.intl-tel-input .uz{background-position:-48px -33px}.intl-tel-input .uy{background-position:0px -44px}.intl-tel-input .us{background-position:-16px -44px}.intl-tel-input .um{background-position:-16px -44px}.intl-tel-input .ug{background-position:-32px -44px}.intl-tel-input .ua{background-position:-48px -44px}.intl-tel-input .tz{background-position:-64px 0px}.intl-tel-input .tw{background-position:-64px -11px}.intl-tel-input .tv{background-position:-64px -22px}.intl-tel-input .tt{background-position:-64px -33px}.intl-tel-input .tr{background-position:-64px -44px}.intl-tel-input .to{background-position:0px -55px}.intl-tel-input .tn{background-position:-16px -55px}.intl-tel-input .tm{background-position:-32px -55px}.intl-tel-input .tl{background-position:-48px -55px}.intl-tel-input .tk{background-position:-64px -55px}.intl-tel-input .tj{background-position:0px -66px}.intl-tel-input .th{background-position:-16px -66px}.intl-tel-input .tg{background-position:-32px -66px}.intl-tel-input .tf{background-position:-48px -66px}.intl-tel-input .td{background-position:-64px -66px}.intl-tel-input .tc{background-position:-80px 0px}.intl-tel-input .sz{background-position:-80px -11px}.intl-tel-input .sy{background-position:-80px -22px}.intl-tel-input .sx{background-position:-80px -33px}.intl-tel-input .sv{background-position:-80px -44px}.intl-tel-input .st{background-position:-80px -55px}.intl-tel-input .ss{background-position:-80px -66px}.intl-tel-input .sr{background-position:0px -77px}.intl-tel-input .so{background-position:-16px -77px}.intl-tel-input .sn{background-position:-32px -77px}.intl-tel-input .sm{background-position:-48px -77px}.intl-tel-input .sl{background-position:-64px -77px}.intl-tel-input .sk{background-position:-80px -77px}.intl-tel-input .si{background-position:-96px 0px}.intl-tel-input .sh{background-position:-96px -11px}.intl-tel-input .sg{background-position:-96px -22px}.intl-tel-input .se{background-position:-96px -33px}.intl-tel-input .sd{background-position:-96px -44px}.intl-tel-input .sc{background-position:-96px -66px}.intl-tel-input .sb{background-position:-96px -77px}.intl-tel-input .sa{background-position:0px -88px}.intl-tel-input .rw{background-position:-16px -88px}.intl-tel-input .ru{background-position:-32px -88px}.intl-tel-input .rs{background-position:-48px -88px}.intl-tel-input .ro{background-position:-64px -88px}.intl-tel-input .qa{background-position:-80px -88px}.intl-tel-input .py{background-position:-96px -88px}.intl-tel-input .pw{background-position:0px -99px}.intl-tel-input .pt{background-position:-16px -99px}.intl-tel-input .ps{background-position:-32px -99px}.intl-tel-input .pr{background-position:-48px -99px}.intl-tel-input .pn{background-position:-64px -99px}.intl-tel-input .pm{background-position:-80px -99px}.intl-tel-input .pl{background-position:-96px -99px}.intl-tel-input .pk{background-position:-112px 0px}.intl-tel-input .ph{background-position:-112px -11px}.intl-tel-input .pg{background-position:-112px -22px}.intl-tel-input .pf{background-position:-112px -33px}.intl-tel-input .pe{background-position:-112px -44px}.intl-tel-input .pa{background-position:-112px -55px}.intl-tel-input .om{background-position:-112px -66px}.intl-tel-input .nz{background-position:-112px -77px}.intl-tel-input .nu{background-position:-112px -88px}.intl-tel-input .nr{background-position:-112px -99px}.intl-tel-input .no{background-position:0px -110px}.intl-tel-input .bv{background-position:0px -110px}.intl-tel-input .sj{background-position:0px -110px}.intl-tel-input .nl{background-position:-16px -110px}.intl-tel-input .ni{background-position:-32px -110px}.intl-tel-input .ng{background-position:-48px -110px}.intl-tel-input .nf{background-position:-64px -110px}.intl-tel-input .ne{background-position:-80px -110px}.intl-tel-input .nc{background-position:-96px -110px}.intl-tel-input .na{background-position:-112px -110px}.intl-tel-input .mz{background-position:-128px 0px}.intl-tel-input .my{background-position:-128px -11px}.intl-tel-input .mx{background-position:-128px -22px}.intl-tel-input .mw{background-position:-128px -33px}.intl-tel-input .mv{background-position:-128px -44px}.intl-tel-input .mu{background-position:-128px -55px}.intl-tel-input .mt{background-position:-128px -66px}.intl-tel-input .ms{background-position:-128px -77px}.intl-tel-input .mr{background-position:-128px -88px}.intl-tel-input .mq{background-position:-128px -99px}.intl-tel-input .mp{background-position:-128px -110px}.intl-tel-input .mo{background-position:0px -121px}.intl-tel-input .mn{background-position:-16px -121px}.intl-tel-input .mm{background-position:-32px -121px}.intl-tel-input .ml{background-position:-48px -121px}.intl-tel-input .mk{background-position:-64px -121px}.intl-tel-input .mh{background-position:-80px -121px}.intl-tel-input .mg{background-position:-96px -121px}.intl-tel-input .me{background-position:0px -132px;height:12px}.intl-tel-input .md{background-position:-112px -121px}.intl-tel-input .mc{background-position:-128px -121px}.intl-tel-input .ma{background-position:-16px -132px}.intl-tel-input .ly{background-position:-32px -132px}.intl-tel-input .lv{background-position:-48px -132px}.intl-tel-input .lu{background-position:-64px -132px}.intl-tel-input .lt{background-position:-80px -132px}.intl-tel-input .ls{background-position:-96px -132px}.intl-tel-input .lr{background-position:-112px -132px}.intl-tel-input .lk{background-position:-128px -132px}.intl-tel-input .li{background-position:-144px 0px}.intl-tel-input .lc{background-position:-144px -11px}.intl-tel-input .lb{background-position:-144px -22px}.intl-tel-input .la{background-position:-144px -33px}.intl-tel-input .kz{background-position:-144px -44px}.intl-tel-input .ky{background-position:-144px -55px}.intl-tel-input .kw{background-position:-144px -66px}.intl-tel-input .kr{background-position:-144px -77px}.intl-tel-input .kp{background-position:-144px -88px}.intl-tel-input .kn{background-position:-144px -99px}.intl-tel-input .km{background-position:-144px -110px}.intl-tel-input .ki{background-position:-144px -121px}.intl-tel-input .kh{background-position:-144px -132px}.intl-tel-input .kg{background-position:0px -144px}.intl-tel-input .ke{background-position:-16px -144px}.intl-tel-input .jp{background-position:-32px -144px}.intl-tel-input .jo{background-position:-48px -144px}.intl-tel-input .jm{background-position:-64px -144px}.intl-tel-input .je{background-position:-80px -144px}.intl-tel-input .it{background-position:-96px -144px}.intl-tel-input .is{background-position:-112px -144px}.intl-tel-input .ir{background-position:-128px -144px}.intl-tel-input .iq{background-position:-144px -144px}.intl-tel-input .io{background-position:-160px 0px}.intl-tel-input .in{background-position:-160px -11px}.intl-tel-input .im{background-position:-160px -22px;height:9px}.intl-tel-input .il{background-position:-160px -31px}.intl-tel-input .ie{background-position:-160px -42px}.intl-tel-input .id{background-position:-160px -53px}.intl-tel-input .hu{background-position:-160px -64px}.intl-tel-input .ht{background-position:-160px -75px}.intl-tel-input .hr{background-position:-160px -86px}.intl-tel-input .hn{background-position:-160px -97px}.intl-tel-input .hk{background-position:-160px -108px}.intl-tel-input .gy{background-position:-160px -119px}.intl-tel-input .gw{background-position:-160px -130px}.intl-tel-input .gu{background-position:-160px -141px}.intl-tel-input .gt{background-position:0px -155px}.intl-tel-input .gs{background-position:-16px -155px}.intl-tel-input .gr{background-position:-32px -155px}.intl-tel-input .gq{background-position:-48px -155px}.intl-tel-input .gp{background-position:-64px -155px}.intl-tel-input .gn{background-position:-80px -155px}.intl-tel-input .gm{background-position:-96px -155px}.intl-tel-input .gl{background-position:-112px -155px}.intl-tel-input .gi{background-position:-128px -155px}.intl-tel-input .gh{background-position:-144px -155px}.intl-tel-input .gg{background-position:-160px -155px}.intl-tel-input .ge{background-position:-176px 0px}.intl-tel-input .gd{background-position:-176px -11px}.intl-tel-input .gb{background-position:-176px -22px}.intl-tel-input .ga{background-position:-176px -33px}.intl-tel-input .fr{background-position:-176px -44px}.intl-tel-input .gf{background-position:-176px -44px}.intl-tel-input .re{background-position:-176px -44px}.intl-tel-input .mf{background-position:-176px -44px}.intl-tel-input .bl{background-position:-176px -44px}.intl-tel-input .fo{background-position:-176px -55px}.intl-tel-input .fm{background-position:-176px -66px}.intl-tel-input .fk{background-position:-176px -77px}.intl-tel-input .fj{background-position:-176px -88px}.intl-tel-input .fi{background-position:-176px -99px}.intl-tel-input .eu{background-position:-176px -121px}.intl-tel-input .et{background-position:-176px -132px}.intl-tel-input .es{background-position:-176px -143px}.intl-tel-input .er{background-position:-176px -154px}.intl-tel-input .eh{background-position:-16px -166px}.intl-tel-input .eg{background-position:-32px -166px}.intl-tel-input .ee{background-position:-48px -166px}.intl-tel-input .ec{background-position:-64px -166px}.intl-tel-input .dz{background-position:-80px -166px}.intl-tel-input .do{background-position:-96px -166px}.intl-tel-input .dm{background-position:-112px -166px}.intl-tel-input .dk{background-position:-128px -166px}.intl-tel-input .dj{background-position:-144px -166px}.intl-tel-input .de{background-position:-160px -166px}.intl-tel-input .cz{background-position:-176px -166px}.intl-tel-input .cy{background-position:0px -177px}.intl-tel-input .cx{background-position:-16px -177px}.intl-tel-input .cw{background-position:-32px -177px}.intl-tel-input .cv{background-position:-48px -177px}.intl-tel-input .cu{background-position:-64px -177px}.intl-tel-input .cs{background-position:-80px -177px}.intl-tel-input .cr{background-position:-96px -177px}.intl-tel-input .co{background-position:-112px -177px}.intl-tel-input .cn{background-position:-128px -177px}.intl-tel-input .cm{background-position:-144px -177px}.intl-tel-input .cl{background-position:-160px -177px}.intl-tel-input .ck{background-position:-176px -177px}.intl-tel-input .ci{background-position:-192px 0px}.intl-tel-input .cg{background-position:-192px -11px}.intl-tel-input .cf{background-position:-192px -22px}.intl-tel-input .cd{background-position:-192px -33px}.intl-tel-input .cc{background-position:-192px -44px}.intl-tel-input .ca{background-position:-192px -66px}.intl-tel-input .bz{background-position:-192px -77px}.intl-tel-input .by{background-position:-192px -88px}.intl-tel-input .bw{background-position:-192px -99px}.intl-tel-input .bt{background-position:-192px -110px}.intl-tel-input .bs{background-position:-192px -121px}.intl-tel-input .br{background-position:-192px -132px}.intl-tel-input .bq{background-position:-192px -143px}.intl-tel-input .bo{background-position:-192px -154px}.intl-tel-input .bn{background-position:-192px -165px}.intl-tel-input .bm{background-position:-192px -176px}.intl-tel-input .bj{background-position:0px -188px}.intl-tel-input .bi{background-position:-16px -188px}.intl-tel-input .bh{background-position:-32px -188px}.intl-tel-input .bg{background-position:-48px -188px}.intl-tel-input .bf{background-position:-64px -188px}.intl-tel-input .be{background-position:-80px -188px}.intl-tel-input .bd{background-position:-96px -188px}.intl-tel-input .bb{background-position:-112px -188px}.intl-tel-input .ba{background-position:-128px -188px}.intl-tel-input .az{background-position:-144px -188px}.intl-tel-input .ax{background-position:-160px -188px}.intl-tel-input .aw{background-position:-176px -188px}.intl-tel-input .au{background-position:-192px -188px}.intl-tel-input .hm{background-position:-192px -188px}.intl-tel-input .at{background-position:-208px 0px}.intl-tel-input .as{background-position:-208px -11px}.intl-tel-input .ar{background-position:-208px -22px}.intl-tel-input .ao{background-position:-208px -33px}.intl-tel-input .an{background-position:-208px -44px}.intl-tel-input .am{background-position:-208px -55px}.intl-tel-input .al{background-position:-208px -66px}.intl-tel-input .ai{background-position:-208px -77px}.intl-tel-input .ag{background-position:-208px -88px}.intl-tel-input .af{background-position:-208px -99px}.intl-tel-input .ae{background-position:-208px -110px}.intl-tel-input .ad{background-position:-208px -121px}.intl-tel-input .np{background-position:-208px -132px}.intl-tel-input .ch{background-position:-208px -143px}.intl-tel-input{position:relative;display:inline-block}.intl-tel-input *{box-sizing:border-box;-moz-box-sizing:border-box}.intl-tel-input .hide{display:none}.intl-tel-input .v-hide{visibility:hidden}.intl-tel-input input{position:relative;z-index:0;margin-top:0 !important;margin-bottom:0 !important}.intl-tel-input .flag-dropdown{position:absolute;top:0;bottom:0}.intl-tel-input .flag-dropdown:hover{cursor:pointer}.intl-tel-input .flag-dropdown:hover .selected-flag{background-color:rgba(0,0,0,0.05)}.intl-tel-input input[disabled]+.flag-dropdown:hover{cursor:default}.intl-tel-input input[disabled]+.flag-dropdown:hover .selected-flag{background-color:transparent}.intl-tel-input .selected-flag{z-index:1;position:relative;width:38px;height:100%;padding:0 0 0 8px}.intl-tel-input .selected-flag .flag{position:absolute;top:50%;margin-top:-5px}.intl-tel-input .selected-flag .arrow{position:relative;top:50%;margin-top:-2px;left:20px;width:0;height:0;border-left:3px solid transparent;border-right:3px solid transparent;border-top:4px solid #555}.intl-tel-input .selected-flag .arrow.up{border-top:none;border-bottom:4px solid #555}.intl-tel-input .country-list{list-style:none;position:absolute;z-index:2;padding:0;margin:0 0 0 -1px;box-shadow:1px 1px 4px rgba(0,0,0,0.2);background-color:white;border:1px solid #ccc;width:430px;max-height:200px;overflow-y:scroll}.intl-tel-input .country-list .flag{display:inline-block}.intl-tel-input .country-list .divider{padding-bottom:5px;margin-bottom:5px;border-bottom:1px solid #ccc}.intl-tel-input .country-list .country{padding:5px 10px}.intl-tel-input .country-list .country .dial-code{color:#999}.intl-tel-input .country-list .country.highlight{background-color:rgba(0,0,0,0.05)}.intl-tel-input .country-list .flag,.intl-tel-input .country-list .country-name{margin-right:6px}.intl-tel-input.inside .flag-dropdown{padding:1px}.intl-tel-input.inside input[type="text"],.intl-tel-input.inside input[type="tel"]{padding-left:44px;margin-left:0}.intl-tel-input.outside input[type="text"],.intl-tel-input.outside input[type="tel"]{border:none;margin-left:38px} 2 | -------------------------------------------------------------------------------- /assets/vender/intl-tel-input/img/flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonmbake/bootstrap3-contact-form/b5d1f2f9cd105001bba8e01a0b0372051c782a59/assets/vender/intl-tel-input/img/flags.png -------------------------------------------------------------------------------- /assets/vender/intl-tel-input/js/intlTelInput.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | International Telephone Input v1.2.1 3 | https://github.com/Bluefieldscom/intl-tel-input.git 4 | */ 5 | !function(a){"function"==typeof define&&define.amd?define(["jquery"],function(b){a(b,window,document)}):a(jQuery,window,document)}(function(a,b,c,d){"use strict";function e(b,c){this.element=b,this.options=a.extend({},h,c),this._defaults=h,this.ns="."+f+g++,this._name=f,this.init()}var f="intlTelInput",g=1,h={nationalMode:!1,autoHideDialCode:!0,defaultCountry:"",dialCodeDelimiter:" ",defaultStyling:"inside",onlyCountries:[],preferredCountries:["us","gb"],responsiveDropdown:!1,validationScript:""},i={UP:38,DOWN:40,ENTER:13,ESC:27,PLUS:43,A:65,Z:90,SPACE:32},j=!1;a(b).load(function(){j=!0}),e.prototype={init:function(){this._processCountryData(),this._generateMarkup(),this._setInitialState(),this._initListeners()},_processCountryData:function(){this._setInstanceCountryData(),this._setPreferredCountries()},_setInstanceCountryData:function(){var b=this;if(this.options.onlyCountries.length){var c=[],d={};a.each(this.options.onlyCountries,function(a,e){var f=b._getCountryData(e,!0);if(f){c.push(f);var g=f.dialCode;d[g]?d[g].push(e):d[g]=[e]}});for(var e in d)if(d[e].length>1){for(var f=[],g=0;g",{"class":b}));var c=a("
",{"class":"flag-dropdown"}).insertAfter(this.telInput),d=a("
",{"class":"selected-flag"}).appendTo(c);this.selectedFlagInner=a("
",{"class":"flag"}).appendTo(d),a("
",{"class":"arrow"}).appendTo(this.selectedFlagInner),this.countryList=a("
    ",{"class":"country-list v-hide"}).appendTo(c),this.preferredCountries.length&&(this._appendListItems(this.preferredCountries,"preferred"),a("
  • ",{"class":"divider"}).appendTo(this.countryList)),this._appendListItems(this.countries,""),this.dropdownHeight=this.countryList.outerHeight(),this.countryList.removeClass("v-hide").addClass("hide"),this.options.responsiveDropdown&&this.countryList.css("width",this.telInput.outerWidth()),this.countryListItems=this.countryList.children(".country")},_appendListItems:function(b,c){var d="";a.each(b,function(a,b){d+="
  • ",d+="
    ",d+=""+b.name+"",d+="+"+b.dialCode+"",d+="
  • "}),this.countryList.append(d)},_setInitialState:function(){var a=!1;if(this.telInput.val()&&(a=this._updateFlagFromInputVal()),!a){var b;b=this.options.defaultCountry?this._getCountryData(this.options.defaultCountry,!1):this.preferredCountries.length?this.preferredCountries[0]:this.countries[0],this._selectFlag(b.iso2),this.options.autoHideDialCode||this._resetToDialCode(b.dialCode)}},_initListeners:function(){var d=this;this.options.autoHideDialCode&&!this.options.nationalMode&&this._initAutoHideDialCode();var e=this.telInput.closest("label");e.length&&e.on("click",function(a){d.countryList.hasClass("hide")?d.telInput.focus():a.preventDefault()}),this.telInput.on("keyup"+this.ns,function(){d._updateFlagFromInputVal()});var f=this.selectedFlagInner.parent();if(f.on("click"+this.ns,function(){d.countryList.hasClass("hide")&&!d.telInput.prop("disabled")&&d._showDropdown()}),this.options.validationScript){var g=function(){var a=c.createElement("script");a.type="text/javascript",a.src=d.options.validationScript,c.body.appendChild(a)};j?g():a(b).load(g)}},_initAutoHideDialCode:function(){var b=this;this.telInput.on("mousedown"+this.ns,function(a){b.telInput.is(":focus")||b.telInput.val()||(a.preventDefault(),b._focus())}),this.telInput.on("focus"+this.ns,function(){if(!a.trim(b.telInput.val())){var c=b.getSelectedCountryData();b._resetToDialCode(c.dialCode),b.telInput.one("keypress"+b.ns,function(a){a.which==i.PLUS&&b.telInput.val("")})}}),this.telInput.on("blur"+this.ns,function(){var c=a.trim(b.telInput.val());c&&a.trim(b._getDialCode(c)+b.options.dialCodeDelimiter)==c&&b.telInput.val(""),b.telInput.off("keypress"+b.ns)})},_focus:function(){this.telInput.focus();var a=this.telInput[0];if(a.setSelectionRange){var b=this.telInput.val().length;a.setSelectionRange(b,b)}},_showDropdown:function(){this._setDropdownPosition();var a=this.countryList.children(".active");this._highlightListItem(a),this.countryList.removeClass("hide"),this._scrollTo(a),this._bindDropdownListeners(),this.selectedFlagInner.children(".arrow").addClass("up")},_setDropdownPosition:function(){var c=this.telInput.offset().top,d=a(b).scrollTop(),e=c+this.telInput.outerHeight()+this.dropdownHeightd,g=!e&&f?"-"+(this.dropdownHeight-1)+"px":"";this.countryList.css("top",g)},_bindDropdownListeners:function(){var b=this;this.countryList.on("mouseover"+this.ns,".country",function(){b._highlightListItem(a(this))}),this.countryList.on("click"+this.ns,".country",function(){b._selectListItem(a(this))});var d=!0;a("html").on("click"+this.ns,function(){d||b._closeDropdown(),d=!1});var e="",f=null;a(c).on("keydown"+this.ns,function(a){a.preventDefault(),a.which==i.UP||a.which==i.DOWN?b._handleUpDownKey(a.which):a.which==i.ENTER?b._handleEnterKey():a.which==i.ESC?b._closeDropdown():(a.which>=i.A&&a.which<=i.Z||a.which==i.SPACE)&&(f&&clearTimeout(f),e+=String.fromCharCode(a.which),b._searchForCountry(e),f=setTimeout(function(){e=""},1e3))})},_handleUpDownKey:function(a){var b=this.countryList.children(".highlight").first(),c=a==i.UP?b.prev():b.next();c.length&&(c.hasClass("divider")&&(c=a==i.UP?c.prev():c.next()),this._highlightListItem(c),this._scrollTo(c))},_handleEnterKey:function(){var a=this.countryList.children(".highlight").first();a.length&&this._selectListItem(a)},_searchForCountry:function(a){for(var b=0;bh)b&&(j-=k),c.scrollTop(j);else if(i>f){b&&(j+=k);var l=d-g;c.scrollTop(j-l)}},_updateNumber:function(b){var c,d=this.telInput.val(),e=this._getDialCode(d);if(e.length>1)c=d.replace(e,b),d==e&&(c+=this.options.dialCodeDelimiter);else{var f=d&&"+"!=d.substr(0,1)?a.trim(d):"";c=b+this.options.dialCodeDelimiter+f}this.telInput.val(c)},_getDialCode:function(b){var c="";if(b=a.trim(b),"+"==b.charAt(0))for(var d="",e=0;e 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Bootstrap 3 Contact Form 10 | 11 | 12 | 13 | 25 | 26 | 27 | 31 | 32 | 33 | 34 |
    35 |
    36 |
    37 |

    Contact Us

    38 |
    39 |
    40 | 41 |
    42 | 43 | 44 |
    45 | 46 |
    47 | 66 |
    67 | 68 | 73 | 74 |
    75 |
    76 | 77 |
    78 | 79 | 80 |
    81 | 82 |
    83 |
    84 | 85 |
    86 | 87 | 88 |
    89 | 90 |
    91 |
    92 |
    93 | 94 |
    95 | 96 | 97 |
    98 |
    99 |
    100 |
    101 |
    102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /library/sendmail.php: -------------------------------------------------------------------------------- 1 | $messsage))); 11 | } 12 | 13 | /** 14 | * Pulls posted values for all fields in $fields_req array. 15 | * If a required field does not have a value, an error response is given. 16 | */ 17 | function constructMessageBody () { 18 | $fields_req = array("name" => true, "email" => true, "message" => true); 19 | $message_body = ""; 20 | foreach ($fields_req as $name => $required) { 21 | $postedValue = $_POST[$name]; 22 | if ($required && empty($postedValue)) { 23 | errorResponse("$name is empty."); 24 | } else { 25 | $message_body .= ucfirst($name) . ": " . $postedValue . "\n"; 26 | } 27 | } 28 | return $message_body; 29 | } 30 | 31 | header('Content-type: application/json'); 32 | 33 | //do Captcha check, make sure the submitter is not a robot:)... 34 | $captcha_url = 'https://www.google.com/recaptcha/api/siteverify'; 35 | $captcha_header = 'Content-type: application/x-www-form-urlencoded'; 36 | $captcha_post_data = http_build_query(array('secret' => getenv('RECAPTCHA_SECRET_KEY'), 'response' => $_POST["g-recaptcha-response"])); 37 | // prefer cURL over #file_get_contents... 38 | if (function_exists('curl_init')) { 39 | $ch = curl_init(); 40 | curl_setopt($ch, CURLOPT_URL, $captcha_url); 41 | curl_setopt($ch, CURLOPT_POST, 1); 42 | curl_setopt($ch, CURLOPT_HTTPHEADER, array($captcha_header)); 43 | curl_setopt($ch, CURLOPT_POSTFIELDS, $captcha_post_data); 44 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 45 | $result = json_decode(curl_exec($ch)); 46 | curl_close($ch); 47 | } else { 48 | $opts = array('http' => 49 | array( 50 | 'method' => 'POST', 51 | 'header' => $captcha_header, 52 | 'content' => $captcha_post_data 53 | ) 54 | ); 55 | $context = stream_context_create($opts); 56 | $result = json_decode(file_get_contents($captcha_url, false, $context, -1, 40000)); 57 | } 58 | 59 | 60 | if (!$result->success) { 61 | errorResponse('reCAPTCHA checked failed! Error codes: ' . join(', ', $result->{"error-codes"})); 62 | } 63 | //attempt to send email 64 | $messageBody = constructMessageBody(); 65 | require './vender/php_mailer/PHPMailerAutoload.php'; 66 | $mail = new PHPMailer; 67 | $mail->CharSet = 'UTF-8'; 68 | $mail->isSMTP(); 69 | $mail->Host = getEnv('FEEDBACK_HOSTNAME'); 70 | if (!getenv('FEEDBACK_SKIP_AUTH')) { 71 | $mail->SMTPAuth = true; 72 | $mail->Username = getenv('FEEDBACK_EMAIL'); 73 | $mail->Password = getenv('FEEDBACK_PASSWORD'); 74 | } 75 | if (getenv('FEEDBACK_ENCRYPTION') == 'TLS') { 76 | $mail->SMTPSecure = 'tls'; 77 | $mail->Port = 587; 78 | } elseif (getenv('FEEDBACK_ENCRYPTION') == 'SSL') { 79 | $mail->SMTPSecure = 'ssl'; 80 | $mail->Port = 465; 81 | } 82 | 83 | $mail->Sender = getenv('FEEDBACK_EMAIL'); 84 | $mail->setFrom($_POST['email'], $_POST['name']); 85 | $mail->addAddress(getenv('FEEDBACK_EMAIL')); 86 | 87 | $mail->Subject = $_POST['reason']; 88 | $mail->Body = $messageBody; 89 | 90 | 91 | //try to send the message 92 | if($mail->send()) { 93 | echo json_encode(array('message' => 'Your message was successfully submitted.')); 94 | } else { 95 | errorResponse('An unexpected error occured while attempting to send the email: ' . $mail->ErrorInfo); 96 | } 97 | ?> 98 | -------------------------------------------------------------------------------- /library/vender/php_mailer/PHPMailerAutoload.php: -------------------------------------------------------------------------------- 1 | 8 | * @author Jim Jagielski (jimjag) 9 | * @author Andy Prevost (codeworxtech) 10 | * @author Brent R. Matzelle (original founder) 11 | * @copyright 2012 - 2014 Marcus Bointon 12 | * @copyright 2010 - 2012 Jim Jagielski 13 | * @copyright 2004 - 2009 Andy Prevost 14 | * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 15 | * @note This program is distributed in the hope that it will be useful - WITHOUT 16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | * FITNESS FOR A PARTICULAR PURPOSE. 18 | */ 19 | 20 | /** 21 | * PHPMailer SPL autoloader. 22 | * @param string $classname The name of the class to load 23 | */ 24 | function PHPMailerAutoload($classname) 25 | { 26 | //Can't use __DIR__ as it's only in PHP 5.3+ 27 | $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'class.'.strtolower($classname).'.php'; 28 | if (is_readable($filename)) { 29 | require $filename; 30 | } 31 | } 32 | 33 | if (version_compare(PHP_VERSION, '5.1.2', '>=')) { 34 | //SPL autoloading was introduced in PHP 5.1.2 35 | if (version_compare(PHP_VERSION, '5.3.0', '>=')) { 36 | spl_autoload_register('PHPMailerAutoload', true, true); 37 | } else { 38 | spl_autoload_register('PHPMailerAutoload'); 39 | } 40 | } else { 41 | /** 42 | * Fall back to traditional autoload for old PHP versions 43 | * @param string $classname The name of the class to load 44 | */ 45 | function __autoload($classname) 46 | { 47 | PHPMailerAutoload($classname); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /library/vender/php_mailer/class.pop3.php: -------------------------------------------------------------------------------- 1 | 8 | * @author Jim Jagielski (jimjag) 9 | * @author Andy Prevost (codeworxtech) 10 | * @author Brent R. Matzelle (original founder) 11 | * @copyright 2012 - 2014 Marcus Bointon 12 | * @copyright 2010 - 2012 Jim Jagielski 13 | * @copyright 2004 - 2009 Andy Prevost 14 | * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 15 | * @note This program is distributed in the hope that it will be useful - WITHOUT 16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | * FITNESS FOR A PARTICULAR PURPOSE. 18 | */ 19 | 20 | /** 21 | * PHPMailer POP-Before-SMTP Authentication Class. 22 | * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication. 23 | * Does not support APOP. 24 | * @package PHPMailer 25 | * @author Richard Davey (original author) 26 | * @author Marcus Bointon (Synchro/coolbru) 27 | * @author Jim Jagielski (jimjag) 28 | * @author Andy Prevost (codeworxtech) 29 | */ 30 | class POP3 31 | { 32 | /** 33 | * The POP3 PHPMailer Version number. 34 | * @var string 35 | * @access public 36 | */ 37 | public $Version = '5.2.21'; 38 | 39 | /** 40 | * Default POP3 port number. 41 | * @var integer 42 | * @access public 43 | */ 44 | public $POP3_PORT = 110; 45 | 46 | /** 47 | * Default timeout in seconds. 48 | * @var integer 49 | * @access public 50 | */ 51 | public $POP3_TIMEOUT = 30; 52 | 53 | /** 54 | * POP3 Carriage Return + Line Feed. 55 | * @var string 56 | * @access public 57 | * @deprecated Use the constant instead 58 | */ 59 | public $CRLF = "\r\n"; 60 | 61 | /** 62 | * Debug display level. 63 | * Options: 0 = no, 1+ = yes 64 | * @var integer 65 | * @access public 66 | */ 67 | public $do_debug = 0; 68 | 69 | /** 70 | * POP3 mail server hostname. 71 | * @var string 72 | * @access public 73 | */ 74 | public $host; 75 | 76 | /** 77 | * POP3 port number. 78 | * @var integer 79 | * @access public 80 | */ 81 | public $port; 82 | 83 | /** 84 | * POP3 Timeout Value in seconds. 85 | * @var integer 86 | * @access public 87 | */ 88 | public $tval; 89 | 90 | /** 91 | * POP3 username 92 | * @var string 93 | * @access public 94 | */ 95 | public $username; 96 | 97 | /** 98 | * POP3 password. 99 | * @var string 100 | * @access public 101 | */ 102 | public $password; 103 | 104 | /** 105 | * Resource handle for the POP3 connection socket. 106 | * @var resource 107 | * @access protected 108 | */ 109 | protected $pop_conn; 110 | 111 | /** 112 | * Are we connected? 113 | * @var boolean 114 | * @access protected 115 | */ 116 | protected $connected = false; 117 | 118 | /** 119 | * Error container. 120 | * @var array 121 | * @access protected 122 | */ 123 | protected $errors = array(); 124 | 125 | /** 126 | * Line break constant 127 | */ 128 | const CRLF = "\r\n"; 129 | 130 | /** 131 | * Simple static wrapper for all-in-one POP before SMTP 132 | * @param $host 133 | * @param integer|boolean $port The port number to connect to 134 | * @param integer|boolean $timeout The timeout value 135 | * @param string $username 136 | * @param string $password 137 | * @param integer $debug_level 138 | * @return boolean 139 | */ 140 | public static function popBeforeSmtp( 141 | $host, 142 | $port = false, 143 | $timeout = false, 144 | $username = '', 145 | $password = '', 146 | $debug_level = 0 147 | ) { 148 | $pop = new POP3; 149 | return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level); 150 | } 151 | 152 | /** 153 | * Authenticate with a POP3 server. 154 | * A connect, login, disconnect sequence 155 | * appropriate for POP-before SMTP authorisation. 156 | * @access public 157 | * @param string $host The hostname to connect to 158 | * @param integer|boolean $port The port number to connect to 159 | * @param integer|boolean $timeout The timeout value 160 | * @param string $username 161 | * @param string $password 162 | * @param integer $debug_level 163 | * @return boolean 164 | */ 165 | public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0) 166 | { 167 | $this->host = $host; 168 | // If no port value provided, use default 169 | if (false === $port) { 170 | $this->port = $this->POP3_PORT; 171 | } else { 172 | $this->port = (integer)$port; 173 | } 174 | // If no timeout value provided, use default 175 | if (false === $timeout) { 176 | $this->tval = $this->POP3_TIMEOUT; 177 | } else { 178 | $this->tval = (integer)$timeout; 179 | } 180 | $this->do_debug = $debug_level; 181 | $this->username = $username; 182 | $this->password = $password; 183 | // Reset the error log 184 | $this->errors = array(); 185 | // connect 186 | $result = $this->connect($this->host, $this->port, $this->tval); 187 | if ($result) { 188 | $login_result = $this->login($this->username, $this->password); 189 | if ($login_result) { 190 | $this->disconnect(); 191 | return true; 192 | } 193 | } 194 | // We need to disconnect regardless of whether the login succeeded 195 | $this->disconnect(); 196 | return false; 197 | } 198 | 199 | /** 200 | * Connect to a POP3 server. 201 | * @access public 202 | * @param string $host 203 | * @param integer|boolean $port 204 | * @param integer $tval 205 | * @return boolean 206 | */ 207 | public function connect($host, $port = false, $tval = 30) 208 | { 209 | // Are we already connected? 210 | if ($this->connected) { 211 | return true; 212 | } 213 | 214 | //On Windows this will raise a PHP Warning error if the hostname doesn't exist. 215 | //Rather than suppress it with @fsockopen, capture it cleanly instead 216 | set_error_handler(array($this, 'catchWarning')); 217 | 218 | if (false === $port) { 219 | $port = $this->POP3_PORT; 220 | } 221 | 222 | // connect to the POP3 server 223 | $this->pop_conn = fsockopen( 224 | $host, // POP3 Host 225 | $port, // Port # 226 | $errno, // Error Number 227 | $errstr, // Error Message 228 | $tval 229 | ); // Timeout (seconds) 230 | // Restore the error handler 231 | restore_error_handler(); 232 | 233 | // Did we connect? 234 | if (false === $this->pop_conn) { 235 | // It would appear not... 236 | $this->setError(array( 237 | 'error' => "Failed to connect to server $host on port $port", 238 | 'errno' => $errno, 239 | 'errstr' => $errstr 240 | )); 241 | return false; 242 | } 243 | 244 | // Increase the stream time-out 245 | stream_set_timeout($this->pop_conn, $tval, 0); 246 | 247 | // Get the POP3 server response 248 | $pop3_response = $this->getResponse(); 249 | // Check for the +OK 250 | if ($this->checkResponse($pop3_response)) { 251 | // The connection is established and the POP3 server is talking 252 | $this->connected = true; 253 | return true; 254 | } 255 | return false; 256 | } 257 | 258 | /** 259 | * Log in to the POP3 server. 260 | * Does not support APOP (RFC 2828, 4949). 261 | * @access public 262 | * @param string $username 263 | * @param string $password 264 | * @return boolean 265 | */ 266 | public function login($username = '', $password = '') 267 | { 268 | if (!$this->connected) { 269 | $this->setError('Not connected to POP3 server'); 270 | } 271 | if (empty($username)) { 272 | $username = $this->username; 273 | } 274 | if (empty($password)) { 275 | $password = $this->password; 276 | } 277 | 278 | // Send the Username 279 | $this->sendString("USER $username" . self::CRLF); 280 | $pop3_response = $this->getResponse(); 281 | if ($this->checkResponse($pop3_response)) { 282 | // Send the Password 283 | $this->sendString("PASS $password" . self::CRLF); 284 | $pop3_response = $this->getResponse(); 285 | if ($this->checkResponse($pop3_response)) { 286 | return true; 287 | } 288 | } 289 | return false; 290 | } 291 | 292 | /** 293 | * Disconnect from the POP3 server. 294 | * @access public 295 | */ 296 | public function disconnect() 297 | { 298 | $this->sendString('QUIT'); 299 | //The QUIT command may cause the daemon to exit, which will kill our connection 300 | //So ignore errors here 301 | try { 302 | @fclose($this->pop_conn); 303 | } catch (Exception $e) { 304 | //Do nothing 305 | }; 306 | } 307 | 308 | /** 309 | * Get a response from the POP3 server. 310 | * $size is the maximum number of bytes to retrieve 311 | * @param integer $size 312 | * @return string 313 | * @access protected 314 | */ 315 | protected function getResponse($size = 128) 316 | { 317 | $response = fgets($this->pop_conn, $size); 318 | if ($this->do_debug >= 1) { 319 | echo "Server -> Client: $response"; 320 | } 321 | return $response; 322 | } 323 | 324 | /** 325 | * Send raw data to the POP3 server. 326 | * @param string $string 327 | * @return integer 328 | * @access protected 329 | */ 330 | protected function sendString($string) 331 | { 332 | if ($this->pop_conn) { 333 | if ($this->do_debug >= 2) { //Show client messages when debug >= 2 334 | echo "Client -> Server: $string"; 335 | } 336 | return fwrite($this->pop_conn, $string, strlen($string)); 337 | } 338 | return 0; 339 | } 340 | 341 | /** 342 | * Checks the POP3 server response. 343 | * Looks for for +OK or -ERR. 344 | * @param string $string 345 | * @return boolean 346 | * @access protected 347 | */ 348 | protected function checkResponse($string) 349 | { 350 | if (substr($string, 0, 3) !== '+OK') { 351 | $this->setError(array( 352 | 'error' => "Server reported an error: $string", 353 | 'errno' => 0, 354 | 'errstr' => '' 355 | )); 356 | return false; 357 | } else { 358 | return true; 359 | } 360 | } 361 | 362 | /** 363 | * Add an error to the internal error store. 364 | * Also display debug output if it's enabled. 365 | * @param $error 366 | * @access protected 367 | */ 368 | protected function setError($error) 369 | { 370 | $this->errors[] = $error; 371 | if ($this->do_debug >= 1) { 372 | echo '
    ';
    373 |             foreach ($this->errors as $error) {
    374 |                 print_r($error);
    375 |             }
    376 |             echo '
    '; 377 | } 378 | } 379 | 380 | /** 381 | * Get an array of error messages, if any. 382 | * @return array 383 | */ 384 | public function getErrors() 385 | { 386 | return $this->errors; 387 | } 388 | 389 | /** 390 | * POP3 connection error handler. 391 | * @param integer $errno 392 | * @param string $errstr 393 | * @param string $errfile 394 | * @param integer $errline 395 | * @access protected 396 | */ 397 | protected function catchWarning($errno, $errstr, $errfile, $errline) 398 | { 399 | $this->setError(array( 400 | 'error' => "Connecting to the POP3 server raised a PHP warning: ", 401 | 'errno' => $errno, 402 | 'errstr' => $errstr, 403 | 'errfile' => $errfile, 404 | 'errline' => $errline 405 | )); 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /library/vender/php_mailer/class.smtp.php: -------------------------------------------------------------------------------- 1 | 8 | * @author Jim Jagielski (jimjag) 9 | * @author Andy Prevost (codeworxtech) 10 | * @author Brent R. Matzelle (original founder) 11 | * @copyright 2014 Marcus Bointon 12 | * @copyright 2010 - 2012 Jim Jagielski 13 | * @copyright 2004 - 2009 Andy Prevost 14 | * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License 15 | * @note This program is distributed in the hope that it will be useful - WITHOUT 16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 | * FITNESS FOR A PARTICULAR PURPOSE. 18 | */ 19 | 20 | /** 21 | * PHPMailer RFC821 SMTP email transport class. 22 | * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. 23 | * @package PHPMailer 24 | * @author Chris Ryan 25 | * @author Marcus Bointon 26 | */ 27 | class SMTP 28 | { 29 | /** 30 | * The PHPMailer SMTP version number. 31 | * @var string 32 | */ 33 | const VERSION = '5.2.21'; 34 | 35 | /** 36 | * SMTP line break constant. 37 | * @var string 38 | */ 39 | const CRLF = "\r\n"; 40 | 41 | /** 42 | * The SMTP port to use if one is not specified. 43 | * @var integer 44 | */ 45 | const DEFAULT_SMTP_PORT = 25; 46 | 47 | /** 48 | * The maximum line length allowed by RFC 2822 section 2.1.1 49 | * @var integer 50 | */ 51 | const MAX_LINE_LENGTH = 998; 52 | 53 | /** 54 | * Debug level for no output 55 | */ 56 | const DEBUG_OFF = 0; 57 | 58 | /** 59 | * Debug level to show client -> server messages 60 | */ 61 | const DEBUG_CLIENT = 1; 62 | 63 | /** 64 | * Debug level to show client -> server and server -> client messages 65 | */ 66 | const DEBUG_SERVER = 2; 67 | 68 | /** 69 | * Debug level to show connection status, client -> server and server -> client messages 70 | */ 71 | const DEBUG_CONNECTION = 3; 72 | 73 | /** 74 | * Debug level to show all messages 75 | */ 76 | const DEBUG_LOWLEVEL = 4; 77 | 78 | /** 79 | * The PHPMailer SMTP Version number. 80 | * @var string 81 | * @deprecated Use the `VERSION` constant instead 82 | * @see SMTP::VERSION 83 | */ 84 | public $Version = '5.2.21'; 85 | 86 | /** 87 | * SMTP server port number. 88 | * @var integer 89 | * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead 90 | * @see SMTP::DEFAULT_SMTP_PORT 91 | */ 92 | public $SMTP_PORT = 25; 93 | 94 | /** 95 | * SMTP reply line ending. 96 | * @var string 97 | * @deprecated Use the `CRLF` constant instead 98 | * @see SMTP::CRLF 99 | */ 100 | public $CRLF = "\r\n"; 101 | 102 | /** 103 | * Debug output level. 104 | * Options: 105 | * * self::DEBUG_OFF (`0`) No debug output, default 106 | * * self::DEBUG_CLIENT (`1`) Client commands 107 | * * self::DEBUG_SERVER (`2`) Client commands and server responses 108 | * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status 109 | * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages 110 | * @var integer 111 | */ 112 | public $do_debug = self::DEBUG_OFF; 113 | 114 | /** 115 | * How to handle debug output. 116 | * Options: 117 | * * `echo` Output plain-text as-is, appropriate for CLI 118 | * * `html` Output escaped, line breaks converted to `
    `, appropriate for browser output 119 | * * `error_log` Output to error log as configured in php.ini 120 | * 121 | * Alternatively, you can provide a callable expecting two params: a message string and the debug level: 122 | * 123 | * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; 124 | * 125 | * @var string|callable 126 | */ 127 | public $Debugoutput = 'echo'; 128 | 129 | /** 130 | * Whether to use VERP. 131 | * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path 132 | * @link http://www.postfix.org/VERP_README.html Info on VERP 133 | * @var boolean 134 | */ 135 | public $do_verp = false; 136 | 137 | /** 138 | * The timeout value for connection, in seconds. 139 | * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 140 | * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. 141 | * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 142 | * @var integer 143 | */ 144 | public $Timeout = 300; 145 | 146 | /** 147 | * How long to wait for commands to complete, in seconds. 148 | * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 149 | * @var integer 150 | */ 151 | public $Timelimit = 300; 152 | 153 | /** 154 | * @var array patterns to extract smtp transaction id from smtp reply 155 | * Only first capture group will be use, use non-capturing group to deal with it 156 | * Extend this class to override this property to fulfil your needs. 157 | */ 158 | protected $smtp_transaction_id_patterns = array( 159 | 'exim' => '/[0-9]{3} OK id=(.*)/', 160 | 'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/', 161 | 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' 162 | ); 163 | 164 | /** 165 | * The socket for the server connection. 166 | * @var resource 167 | */ 168 | protected $smtp_conn; 169 | 170 | /** 171 | * Error information, if any, for the last SMTP command. 172 | * @var array 173 | */ 174 | protected $error = array( 175 | 'error' => '', 176 | 'detail' => '', 177 | 'smtp_code' => '', 178 | 'smtp_code_ex' => '' 179 | ); 180 | 181 | /** 182 | * The reply the server sent to us for HELO. 183 | * If null, no HELO string has yet been received. 184 | * @var string|null 185 | */ 186 | protected $helo_rply = null; 187 | 188 | /** 189 | * The set of SMTP extensions sent in reply to EHLO command. 190 | * Indexes of the array are extension names. 191 | * Value at index 'HELO' or 'EHLO' (according to command that was sent) 192 | * represents the server name. In case of HELO it is the only element of the array. 193 | * Other values can be boolean TRUE or an array containing extension options. 194 | * If null, no HELO/EHLO string has yet been received. 195 | * @var array|null 196 | */ 197 | protected $server_caps = null; 198 | 199 | /** 200 | * The most recent reply received from the server. 201 | * @var string 202 | */ 203 | protected $last_reply = ''; 204 | 205 | /** 206 | * Output debugging info via a user-selected method. 207 | * @see SMTP::$Debugoutput 208 | * @see SMTP::$do_debug 209 | * @param string $str Debug string to output 210 | * @param integer $level The debug level of this message; see DEBUG_* constants 211 | * @return void 212 | */ 213 | protected function edebug($str, $level = 0) 214 | { 215 | if ($level > $this->do_debug) { 216 | return; 217 | } 218 | //Avoid clash with built-in function names 219 | if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { 220 | call_user_func($this->Debugoutput, $str, $level); 221 | return; 222 | } 223 | switch ($this->Debugoutput) { 224 | case 'error_log': 225 | //Don't output, just log 226 | error_log($str); 227 | break; 228 | case 'html': 229 | //Cleans up output a bit for a better looking, HTML-safe output 230 | echo htmlentities( 231 | preg_replace('/[\r\n]+/', '', $str), 232 | ENT_QUOTES, 233 | 'UTF-8' 234 | ) 235 | . "
    \n"; 236 | break; 237 | case 'echo': 238 | default: 239 | //Normalize line breaks 240 | $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); 241 | echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( 242 | "\n", 243 | "\n \t ", 244 | trim($str) 245 | )."\n"; 246 | } 247 | } 248 | 249 | /** 250 | * Connect to an SMTP server. 251 | * @param string $host SMTP server IP or host name 252 | * @param integer $port The port number to connect to 253 | * @param integer $timeout How long to wait for the connection to open 254 | * @param array $options An array of options for stream_context_create() 255 | * @access public 256 | * @return boolean 257 | */ 258 | public function connect($host, $port = null, $timeout = 30, $options = array()) 259 | { 260 | static $streamok; 261 | //This is enabled by default since 5.0.0 but some providers disable it 262 | //Check this once and cache the result 263 | if (is_null($streamok)) { 264 | $streamok = function_exists('stream_socket_client'); 265 | } 266 | // Clear errors to avoid confusion 267 | $this->setError(''); 268 | // Make sure we are __not__ connected 269 | if ($this->connected()) { 270 | // Already connected, generate error 271 | $this->setError('Already connected to a server'); 272 | return false; 273 | } 274 | if (empty($port)) { 275 | $port = self::DEFAULT_SMTP_PORT; 276 | } 277 | // Connect to the SMTP server 278 | $this->edebug( 279 | "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true), 280 | self::DEBUG_CONNECTION 281 | ); 282 | $errno = 0; 283 | $errstr = ''; 284 | if ($streamok) { 285 | $socket_context = stream_context_create($options); 286 | set_error_handler(array($this, 'errorHandler')); 287 | $this->smtp_conn = stream_socket_client( 288 | $host . ":" . $port, 289 | $errno, 290 | $errstr, 291 | $timeout, 292 | STREAM_CLIENT_CONNECT, 293 | $socket_context 294 | ); 295 | restore_error_handler(); 296 | } else { 297 | //Fall back to fsockopen which should work in more places, but is missing some features 298 | $this->edebug( 299 | "Connection: stream_socket_client not available, falling back to fsockopen", 300 | self::DEBUG_CONNECTION 301 | ); 302 | set_error_handler(array($this, 'errorHandler')); 303 | $this->smtp_conn = fsockopen( 304 | $host, 305 | $port, 306 | $errno, 307 | $errstr, 308 | $timeout 309 | ); 310 | restore_error_handler(); 311 | } 312 | // Verify we connected properly 313 | if (!is_resource($this->smtp_conn)) { 314 | $this->setError( 315 | 'Failed to connect to server', 316 | $errno, 317 | $errstr 318 | ); 319 | $this->edebug( 320 | 'SMTP ERROR: ' . $this->error['error'] 321 | . ": $errstr ($errno)", 322 | self::DEBUG_CLIENT 323 | ); 324 | return false; 325 | } 326 | $this->edebug('Connection: opened', self::DEBUG_CONNECTION); 327 | // SMTP server can take longer to respond, give longer timeout for first read 328 | // Windows does not have support for this timeout function 329 | if (substr(PHP_OS, 0, 3) != 'WIN') { 330 | $max = ini_get('max_execution_time'); 331 | // Don't bother if unlimited 332 | if ($max != 0 && $timeout > $max) { 333 | @set_time_limit($timeout); 334 | } 335 | stream_set_timeout($this->smtp_conn, $timeout, 0); 336 | } 337 | // Get any announcement 338 | $announce = $this->get_lines(); 339 | $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); 340 | return true; 341 | } 342 | 343 | /** 344 | * Initiate a TLS (encrypted) session. 345 | * @access public 346 | * @return boolean 347 | */ 348 | public function startTLS() 349 | { 350 | if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { 351 | return false; 352 | } 353 | 354 | //Allow the best TLS version(s) we can 355 | $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; 356 | 357 | //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT 358 | //so add them back in manually if we can 359 | if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { 360 | $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; 361 | $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; 362 | } 363 | 364 | // Begin encrypted connection 365 | if (!stream_socket_enable_crypto( 366 | $this->smtp_conn, 367 | true, 368 | $crypto_method 369 | )) { 370 | return false; 371 | } 372 | return true; 373 | } 374 | 375 | /** 376 | * Perform SMTP authentication. 377 | * Must be run after hello(). 378 | * @see hello() 379 | * @param string $username The user name 380 | * @param string $password The password 381 | * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2) 382 | * @param string $realm The auth realm for NTLM 383 | * @param string $workstation The auth workstation for NTLM 384 | * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth) 385 | * @return bool True if successfully authenticated.* @access public 386 | */ 387 | public function authenticate( 388 | $username, 389 | $password, 390 | $authtype = null, 391 | $realm = '', 392 | $workstation = '', 393 | $OAuth = null 394 | ) { 395 | if (!$this->server_caps) { 396 | $this->setError('Authentication is not allowed before HELO/EHLO'); 397 | return false; 398 | } 399 | 400 | if (array_key_exists('EHLO', $this->server_caps)) { 401 | // SMTP extensions are available. Let's try to find a proper authentication method 402 | 403 | if (!array_key_exists('AUTH', $this->server_caps)) { 404 | $this->setError('Authentication is not allowed at this stage'); 405 | // 'at this stage' means that auth may be allowed after the stage changes 406 | // e.g. after STARTTLS 407 | return false; 408 | } 409 | 410 | self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL); 411 | self::edebug( 412 | 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), 413 | self::DEBUG_LOWLEVEL 414 | ); 415 | 416 | if (empty($authtype)) { 417 | foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN', 'NTLM', 'XOAUTH2') as $method) { 418 | if (in_array($method, $this->server_caps['AUTH'])) { 419 | $authtype = $method; 420 | break; 421 | } 422 | } 423 | if (empty($authtype)) { 424 | $this->setError('No supported authentication methods found'); 425 | return false; 426 | } 427 | self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL); 428 | } 429 | 430 | if (!in_array($authtype, $this->server_caps['AUTH'])) { 431 | $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); 432 | return false; 433 | } 434 | } elseif (empty($authtype)) { 435 | $authtype = 'LOGIN'; 436 | } 437 | switch ($authtype) { 438 | case 'PLAIN': 439 | // Start authentication 440 | if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { 441 | return false; 442 | } 443 | // Send encoded username and password 444 | if (!$this->sendCommand( 445 | 'User & Password', 446 | base64_encode("\0" . $username . "\0" . $password), 447 | 235 448 | ) 449 | ) { 450 | return false; 451 | } 452 | break; 453 | case 'LOGIN': 454 | // Start authentication 455 | if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { 456 | return false; 457 | } 458 | if (!$this->sendCommand("Username", base64_encode($username), 334)) { 459 | return false; 460 | } 461 | if (!$this->sendCommand("Password", base64_encode($password), 235)) { 462 | return false; 463 | } 464 | break; 465 | case 'XOAUTH2': 466 | //If the OAuth Instance is not set. Can be a case when PHPMailer is used 467 | //instead of PHPMailerOAuth 468 | if (is_null($OAuth)) { 469 | return false; 470 | } 471 | $oauth = $OAuth->getOauth64(); 472 | 473 | // Start authentication 474 | if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { 475 | return false; 476 | } 477 | break; 478 | case 'NTLM': 479 | /* 480 | * ntlm_sasl_client.php 481 | * Bundled with Permission 482 | * 483 | * How to telnet in windows: 484 | * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx 485 | * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication 486 | */ 487 | require_once 'extras/ntlm_sasl_client.php'; 488 | $temp = new stdClass; 489 | $ntlm_client = new ntlm_sasl_client_class; 490 | //Check that functions are available 491 | if (!$ntlm_client->initialize($temp)) { 492 | $this->setError($temp->error); 493 | $this->edebug( 494 | 'You need to enable some modules in your php.ini file: ' 495 | . $this->error['error'], 496 | self::DEBUG_CLIENT 497 | ); 498 | return false; 499 | } 500 | //msg1 501 | $msg1 = $ntlm_client->typeMsg1($realm, $workstation); //msg1 502 | 503 | if (!$this->sendCommand( 504 | 'AUTH NTLM', 505 | 'AUTH NTLM ' . base64_encode($msg1), 506 | 334 507 | ) 508 | ) { 509 | return false; 510 | } 511 | //Though 0 based, there is a white space after the 3 digit number 512 | //msg2 513 | $challenge = substr($this->last_reply, 3); 514 | $challenge = base64_decode($challenge); 515 | $ntlm_res = $ntlm_client->NTLMResponse( 516 | substr($challenge, 24, 8), 517 | $password 518 | ); 519 | //msg3 520 | $msg3 = $ntlm_client->typeMsg3( 521 | $ntlm_res, 522 | $username, 523 | $realm, 524 | $workstation 525 | ); 526 | // send encoded username 527 | return $this->sendCommand('Username', base64_encode($msg3), 235); 528 | case 'CRAM-MD5': 529 | // Start authentication 530 | if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { 531 | return false; 532 | } 533 | // Get the challenge 534 | $challenge = base64_decode(substr($this->last_reply, 4)); 535 | 536 | // Build the response 537 | $response = $username . ' ' . $this->hmac($challenge, $password); 538 | 539 | // send encoded credentials 540 | return $this->sendCommand('Username', base64_encode($response), 235); 541 | default: 542 | $this->setError("Authentication method \"$authtype\" is not supported"); 543 | return false; 544 | } 545 | return true; 546 | } 547 | 548 | /** 549 | * Calculate an MD5 HMAC hash. 550 | * Works like hash_hmac('md5', $data, $key) 551 | * in case that function is not available 552 | * @param string $data The data to hash 553 | * @param string $key The key to hash with 554 | * @access protected 555 | * @return string 556 | */ 557 | protected function hmac($data, $key) 558 | { 559 | if (function_exists('hash_hmac')) { 560 | return hash_hmac('md5', $data, $key); 561 | } 562 | 563 | // The following borrowed from 564 | // http://php.net/manual/en/function.mhash.php#27225 565 | 566 | // RFC 2104 HMAC implementation for php. 567 | // Creates an md5 HMAC. 568 | // Eliminates the need to install mhash to compute a HMAC 569 | // by Lance Rushing 570 | 571 | $bytelen = 64; // byte length for md5 572 | if (strlen($key) > $bytelen) { 573 | $key = pack('H*', md5($key)); 574 | } 575 | $key = str_pad($key, $bytelen, chr(0x00)); 576 | $ipad = str_pad('', $bytelen, chr(0x36)); 577 | $opad = str_pad('', $bytelen, chr(0x5c)); 578 | $k_ipad = $key ^ $ipad; 579 | $k_opad = $key ^ $opad; 580 | 581 | return md5($k_opad . pack('H*', md5($k_ipad . $data))); 582 | } 583 | 584 | /** 585 | * Check connection state. 586 | * @access public 587 | * @return boolean True if connected. 588 | */ 589 | public function connected() 590 | { 591 | if (is_resource($this->smtp_conn)) { 592 | $sock_status = stream_get_meta_data($this->smtp_conn); 593 | if ($sock_status['eof']) { 594 | // The socket is valid but we are not connected 595 | $this->edebug( 596 | 'SMTP NOTICE: EOF caught while checking if connected', 597 | self::DEBUG_CLIENT 598 | ); 599 | $this->close(); 600 | return false; 601 | } 602 | return true; // everything looks good 603 | } 604 | return false; 605 | } 606 | 607 | /** 608 | * Close the socket and clean up the state of the class. 609 | * Don't use this function without first trying to use QUIT. 610 | * @see quit() 611 | * @access public 612 | * @return void 613 | */ 614 | public function close() 615 | { 616 | $this->setError(''); 617 | $this->server_caps = null; 618 | $this->helo_rply = null; 619 | if (is_resource($this->smtp_conn)) { 620 | // close the connection and cleanup 621 | fclose($this->smtp_conn); 622 | $this->smtp_conn = null; //Makes for cleaner serialization 623 | $this->edebug('Connection: closed', self::DEBUG_CONNECTION); 624 | } 625 | } 626 | 627 | /** 628 | * Send an SMTP DATA command. 629 | * Issues a data command and sends the msg_data to the server, 630 | * finializing the mail transaction. $msg_data is the message 631 | * that is to be send with the headers. Each header needs to be 632 | * on a single line followed by a with the message headers 633 | * and the message body being separated by and additional . 634 | * Implements rfc 821: DATA 635 | * @param string $msg_data Message data to send 636 | * @access public 637 | * @return boolean 638 | */ 639 | public function data($msg_data) 640 | { 641 | //This will use the standard timelimit 642 | if (!$this->sendCommand('DATA', 'DATA', 354)) { 643 | return false; 644 | } 645 | 646 | /* The server is ready to accept data! 647 | * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF) 648 | * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into 649 | * smaller lines to fit within the limit. 650 | * We will also look for lines that start with a '.' and prepend an additional '.'. 651 | * NOTE: this does not count towards line-length limit. 652 | */ 653 | 654 | // Normalize line breaks before exploding 655 | $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); 656 | 657 | /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field 658 | * of the first line (':' separated) does not contain a space then it _should_ be a header and we will 659 | * process all lines before a blank line as headers. 660 | */ 661 | 662 | $field = substr($lines[0], 0, strpos($lines[0], ':')); 663 | $in_headers = false; 664 | if (!empty($field) && strpos($field, ' ') === false) { 665 | $in_headers = true; 666 | } 667 | 668 | foreach ($lines as $line) { 669 | $lines_out = array(); 670 | if ($in_headers and $line == '') { 671 | $in_headers = false; 672 | } 673 | //Break this line up into several smaller lines if it's too long 674 | //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len), 675 | while (isset($line[self::MAX_LINE_LENGTH])) { 676 | //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on 677 | //so as to avoid breaking in the middle of a word 678 | $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); 679 | //Deliberately matches both false and 0 680 | if (!$pos) { 681 | //No nice break found, add a hard break 682 | $pos = self::MAX_LINE_LENGTH - 1; 683 | $lines_out[] = substr($line, 0, $pos); 684 | $line = substr($line, $pos); 685 | } else { 686 | //Break at the found point 687 | $lines_out[] = substr($line, 0, $pos); 688 | //Move along by the amount we dealt with 689 | $line = substr($line, $pos + 1); 690 | } 691 | //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 692 | if ($in_headers) { 693 | $line = "\t" . $line; 694 | } 695 | } 696 | $lines_out[] = $line; 697 | 698 | //Send the lines to the server 699 | foreach ($lines_out as $line_out) { 700 | //RFC2821 section 4.5.2 701 | if (!empty($line_out) and $line_out[0] == '.') { 702 | $line_out = '.' . $line_out; 703 | } 704 | $this->client_send($line_out . self::CRLF); 705 | } 706 | } 707 | 708 | //Message data has been sent, complete the command 709 | //Increase timelimit for end of DATA command 710 | $savetimelimit = $this->Timelimit; 711 | $this->Timelimit = $this->Timelimit * 2; 712 | $result = $this->sendCommand('DATA END', '.', 250); 713 | //Restore timelimit 714 | $this->Timelimit = $savetimelimit; 715 | return $result; 716 | } 717 | 718 | /** 719 | * Send an SMTP HELO or EHLO command. 720 | * Used to identify the sending server to the receiving server. 721 | * This makes sure that client and server are in a known state. 722 | * Implements RFC 821: HELO 723 | * and RFC 2821 EHLO. 724 | * @param string $host The host name or IP to connect to 725 | * @access public 726 | * @return boolean 727 | */ 728 | public function hello($host = '') 729 | { 730 | //Try extended hello first (RFC 2821) 731 | return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host)); 732 | } 733 | 734 | /** 735 | * Send an SMTP HELO or EHLO command. 736 | * Low-level implementation used by hello() 737 | * @see hello() 738 | * @param string $hello The HELO string 739 | * @param string $host The hostname to say we are 740 | * @access protected 741 | * @return boolean 742 | */ 743 | protected function sendHello($hello, $host) 744 | { 745 | $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); 746 | $this->helo_rply = $this->last_reply; 747 | if ($noerror) { 748 | $this->parseHelloFields($hello); 749 | } else { 750 | $this->server_caps = null; 751 | } 752 | return $noerror; 753 | } 754 | 755 | /** 756 | * Parse a reply to HELO/EHLO command to discover server extensions. 757 | * In case of HELO, the only parameter that can be discovered is a server name. 758 | * @access protected 759 | * @param string $type - 'HELO' or 'EHLO' 760 | */ 761 | protected function parseHelloFields($type) 762 | { 763 | $this->server_caps = array(); 764 | $lines = explode("\n", $this->helo_rply); 765 | 766 | foreach ($lines as $n => $s) { 767 | //First 4 chars contain response code followed by - or space 768 | $s = trim(substr($s, 4)); 769 | if (empty($s)) { 770 | continue; 771 | } 772 | $fields = explode(' ', $s); 773 | if (!empty($fields)) { 774 | if (!$n) { 775 | $name = $type; 776 | $fields = $fields[0]; 777 | } else { 778 | $name = array_shift($fields); 779 | switch ($name) { 780 | case 'SIZE': 781 | $fields = ($fields ? $fields[0] : 0); 782 | break; 783 | case 'AUTH': 784 | if (!is_array($fields)) { 785 | $fields = array(); 786 | } 787 | break; 788 | default: 789 | $fields = true; 790 | } 791 | } 792 | $this->server_caps[$name] = $fields; 793 | } 794 | } 795 | } 796 | 797 | /** 798 | * Send an SMTP MAIL command. 799 | * Starts a mail transaction from the email address specified in 800 | * $from. Returns true if successful or false otherwise. If True 801 | * the mail transaction is started and then one or more recipient 802 | * commands may be called followed by a data command. 803 | * Implements rfc 821: MAIL FROM: 804 | * @param string $from Source address of this message 805 | * @access public 806 | * @return boolean 807 | */ 808 | public function mail($from) 809 | { 810 | $useVerp = ($this->do_verp ? ' XVERP' : ''); 811 | return $this->sendCommand( 812 | 'MAIL FROM', 813 | 'MAIL FROM:<' . $from . '>' . $useVerp, 814 | 250 815 | ); 816 | } 817 | 818 | /** 819 | * Send an SMTP QUIT command. 820 | * Closes the socket if there is no error or the $close_on_error argument is true. 821 | * Implements from rfc 821: QUIT 822 | * @param boolean $close_on_error Should the connection close if an error occurs? 823 | * @access public 824 | * @return boolean 825 | */ 826 | public function quit($close_on_error = true) 827 | { 828 | $noerror = $this->sendCommand('QUIT', 'QUIT', 221); 829 | $err = $this->error; //Save any error 830 | if ($noerror or $close_on_error) { 831 | $this->close(); 832 | $this->error = $err; //Restore any error from the quit command 833 | } 834 | return $noerror; 835 | } 836 | 837 | /** 838 | * Send an SMTP RCPT command. 839 | * Sets the TO argument to $toaddr. 840 | * Returns true if the recipient was accepted false if it was rejected. 841 | * Implements from rfc 821: RCPT TO: 842 | * @param string $address The address the message is being sent to 843 | * @access public 844 | * @return boolean 845 | */ 846 | public function recipient($address) 847 | { 848 | return $this->sendCommand( 849 | 'RCPT TO', 850 | 'RCPT TO:<' . $address . '>', 851 | array(250, 251) 852 | ); 853 | } 854 | 855 | /** 856 | * Send an SMTP RSET command. 857 | * Abort any transaction that is currently in progress. 858 | * Implements rfc 821: RSET 859 | * @access public 860 | * @return boolean True on success. 861 | */ 862 | public function reset() 863 | { 864 | return $this->sendCommand('RSET', 'RSET', 250); 865 | } 866 | 867 | /** 868 | * Send a command to an SMTP server and check its return code. 869 | * @param string $command The command name - not sent to the server 870 | * @param string $commandstring The actual command to send 871 | * @param integer|array $expect One or more expected integer success codes 872 | * @access protected 873 | * @return boolean True on success. 874 | */ 875 | protected function sendCommand($command, $commandstring, $expect) 876 | { 877 | if (!$this->connected()) { 878 | $this->setError("Called $command without being connected"); 879 | return false; 880 | } 881 | //Reject line breaks in all commands 882 | if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) { 883 | $this->setError("Command '$command' contained line breaks"); 884 | return false; 885 | } 886 | $this->client_send($commandstring . self::CRLF); 887 | 888 | $this->last_reply = $this->get_lines(); 889 | // Fetch SMTP code and possible error code explanation 890 | $matches = array(); 891 | if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) { 892 | $code = $matches[1]; 893 | $code_ex = (count($matches) > 2 ? $matches[2] : null); 894 | // Cut off error code from each response line 895 | $detail = preg_replace( 896 | "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m", 897 | '', 898 | $this->last_reply 899 | ); 900 | } else { 901 | // Fall back to simple parsing if regex fails 902 | $code = substr($this->last_reply, 0, 3); 903 | $code_ex = null; 904 | $detail = substr($this->last_reply, 4); 905 | } 906 | 907 | $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); 908 | 909 | if (!in_array($code, (array)$expect)) { 910 | $this->setError( 911 | "$command command failed", 912 | $detail, 913 | $code, 914 | $code_ex 915 | ); 916 | $this->edebug( 917 | 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, 918 | self::DEBUG_CLIENT 919 | ); 920 | return false; 921 | } 922 | 923 | $this->setError(''); 924 | return true; 925 | } 926 | 927 | /** 928 | * Send an SMTP SAML command. 929 | * Starts a mail transaction from the email address specified in $from. 930 | * Returns true if successful or false otherwise. If True 931 | * the mail transaction is started and then one or more recipient 932 | * commands may be called followed by a data command. This command 933 | * will send the message to the users terminal if they are logged 934 | * in and send them an email. 935 | * Implements rfc 821: SAML FROM: 936 | * @param string $from The address the message is from 937 | * @access public 938 | * @return boolean 939 | */ 940 | public function sendAndMail($from) 941 | { 942 | return $this->sendCommand('SAML', "SAML FROM:$from", 250); 943 | } 944 | 945 | /** 946 | * Send an SMTP VRFY command. 947 | * @param string $name The name to verify 948 | * @access public 949 | * @return boolean 950 | */ 951 | public function verify($name) 952 | { 953 | return $this->sendCommand('VRFY', "VRFY $name", array(250, 251)); 954 | } 955 | 956 | /** 957 | * Send an SMTP NOOP command. 958 | * Used to keep keep-alives alive, doesn't actually do anything 959 | * @access public 960 | * @return boolean 961 | */ 962 | public function noop() 963 | { 964 | return $this->sendCommand('NOOP', 'NOOP', 250); 965 | } 966 | 967 | /** 968 | * Send an SMTP TURN command. 969 | * This is an optional command for SMTP that this class does not support. 970 | * This method is here to make the RFC821 Definition complete for this class 971 | * and _may_ be implemented in future 972 | * Implements from rfc 821: TURN 973 | * @access public 974 | * @return boolean 975 | */ 976 | public function turn() 977 | { 978 | $this->setError('The SMTP TURN command is not implemented'); 979 | $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); 980 | return false; 981 | } 982 | 983 | /** 984 | * Send raw data to the server. 985 | * @param string $data The data to send 986 | * @access public 987 | * @return integer|boolean The number of bytes sent to the server or false on error 988 | */ 989 | public function client_send($data) 990 | { 991 | $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); 992 | return fwrite($this->smtp_conn, $data); 993 | } 994 | 995 | /** 996 | * Get the latest error. 997 | * @access public 998 | * @return array 999 | */ 1000 | public function getError() 1001 | { 1002 | return $this->error; 1003 | } 1004 | 1005 | /** 1006 | * Get SMTP extensions available on the server 1007 | * @access public 1008 | * @return array|null 1009 | */ 1010 | public function getServerExtList() 1011 | { 1012 | return $this->server_caps; 1013 | } 1014 | 1015 | /** 1016 | * A multipurpose method 1017 | * The method works in three ways, dependent on argument value and current state 1018 | * 1. HELO/EHLO was not sent - returns null and set up $this->error 1019 | * 2. HELO was sent 1020 | * $name = 'HELO': returns server name 1021 | * $name = 'EHLO': returns boolean false 1022 | * $name = any string: returns null and set up $this->error 1023 | * 3. EHLO was sent 1024 | * $name = 'HELO'|'EHLO': returns server name 1025 | * $name = any string: if extension $name exists, returns boolean True 1026 | * or its options. Otherwise returns boolean False 1027 | * In other words, one can use this method to detect 3 conditions: 1028 | * - null returned: handshake was not or we don't know about ext (refer to $this->error) 1029 | * - false returned: the requested feature exactly not exists 1030 | * - positive value returned: the requested feature exists 1031 | * @param string $name Name of SMTP extension or 'HELO'|'EHLO' 1032 | * @return mixed 1033 | */ 1034 | public function getServerExt($name) 1035 | { 1036 | if (!$this->server_caps) { 1037 | $this->setError('No HELO/EHLO was sent'); 1038 | return null; 1039 | } 1040 | 1041 | // the tight logic knot ;) 1042 | if (!array_key_exists($name, $this->server_caps)) { 1043 | if ($name == 'HELO') { 1044 | return $this->server_caps['EHLO']; 1045 | } 1046 | if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) { 1047 | return false; 1048 | } 1049 | $this->setError('HELO handshake was used. Client knows nothing about server extensions'); 1050 | return null; 1051 | } 1052 | 1053 | return $this->server_caps[$name]; 1054 | } 1055 | 1056 | /** 1057 | * Get the last reply from the server. 1058 | * @access public 1059 | * @return string 1060 | */ 1061 | public function getLastReply() 1062 | { 1063 | return $this->last_reply; 1064 | } 1065 | 1066 | /** 1067 | * Read the SMTP server's response. 1068 | * Either before eof or socket timeout occurs on the operation. 1069 | * With SMTP we can tell if we have more lines to read if the 1070 | * 4th character is '-' symbol. If it is a space then we don't 1071 | * need to read anything else. 1072 | * @access protected 1073 | * @return string 1074 | */ 1075 | protected function get_lines() 1076 | { 1077 | // If the connection is bad, give up straight away 1078 | if (!is_resource($this->smtp_conn)) { 1079 | return ''; 1080 | } 1081 | $data = ''; 1082 | $endtime = 0; 1083 | stream_set_timeout($this->smtp_conn, $this->Timeout); 1084 | if ($this->Timelimit > 0) { 1085 | $endtime = time() + $this->Timelimit; 1086 | } 1087 | while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { 1088 | $str = @fgets($this->smtp_conn, 515); 1089 | $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); 1090 | $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); 1091 | $data .= $str; 1092 | // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen 1093 | if ((isset($str[3]) and $str[3] == ' ')) { 1094 | break; 1095 | } 1096 | // Timed-out? Log and break 1097 | $info = stream_get_meta_data($this->smtp_conn); 1098 | if ($info['timed_out']) { 1099 | $this->edebug( 1100 | 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', 1101 | self::DEBUG_LOWLEVEL 1102 | ); 1103 | break; 1104 | } 1105 | // Now check if reads took too long 1106 | if ($endtime and time() > $endtime) { 1107 | $this->edebug( 1108 | 'SMTP -> get_lines(): timelimit reached ('. 1109 | $this->Timelimit . ' sec)', 1110 | self::DEBUG_LOWLEVEL 1111 | ); 1112 | break; 1113 | } 1114 | } 1115 | return $data; 1116 | } 1117 | 1118 | /** 1119 | * Enable or disable VERP address generation. 1120 | * @param boolean $enabled 1121 | */ 1122 | public function setVerp($enabled = false) 1123 | { 1124 | $this->do_verp = $enabled; 1125 | } 1126 | 1127 | /** 1128 | * Get VERP address generation mode. 1129 | * @return boolean 1130 | */ 1131 | public function getVerp() 1132 | { 1133 | return $this->do_verp; 1134 | } 1135 | 1136 | /** 1137 | * Set error messages and codes. 1138 | * @param string $message The error message 1139 | * @param string $detail Further detail on the error 1140 | * @param string $smtp_code An associated SMTP error code 1141 | * @param string $smtp_code_ex Extended SMTP code 1142 | */ 1143 | protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') 1144 | { 1145 | $this->error = array( 1146 | 'error' => $message, 1147 | 'detail' => $detail, 1148 | 'smtp_code' => $smtp_code, 1149 | 'smtp_code_ex' => $smtp_code_ex 1150 | ); 1151 | } 1152 | 1153 | /** 1154 | * Set debug output method. 1155 | * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. 1156 | */ 1157 | public function setDebugOutput($method = 'echo') 1158 | { 1159 | $this->Debugoutput = $method; 1160 | } 1161 | 1162 | /** 1163 | * Get debug output method. 1164 | * @return string 1165 | */ 1166 | public function getDebugOutput() 1167 | { 1168 | return $this->Debugoutput; 1169 | } 1170 | 1171 | /** 1172 | * Set debug output level. 1173 | * @param integer $level 1174 | */ 1175 | public function setDebugLevel($level = 0) 1176 | { 1177 | $this->do_debug = $level; 1178 | } 1179 | 1180 | /** 1181 | * Get debug output level. 1182 | * @return integer 1183 | */ 1184 | public function getDebugLevel() 1185 | { 1186 | return $this->do_debug; 1187 | } 1188 | 1189 | /** 1190 | * Set SMTP timeout. 1191 | * @param integer $timeout 1192 | */ 1193 | public function setTimeout($timeout = 0) 1194 | { 1195 | $this->Timeout = $timeout; 1196 | } 1197 | 1198 | /** 1199 | * Get SMTP timeout. 1200 | * @return integer 1201 | */ 1202 | public function getTimeout() 1203 | { 1204 | return $this->Timeout; 1205 | } 1206 | 1207 | /** 1208 | * Reports an error number and string. 1209 | * @param integer $errno The error number returned by PHP. 1210 | * @param string $errmsg The error message returned by PHP. 1211 | */ 1212 | protected function errorHandler($errno, $errmsg) 1213 | { 1214 | $notice = 'Connection: Failed to connect to server.'; 1215 | $this->setError( 1216 | $notice, 1217 | $errno, 1218 | $errmsg 1219 | ); 1220 | $this->edebug( 1221 | $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg, 1222 | self::DEBUG_CONNECTION 1223 | ); 1224 | } 1225 | 1226 | /** 1227 | * Will return the ID of the last smtp transaction based on a list of patterns provided 1228 | * in SMTP::$smtp_transaction_id_patterns. 1229 | * If no reply has been received yet, it will return null. 1230 | * If no pattern has been matched, it will return false. 1231 | * @return bool|null|string 1232 | */ 1233 | public function getLastTransactionID() 1234 | { 1235 | $reply = $this->getLastReply(); 1236 | 1237 | if (empty($reply)) { 1238 | return null; 1239 | } 1240 | 1241 | foreach($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { 1242 | if(preg_match($smtp_transaction_id_pattern, $reply, $matches)) { 1243 | return $matches[1]; 1244 | } 1245 | } 1246 | 1247 | return false; 1248 | } 1249 | } 1250 | --------------------------------------------------------------------------------