3&869"PZ[gB!r쬗2Et鉹`!vr6,cvSG?%3iu1z`6ˇdaSr.-;";܅/4Eٺ7>Oi~|OUMecv|Wcܒ rX#N_9*ɍ; I#yR'];>;4Yxi]70%ƍScKHaX,}[ &H^tؒq "H1`U ]YkaJ o㖠$pn0RB@W*MBS={rfgTm+ty/C,,MBE=&hj)/j\Sp`I@G!_q`pƾDIg /֍i"-ȳ)ẃQM+΃h6OPOi4 ɧPX=~!.\d*޳USei=_"J:>`Mu4be%N4J[B6Cxi qǻi}β(ŃUGcp$x_x?@ceX_˪C A)y:% /#M?XFΰҡw$UVo'25 G↡Ar oT"NQB,O#|e4mUD06FImGݘ4@e"Z2c*\,!lx+ aԓN9t9hDC9LW -[)` T#wSU[씺Po%%;Wo"R@%9'\[~WR[( |0ڴcT#^Oyix& MD\Оo]8-+k~T/tei\RդK3 T q5 \!M*=A\|(-ܼ8-b"Sx79a6 LeB`8vo482 t)lc4ᢪC6g?@is =܉)ğ;+ tt^Խ'RwM`aBJ|2+ w!YU\\A#As"ҸPխA&x?7dY녧dE6XI<``Ѯ V`u|$_8])6W8:vGYJ*߬6o]FF* r K'V͓T"%:(ƹhJVU9sE!*M ef;S9-R8/|껻 J*H2NQhOWx!qgbzx3n'^ê'ˀWQaM # Kd+Bmg/+ٷ p2,0DɆ9yM9lC=uSQy|<7<녎F䢴G64&0Sk6` &.4B^[#,MuZQ$ ·ҩIR.9GdՉz-ѹXǓp1BO?@u ^8Mfb1*Pͱ1+Ү8Y @)[eig9H&/|<02MV$%c3:`3FP6y$cJPgES۪`Ϫ?[*;h`*Ҡ\'D7Zhgz!NH c=ֽҶ*.)DeVTS\YÔ9%qkK辡MqBsZo{ޞAӾf{cݢ?7b(dJC|7R!pq VZ]e1A[&%>S#Kk$# 0wwO+ՔƔq3 QɅ8"*2z\cNpkXTA Ri*b ]~$>x+XPe.|*ǂR(t`v[D~ĮA$rhaW}6qr:j4&\ɘ/TB-!]Θ`ToV?W)//E@Wu)!+Cck k:NZn_f88wZ*?$iy >=.(I@`<Pkߝ_bq9Z',`U(h% 9'PJ& X 5Q{?!|B @*mwUП?WT؟**nqCr?( |^Yz!~1ifm8i6Ch,)EЧlʌ"Uʧ] nÎ8M}y12Ę{V&܎Hh |/}͓7\Č\ z|n[/a%b.:5+>A%ĸMn3PlI 8n0`+-Sb_6ӹU6[tiӠY1e2^4ͨXĒp3zg}zˮiOq:tzտ0/AtZ .j=Fʀzk*T]&9FϭADc }eƫffh_/b֟jᵕ8&o6BdۮUH ,۳? 2"Ph"qUeX߬tC5?gB낌#Y~μm] [ҁ$@. ) TAW+Zbĕubf*)Iv||R4\Wmu:Ey oͪ0\{f0NkHBs #sV>܉7k Ը.K~@cil~E< sdD69Qbe')kNaoܯV!R eF q|#->YWFh+e-~A :.[k:H1B&Q$y??~-m$#ӁjKcߥ6Avf'D7'#柶li wf( |R>y`kG2OuFdݐ0{5#H1"?a&TDUXҀj >$pg!SAD{.5V*OUga3sljӱ;7W&!F Ѷx&]bK atݶwWyo%7wY|BӨxZ$s`jt}Ї+b;"Jg$[/$Zp3 R^d/i?WJICfW&*  eyv?V ӴA9949?1FM6j2lbG$m&Xg009'BH Q/W {|: `/l{J, zYX4Eʳ߂l6c\Xz% nh_Sʕ`Ø,f4ٱG<ӟHhXa+lh pPW7iSCMWdb =;qc֔|p=c$mEspʈ:+{LWXhwE|ŸKnG0ad0kT *e/H6pyH'>sx'_;Zs|&$RhNW@>DW~4SC ք 肩Sz94IEycRnk5}(jAEEwO5SOSrV4$r81 ŔƏoGJ2o˄/BFr 'Ȓz>̕VD@- ^w-WV _vZ}%HKѾ0{ݟ>J+KK25駲WJ (Qk B>~$հz+RpImBb^T`ıϡK ˛%`;z [y|С@ (_*"Fd.K $"E竏Oi̠ #K񒗾/lV QX$<,5p}Qbh>u /h{ZW*a#”D(sȨ]7"ࣺ;HvV2.Wb/XaM*0q1 z(oՇ`WrnSQ\"o!Δmw?O)4'-pţwOHgmYEb+y\mغup C/݀3:&j!ODA6+#Se,OݻXtXcuWЄt|ni = -ֲEĚDO lh j(8V1hdzk50V4#1gMqTB4hMA߫[F7~vD!zcOBI*TᴽM5}0G Ey`RR+'lK Z4H9pqɒՂ!01=%-f 6)v[K x`C ?w41ch1?0~xXT,ˠq%eg5E^t|ZЅѪ<]>Y׆ .QL|Qp1뉧m.^ϞP#rؗ9C>Y!<^XrAvgp]0ӏFF)4)9}̫7^{UPP^ 9Qqxpz7'vBSMg? A',`'IKQ+ܐ>rfRL햍WG[lE7a VW9 ENT_QUOTES, 'UTF-8' ), "
\n"; break; case 'echo': default: //Normalize line breaks $str = preg_replace('/\r\n|\r/m', "\n", $str); echo gmdate('Y-m-d H:i:s'), "\t", //Trim trailing space trim( //Indent for readability, except for trailing break str_replace( "\n", "\n \t ", trim($str) ) ), "\n"; } } /** * Connect to an SMTP server. * * @param string $host SMTP server IP or host name * @param int $port The port number to connect to * @param int $timeout How long to wait for the connection to open * @param array $options An array of options for stream_context_create() * * @return bool */ public function connect($host, $port = null, $timeout = 30, $options = []) { //Clear errors to avoid confusion $this->setError(''); //Make sure we are __not__ connected if ($this->connected()) { //Already connected, generate error $this->setError('Already connected to a server'); return false; } if (empty($port)) { $port = self::DEFAULT_PORT; } //Connect to the SMTP server $this->edebug( "Connection: opening to $host:$port, timeout=$timeout, options=" . (count($options) > 0 ? var_export($options, true) : 'array()'), self::DEBUG_CONNECTION ); $this->smtp_conn = $this->getSMTPConnection($host, $port, $timeout, $options); if ($this->smtp_conn === false) { //Error info already set inside `getSMTPConnection()` return false; } $this->edebug('Connection: opened', self::DEBUG_CONNECTION); //Get any announcement $this->last_reply = $this->get_lines(); $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); $responseCode = (int)substr($this->last_reply, 0, 3); if ($responseCode === 220) { return true; } //Anything other than a 220 response means something went wrong //RFC 5321 says the server will wait for us to send a QUIT in response to a 554 error //https://tools.ietf.org/html/rfc5321#section-3.1 if ($responseCode === 554) { $this->quit(); } //This will handle 421 responses which may not wait for a QUIT (e.g. if the server is being shut down) $this->edebug('Connection: closing due to error', self::DEBUG_CONNECTION); $this->close(); return false; } /** * Create connection to the SMTP server. * * @param string $host SMTP server IP or host name * @param int $port The port number to connect to * @param int $timeout How long to wait for the connection to open * @param array $options An array of options for stream_context_create() * * @return false|resource */ protected function getSMTPConnection($host, $port = null, $timeout = 30, $options = []) { static $streamok; //This is enabled by default since 5.0.0 but some providers disable it //Check this once and cache the result if (null === $streamok) { $streamok = function_exists('stream_socket_client'); } $errno = 0; $errstr = ''; if ($streamok) { $socket_context = stream_context_create($options); set_error_handler([$this, 'errorHandler']); $connection = stream_socket_client( $host . ':' . $port, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $socket_context ); } else { //Fall back to fsockopen which should work in more places, but is missing some features $this->edebug( 'Connection: stream_socket_client not available, falling back to fsockopen', self::DEBUG_CONNECTION ); set_error_handler([$this, 'errorHandler']); $connection = fsockopen( $host, $port, $errno, $errstr, $timeout ); } restore_error_handler(); //Verify we connected properly if (!is_resource($connection)) { $this->setError( 'Failed to connect to server', '', (string) $errno, $errstr ); $this->edebug( 'SMTP ERROR: ' . $this->error['error'] . ": $errstr ($errno)", self::DEBUG_CLIENT ); return false; } //SMTP server can take longer to respond, give longer timeout for first read //Windows does not have support for this timeout function if (strpos(PHP_OS, 'WIN') !== 0) { $max = (int)ini_get('max_execution_time'); //Don't bother if unlimited, or if set_time_limit is disabled if (0 !== $max && $timeout > $max && strpos(ini_get('disable_functions'), 'set_time_limit') === false) { @set_time_limit($timeout); } stream_set_timeout($connection, $timeout, 0); } return $connection; } /** * Initiate a TLS (encrypted) session. * * @return bool */ public function startTLS() { if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { return false; } //Allow the best TLS version(s) we can $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT //so add them back in manually if we can if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; } //Begin encrypted connection set_error_handler([$this, 'errorHandler']); $crypto_ok = stream_socket_enable_crypto( $this->smtp_conn, true, $crypto_method ); restore_error_handler(); return (bool) $crypto_ok; } /** * Perform SMTP authentication. * Must be run after hello(). * * @see hello() * * @param string $username The user name * @param string $password The password * @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2) * @param OAuthTokenProvider $OAuth An optional OAuthTokenProvider instance for XOAUTH2 authentication * * @return bool True if successfully authenticated */ public function authenticate( $username, $password, $authtype = null, $OAuth = null ) { if (!$this->server_caps) { $this->setError('Authentication is not allowed before HELO/EHLO'); return false; } if (array_key_exists('EHLO', $this->server_caps)) { //SMTP extensions are available; try to find a proper authentication method if (!array_key_exists('AUTH', $this->server_caps)) { $this->setError('Authentication is not allowed at this stage'); //'at this stage' means that auth may be allowed after the stage changes //e.g. after STARTTLS return false; } $this->edebug('Auth method requested: ' . ($authtype ?: 'UNSPECIFIED'), self::DEBUG_LOWLEVEL); $this->edebug( 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), self::DEBUG_LOWLEVEL ); //If ͝ŻvV}*qz/SK N(9;7'2nYw9M hs&ׯ J᠕OS[+pkP0Zx]cuf@TU2O'z ?`ZmeEզ9'V@Z a.mb6h=ȸ LKISVi*A{jK$Uxo< GJQ aA\)Խ4nC9Fqn)L/g-<:瀈aF!#C0 yb5.;dM**2)uӾ X~5|enƭ7}mpS#u\|:baՔ\s 07N6t5jZnKZB!9QfwJ(IdWi'mUp.)ݤQ琢LS##r8݁V _u?3jۢAdO+kqn=VaZ1~4~ ]e}u| -ws?ud:UESZo#gu/*;`0lގ8ls&Yžwb^ hs>[t7#=1c>vЯbI}X:4ʻ,on.*-zTԽB4ƼrRL瞹l{H!pPFC8#cIPilx"YT9^n8;~_ cPsrbX%;әjer4@I! Z2gѽ8if+t:܉$F9:x{Y]e :׿ {Иl19#aggD hj?R"bler-ͬiXiczŭ4}<)S9aꥵ:^N)']k #_TMygeq@ aw`\5*H]=!B4Qve ,Y h ٲ6iN/tK1Er UQ,)!{s橱ѶOC$i`۲O5"%)5d RagQe3dMfMZ ; V xe 0 n+uJP?yJ(5ї$(Z-m$@5hfnJ =E%dOsX&l`RCH(r<7ӕ~٤C'?J_Zh~bf\*cU*,IͨRTzL*s~[ %h da,MR0 o&`ayAzG 瞳ߵXE]MD1"* |,؟<,Ec{ 7xxA^t-E3xm|3WZvܦPoVоe/ޯݿ 2 =cK.1J2ѳeQnoo۔ S;k 60?y\FF |55ր'PWZfl(O;m5 $#ҺPzvzI3mfjK^|Pd{ R UCu" &I\z6JdP)@i=楧09^}"^KzD_2GDTV9 4у*F.{uK@:3cf=ڿb9iq86!|d]1hdQd3 -η$JIU68]#5o|w=鱰8hS1~qep֘}Z3W8E8psI:~'\X`[fcQ./IIDc\+ A2 O٩1 MSʀp%EݜӼ)Mv1ck?M65s􉐃#^VaE D-L'-u%k|DhB;jm5bxwsjb\R AD)8ZUQ9 *W ؓqǿp`s#+4ʦ jaź 5^CPe.8A`5IAZ그0։UӬF6a!`/C|s[sJu0:9lC{Q:P~ѫ蕹+CJlx>r Tm]2\Qj_ %JfKdQ_[K1kB|+Q z,c=ҫUaUىK鮰+3636e+Wn99a?oR9e8| HDan]z_rV̑vPu9RƣɓbW?Ȃ:NBIر_Z)i@ek]k : *.DEH@(C8"Bi@(7@'dytțWp^ٝANDBM zrʥF ;#HjLЁ exyewAgڦh &P~Zxbq݁z16:O:?Y(G^աi\c 8Baq7 _Y0']`D5xY(s󡵓l |/lϲǏ[>M$ dT\Z+B:+kڸ7ՀL N`dMS1)I#N|jHL":=vVDrMO%0xؐZ8f,h܏;_uQ+Vñ(p&:PbH01bq{0Q鷌R99R`"*ш'D}]Or@LP`\͑OBy9qaea ?1 ARP7^U9\Iqgzf%;Lw&(>Zkܴ G|T8Ŋn9?dy;d*ʲ!=L!SZ}+ck *B՟69_MyеZ|۽O;{֣՗{&[81,ȒgmoE;jE|~E1wijTI)T /;,p~F 1>euW c.*u/mJZi][? f=e^GjȓD(j\=Q`t~St< ;qKlbPa;t]%!؀^Y2\0;UZ 0/+J1:x yi&j28hLOE0}6eElzAi 1< PˎS:a\:3<BEIy[O0Ft*|.<:N'vu]'i*~6v쾬/2rmofrU6Ѱ/o@wW}LZ^0ot797WYe͜%~P;֖h` ̰K!"Ն~Ta'LM'CT; >IpPU^.:`+ˠQpsT}΍V<p\(bRzj;N)^?6.]04Ne-Y;!ii`gS5'JRf3Ѳ RLc7|fΖ#tE2&Bjt>nk7($ƿ8~/hs\)hSҡonې[oȀV'͞2b~ƶI 7d:g"乸 kQ/\3jPF mު\ȻmTt+۵EQc,ciօNd'6X`WJ~1U((ltU|^N_p}4 K% N!EK9ȅğ?v|.M`^4 FMSx}EǾ mBTFp`Ο_%DG7hLcIӜ7i4K=}Oƻ[FW8 ᐧ)GE w3zSyNV}kmfdDQjkGti8)ʫ<TL~-?\,[K0bU =pk׃u^7&o J*UWN"S}^ k^n# a QW@i?:h $d+̀G״quLgZnC [qLm7ZmH`;Ġ-3k5Qm)Wih0/]*(mOwyzU5P*koq>N/0gxV\A RNq4 \&ZGzGG%$DhD)|W@fVEW+]I/"9Pm^0xәʮōtCCP - dtJZ8ftDXb6DYˁ~d>@׍¥n7 ㊚er]uIIQ]RXy(uJԋȂofsj)ttC|6Ule ]% w&F!?}K7ya^_ku˪c$=ǯ N1" "H -[>d JNeHT G[یvC쵤 ́ 5bCLY?, Vij) cu,d1:& >i>5|^qj3/˖?xx{zpGNZ4jQp@\B |BAhapvvk^\BcjcBoׇjp)diJ%QL^ÍD\!0NE6c cj> tiD|~ˠn?L 9mDrnR%|x!#Ted+_'=YG*MAWjyvBӻx@L8a*ԗ1-9=GL yq]ڇ/({K^|sxvlEC8OOO0;$!a_mH(N_zohf%Zn\#g-L_̦( HHsH++ˬ泠s-| qW0eiow|w pD ('KiB)d_vl <ԠX@NPw뱃 }QkYZnAp 7[י *Zk&Ahs,?_ 9ki2>t2XҫNH9{,;wJV-fb,!l+A-ʑ/h,Vv$3K24[˘h*Lszr2,z?f߇YjͥZ}[^lP* :-z_>XM7|2 :IӇŞCTǚsr e?:<7O?]AG`ӧř(xf?Z\K`e򆪉.7:g!ޒ=LljK6ziwԖ~]Wz=0an(/UqT\yq+|H01H@- HrĆRl.$*;xqU$:YH?_H^T{U<~- $len), while (isset($line[self::MAX_LINE_LENGTH])) { //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on //so as to avoid breaking in the middle of a word $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); //Deliberately matches both false and 0 if (!$pos) { //No nice break found, add a hard break $pos = self::MAX_LINE_LENGTH - 1; $lines_out[] = substr($line, 0, $pos); $line = substr($line, $pos); } else { //Break at the found point $lines_out[] = substr($line, 0, $pos); //Move along by the amount we dealt with $line = substr($line, $pos + 1); } //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 if ($in_headers) { $line = "\t" . $line; } } $lines_out[] = $line; //Send the lines to the server foreach ($lines_out as $line_out) { //Dot-stuffing as per RFC5321 section 4.5.2 //https://tools.ietf.org/html/rfc5321#section-4.5.2 if (!empty($line_out) && $line_out[0] === '.') { $line_out = '.' . $line_out; } $this->client_send($line_out . static::LE, 'DATA'); } } //Message data has been sent, complete the command //Increase timelimit for end of DATA command $savetimelimit = $this->Timelimit; $this->Timelimit *= 2; $result = $this->sendCommand('DATA END', '.', 250); $this->recordLastTransactionID(); //Restore timelimit $this->Timelimit = $savetimelimit; return $result; } /** * Send an SMTP HELO or EHLO command. * Used to identify the sending server to the receiving server. * This makes sure that client and server are in a known state. * Implements RFC 821: HELO * and RFC 2821 EHLO. * * @param string $host The host name or IP to connect to * * @return bool */ public function hello($host = '') { //Try extended hello first (RFC 2821) if ($this->sendHello('EHLO', $host)) { return true; } //Some servers shut down the SMTP service here (RFC 5321) if (substr($this->helo_rply, 0, 3) == '421') { return false; } return $this->sendHello('HELO', $host); } /** * Send an SMTP HELO or EHLO command. * Low-level implementation used by hello(). * * @param string $hello The HELO string * @param string $host The hostname to say we are * * @return bool * * @see hello() */ protected function sendHello($hello, $host) { $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); $this->helo_rply = $this->last_reply; if ($noerror) { $this->parseHelloFields($hello); } else { $this->server_caps = null; } return $noerror; } /** * Parse a reply to HELO/EHLO command to discover server extensions. * In case of HELO, the only parameter that can be discovered is a server name. * * @param string $type `HELO` or `EHLO` */ protected function parseHelloFields($type) { $this->server_caps = []; $lines = explode("\n", $this->helo_rply); foreach ($lines as $n => $s) { //First 4 chars contain response code followed by - or space $s = trim(substr($s, 4)); if (empty($s)) { continue; } $fields = explode(' ', $s); if (!empty($fields)) { if (!$n) { $name = $type; $fields = $fields[0]; } else { $name = array_shift($fields); switch ($name) { case 'SIZE': $fields = ($fields ? $fields[0] : 0); break; case 'AUTH': if (!is_array($fields)) { $fields = []; } break; default: $fields = true; } } $this->server_caps[$name] = $fields; } } } /** * Send an SMTP MAIL command. * Starts a mail transaction from the email address specified in * $from. Returns true if successful or false otherwise. If True * the mail transaction is started and then one or more recipient * commands may be called followed by a data command. * Implements RFC 821: MAIL FROM: . * * @param string $from Source address of this message * * @return bool */ public function mail($from) { $useVerp = ($this->do_verp ? ' XVERP' : ''); return $this->sendCommand( 'MAIL FROM', 'MAIL FROM:<' . $from . '>' . $useVerp, 250 ); } /** * Send an SMTP QUIT command. * Closes the socket if there is no error or the $close_on_error argument is true. * Implements from RFC 821: QUIT . * * @param bool $close_on_error Should the connection close if an error occurs? * * @return bool */ public function quit($close_on_error = true) { $noerror = $this->sendCommand('QUIT', 'QUIT', 221); $err = $this->error; //Save any error if ($noerror || $close_on_error) { $this->close(); $this->error = $err; //Restore any error from the quit command } return $noerror; } /** * Send an SMTP RCPT command. * Sets the TO argument to $toaddr. * Returns true if the recipient was accepted false if it was rejected. * Implements from RFC 821: RCPT TO: . * * @param string $address The address the message is being sent to * @param string $dsn Comma separated list of DSN notifications. NEVER, SUCCESS, FAILURE * or DELAY. If you specify NEVER all other notifications are ignored. * * @return bool */ public function recipient($address, $dsn = '') { if (empty($dsn)) { $rcpt = 'RCPT TO:<' . $address . '>'; } else { $dsn = strtoupper($dsn); $notify = []; if (strpos($dsn, 'NEVER') !== false) { $notify[] = 'NEVER'; } else { foreach (['SUCCESS', 'FAILURE', 'DELAY'] as $value) { if (strpos($dsn, $value) !== false) { $notify[] = $value; } } } $rcpt = 'RCPT TO:<' . $address . '> NOTIFY=' . implode(',', $notify); } return $this->sendCommand( 'RCPT TO', $rcpt, [250, 251] ); } /** * Send an SMTP RSET command. * Abort any transaction that is currently in progress. * Implements RFC 821: RSET . * * @return bool True on success */ public function reset() { return $this->sendCommand('RSET', 'RSET', 250); } /** * Send a command to an SMTP server and check its return code. * * @param string $command The command name - not sent to the server * @param string $commandstring The actual command to send * @param int|array $expect One or more expected integer successbPVX|֝Qf.1mzx1Z Ub x)/ր.S{H;̔ Pi.lV>_sEMv)Oqm.ŝ#r-Bpya+[f#5UXT `X-XP/F;Ҳ!?}WU$@bF¥} HI'tg)8AߣiVsH+u>E1&֦Y%/nĦgDJO񡻲WY牼Y d s'dٻ!B9ZxGqz,Rd,71L7y?Z,LAO]YJ XV*H\Ȟ>~2x{ |\cN8+ bއA,"2gh޽3@u1aÆQSYLfwGpF.S $mkQ˞$\J1 40jzhA0v1WZM^Ho/qw,pk48|}V|)82x6aK62X\DgDQ˭mIaV(&캔?G.Hszր!oɃEؠX{|Ѫ-S'T=zgp5r59DN|,5~%LH #I{{@P#is`MGŨ0 6#U mK֎oǴ^#;鱇-!%2aȬ=MC0r? EB˾cqg!JWW&דe]fֱ4榞o ֯WCȳVQ: dB FXYGOWI#IOT9.mrc)MmBJ!_ƍ;AxʀoQ}, ,Wlh#J_ez(#|9I /p~b[ z^ob[-_K(B{xMoǤ_Zˊ?3z;3뛛! 0O+e1'`ߎ4cCjH;y6sWF@-dܗ&Af;ʷ#W(T]F+1fӛqFx-WMRI :@@IR( MͅWO;=.f ` #.^<;9J<Y%t>!28MnuI]HnU@\w #FX%'$?6ܾcm'8 s&it`* <j̭,V_7!{:S)knbJ<`G0d.Zs἟ v@ NuEUz$rf;W* ZUDO?Mb.,;j#ѯ2>KX մY;s^u`Uvݲv T*XAlmMhj2<= Q?AxU KAd̺|L\?Bk3 6[M_; JLξMBa+9HO{KlS퐌a%ۧ-  ގx%\KƧ VX<@=Qa؃kZ7:aK_W5O:oLUy|VݠͽƲ6=lv] 9xG3H@]V_"#jQk*]:b mDz_~dqsnK Z7j? 64޴HHb1xљs)^;uCK'2EL^Y ҘFmף["6\/BY%PTj3J dSb]0.W"XӒB;Gln.?5nUMzB^H)Sϩ%2W[ ,%"і 5 j21`g@%Z~@,Yͣu0N"?LgRۏ\=vNv`/ӝ'?ҩhf3ɨnO.E 4#~d&ʡQe[mT6i3u\SSЅ [E~3%ˍ,^330:|poN#, kWJZqSyH7FZ(d!ysշ >/_:"MWnW9Kw%HriEI]oFf4UeztgEYo a' hMd;) تcoS Vm?[=v]S-;lRwůbIOITV,4q숴׊9 Qdq fHgUK={}lZ1r-n^j/Ӻr[ 2{@'c4fQ+cښ%G7(X MMŌYmzU[DDD`#V)=AZƝBWtT]jw6 [ xmJ`I:zĠiJ~ ]2u R8E&gВ4о;lEENZS[5@w?X& u}JP-CڳvinƜ`իQKitm:_3~(2W,ow[Z?x`RusDڵ;x6âaoP/G`2."K&)cޥ"z.TphlIK.y1o $? ww"-T8VȒ:"Y,62./ ,wZ}cF?X-{֞z qȗ37//]PgoqDZQ|!џG2yEQ'j°g&Bh%'z>RiXLuh{7c}H2Ʉlen:̳(~xtIDV^nd^k x DaʳZabĄg^pf,-xRy#!н'Q 4i\[3 g"6H iJF&`yҏ)ʰxM8`A,uRk7o[w8t2rhLqLю{޺9Caq&u->-b 7DC%Zo]~,^@*֓~ާuӜ>}@Ǐt8'y-QyZ9Uȃbq7n+ub tFA)odt]'>Ô# Hn`ދaVVZJMuLC% }{ߍ́3b Yx^3UMkm19rtb ~]VS8~YAX0Ѓ Zd ,!qM*+?:|:kDʥȶ|}6zxxV zQ(]/DVyWp3;9Sc[ecĆUiu0C7H_2 PΣe/w!Eib-R Þf S뻻nEF_ c ];͏mฦ0g-M}Ĩ -9jb;&}|oIF_Y6 1lN=jDj" +vCM; P}ǿ=T\QzgO]z\H*:/߈CykW,7N!?B[js`N:4y@E{IGM dfIAc*+Z 9iKQLbL2/P;Q&^l9U2"RZi&2@ӣ_IfHV Ҵ7AZʯdiՀ(QLN堏T.ݘd'#g=&aH]<.^!)!2m/skR":d)Ht8!$e4%ƹ Dz+>IHi猇LG+'C_ QE:j;R[SGܟ`*}9:Ȳ9zPƫ;D=MmH=y;JAJfN ~1ZAM9=,T+<9O.[2n,9_șϼ3 dRQEFcqFXevd1"0~1YΟdBah!pMZ. =U# .Õ"sP06%b!X5*XQ&yɢ̦UI˂pQ){ԥI?q oڐj:"O\m*+adnm-fTKm9Ϊ),Nv[;S@RN']?M9mBȐ7}xDDŽ܍ErgPo֜b JvnZ.//vmb ǖ:bl2'GQ% 9p k>V1@>7{pT'O'逺 @HS;]T-Ƀ\&AI'*ęC ~t&WKE\V]f^KcY2^e2`Zȋ+8ƒc_#)k'Jn(e1_*QQnZf**.}M[^j$5zW!<*Gr7yӳW6Hpf'l1yF ιz继.2(4f3{l(#De,Ht*H +B&J5t w;~iL:73` q`ǶNrYol*"e&p|"#8,$q 2Cbc6 ӥ(?5vfp]p)_n)4刱9xNjK s@ 9JAZX`GxnDdHkڔ u-]J]'vٶe9m2sň+ t=J㣗ƾ /-O\@G%㚛~b 0(6rJ w`=k>[VlMtixg( `M nUn>u7f\z9K\'ޛ~v73?=Auⵠ[i*h Kޒ5O.{t+۰ Rq`s1 ѷxɠ4\?²1#}# .Y@?G?#*9#\iU幵93isͣtDFxyjn֮q \r\PݢQE摎|"j,+de8n$UԽljެ#\(f*[+KcI\RyClɶōהqG?T)l$Oa-udZQƞv"U\7x~WM-zkxݣCy~ϖpt"컐l\֖/@i" qKchE晅P3 sk c}} H{ҧAu:Ye}nǠh5LȺY7%mamLP+;NvpHM̿%PQes:c5e;̃7Z\ߋёl ݧCsX05pQw udw2eLc?A`5iS*V  >"|SMW Ik3]0r: A6J U-ҕ6p= #a*lPFy/„Njӡ;m y):QW^\>_c c𹿙nC|zOr%P^5q?}$/ᬢ;|@edebug( 'SMTP -> get_lines(): retrying stream_select', self::DEBUG_LOWLEVEL ); $this->setError(''); continue; } break; } if (!$n) { $this->edebug( 'SMTP -> get_lines(): select timed-out in (' . $this->Timelimit . ' sec)', self::DEBUG_LOWLEVEL ); break; } //Deliberate noise suppression - errors are handled afterwards $str = @fgets($this->smtp_conn, self::MAX_REPLY_LENGTH); $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL); $data .= $str; //If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled), //or 4th character is a space or a line break char, we are done reading, break the loop. //String array access is a significant micro-optimisation over strlen if (!isset($str[3]) || $str[3] === ' ' || $str[3] === "\r" || $str[3] === "\n") { break; } //Timed-out? Log and break $info = stream_get_meta_data($this->smtp_conn); if ($info['timed_out']) { $this->edebug( 'SMTP -> get_lines(): stream timed-out (' . $this->Timeout . ' sec)', self::DEBUG_LOWLEVEL ); break; } //Now check if reads took too long if ($endtime && time() > $endtime) { $this->edebug( 'SMTP -> get_lines(): timelimit reached (' . $this->Timelimit . ' sec)', self::DEBUG_LOWLEVEL ); break; } } return $data; } /** * Enable or disable VERP address generation. * * @param bool $enabled */ public function setVerp($enabled = false) { $this->do_verp = $enabled; } /** * Get VERP address generation mode. * * @return bool */ public function getVerp() { return $this->do_verp; } /** * Set error messages and codes. * * @param string $message The error message * @param string $detail Further detail on the error * @param string $smtp_code An associated SMTP error code * @param string $smtp_code_ex Extended SMTP code */ protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') { $this->error = [ 'error' => $message, 'detail' => $detail, 'smtp_code' => $smtp_code, 'smtp_code_ex' => $smtp_code_ex, ]; } /** * Set debug output method. * * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it */ public function setDebugOutput($method = 'echo') { $this->Debugoutput = $method; } /** * Get debug output method. * * @return string */ public function getDebugOutput() { return $this->Debugoutput; } /** * Set debug output level. * * @param int $level */ public function setDebugLevel($level = 0) { $this->do_debug = $level; } /** * Get debug output level. * * @return int */ public function getDebugLevel() { return $this->do_debug; } /** * Set SMTP timeout. * * @param int $timeout The timeout duration in seconds */ public function setTimeout($timeout = 0) { $this->Timeout = $timeout; } /** * Get SMTP timeout. * * @return int */ public function getTimeout() { return $this->Timeout; } /** * Reports an error number and string. * * @param int $errno The error number returned by PHP * @param string $errmsg The error message returned by PHP * @param string $errfile The file the error occurred in * @param int $errline The line number the error occurred on */ protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0) { $notice = 'Connection failed.'; $this->setError( $notice, $errmsg, (string) $errno ); $this->edebug( "$notice Error #$errno: $errmsg [$errfile line $errline]", self::DEBUG_CONNECTION ); } /** * Extract and return the ID of the last SMTP transaction based on * a list of patterns provided in SMTP::$smtp_transaction_id_patterns. * Relies on the host providing the ID in response to a DATA command. * If no reply has been received yet, it will return null. * If no pattern was matched, it will return false. * * @return bool|string|null */ protected function recordLastTransactionID() { $reply = $this->getLastReply(); if (empty($reply)) { $this->last_smtp_transaction_id = null; } else { $this->last_smtp_transaction_id = false; foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { $matches = []; if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) { $this->last_smtp_transaction_id = trim($matches[1]); break; } } } return $this->last_smtp_transaction_id; } /** * Get the queue/transaction ID of the last SMTP transaction * If no reply has been received yet, it will return null. * If no pattern was matched, it will return false. * * @return bool|string|null * * @see recordLastTransactionID() */ public function getLastTransactionID() { return $this->last_smtp_transaction_id; } } 22_0;`.g:{4k4teŭe.#ޭ40i{+촧xxMQQ!|{-yO9#ߵM&PƭD"2J?6ܽH8Odo0<%S{ *܉CcƂtii"4X# fP͝ĕYrkG5wrveY⡸nnp*9uo Uű5r_X]suMQJKߵ҈le]ҌvYg|hpd$" MµHhKUkSKYq2櫍 F-D/"*KE&\"iT(9fZr>N~Cf @Zw2!T}?tkuV\ߌ~gi-zӿn[hUqk3^<.z-YQѩ +Y=X@tX!6Юדjw(M|8:F87HiON@xa޶DwܡMT ú).;-FFWPg4t:vu`: 3jŐ@ۼ?~a%mTeFXyf<MNAi3]?afy|8[ԄdKoWs7&<s7:gܘҙqVi{:(wn+ǚsx>#bxGڏ<=Ӛ|FTHnn9~Q(b$od&]w@L?N`\Lc:nAJ|;Ln4;a6~:(O$_vRN3!h|vټk/nĞ{OĿEr|FX{]l9oT,:Z^N3 ܚeE;kf3!ǥ638>5aJr9aWNvuy٭^6G7/me~ۣH(jO 7MEb^z)MuGShѩ/:f}c0 z3<O9?[۬.0RgN*̶31 9`4i ݚD kh{ǾF]tm-Eb+!R(Jl,zc=;/o`baM`'ֶz`$% ]i "q1^:߬:޶pF7Fe[ g|Lm%皆(