es_mail.php 78 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Fanwe 方维直播系统
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2011 http://www.fanwe.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Author: 云淡风轻(1956838968@qq.com)
  8. // +----------------------------------------------------------------------
  9. class mail_sender
  10. {
  11. /////////////////////////////////////////////////
  12. // PUBLIC VARIABLES
  13. /////////////////////////////////////////////////
  14. /**
  15. * Email priority (1 = High, 3 = Normal, 5 = low).
  16. * @var int
  17. */
  18. var $Priority = 3;
  19. /**
  20. * Sets the CharSet of the message.
  21. * @var string
  22. */
  23. var $CharSet = "utf8";
  24. /**
  25. * Sets the Content-type of the message.
  26. * @var string
  27. */
  28. var $ContentType = "text/plain";
  29. /**
  30. * Sets the Encoding of the message. Options for this are "8bit",
  31. * "7bit", "binary", "base64", and "quoted-printable".
  32. * @var string
  33. */
  34. var $Encoding = "8bit";
  35. /**
  36. * Holds the most recent mailer error message.
  37. * @var string
  38. */
  39. var $ErrorInfo = "";
  40. /**
  41. * Sets the From email address for the message.
  42. * @var string
  43. */
  44. var $From = "root@localhost";
  45. /**
  46. * Sets the From name of the message.
  47. * @var string
  48. */
  49. var $FromName = "Root User";
  50. /**
  51. * Sets the Sender email (Return-Path) of the message. If not empty,
  52. * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
  53. * @var string
  54. */
  55. var $Sender = "";
  56. /**
  57. * Sets the Subject of the message.
  58. * @var string
  59. */
  60. var $Subject = "";
  61. /**
  62. * Sets the Body of the message. This can be either an HTML or text body.
  63. * If HTML then run IsHTML(true).
  64. * @var string
  65. */
  66. var $Body = "";
  67. /**
  68. * Sets the text-only body of the message. This automatically sets the
  69. * email to multipart/alternative. This body can be read by mail
  70. * clients that do not have HTML email capability such as mutt. Clients
  71. * that can read HTML will view the normal Body.
  72. * @var string
  73. */
  74. var $AltBody = "";
  75. /**
  76. * Sets word wrapping on the body of the message to a given number of
  77. * characters.
  78. * @var int
  79. */
  80. var $WordWrap = 0;
  81. /**
  82. * Method to send mail: ("mail", "sendmail", or "smtp").
  83. * @var string
  84. */
  85. var $Mailer = "mail";
  86. /**
  87. * Sets the path of the sendmail program.
  88. * @var string
  89. */
  90. var $Sendmail = "/usr/sbin/sendmail";
  91. /**
  92. * Path to PHPMailer plugins. This is now only useful if the SMTP class
  93. * is in a different directory than the PHP include path.
  94. * @var string
  95. */
  96. var $PluginDir = "";
  97. /**
  98. * Holds PHPMailer version.
  99. * @var string
  100. */
  101. var $Version;
  102. /**
  103. * Sets the email address that a reading confirmation will be sent.
  104. * @var string
  105. */
  106. var $ConfirmReadingTo = "";
  107. /**
  108. * Sets the hostname to use in Message-Id and Received headers
  109. * and as default HELO string. If empty, the value returned
  110. * by SERVER_NAME is used or 'localhost.localdomain'.
  111. * @var string
  112. */
  113. var $Hostname = "";
  114. /////////////////////////////////////////////////
  115. // SMTP VARIABLES
  116. /////////////////////////////////////////////////
  117. /**
  118. * Sets the SMTP hosts. All hosts must be separated by a
  119. * semicolon. You can also specify a different port
  120. * for each host by using this format: [hostname:port]
  121. * (e.g. "smtp1.example.com:25;smtp2.example.com").
  122. * Hosts will be tried in order.
  123. * @var string
  124. */
  125. var $Host;
  126. /**
  127. * Sets the default SMTP server port.
  128. * @var int
  129. */
  130. var $Port;
  131. /**
  132. * Sets the SMTP HELO of the message (Default is $Hostname).
  133. * @var string
  134. */
  135. var $Helo;
  136. /**
  137. * Sets SMTP authentication. Utilizes the Username and Password variables.
  138. * @var bool
  139. */
  140. var $SMTPAuth = true;
  141. /**
  142. * Sets SMTP username.
  143. * @var string
  144. */
  145. var $Username;
  146. /**
  147. * Sets SMTP password.
  148. * @var string
  149. */
  150. var $Password;
  151. /**
  152. * Sets the SMTP server timeout in seconds. This function will not
  153. * work with the win32 version.
  154. * @var int
  155. */
  156. var $Timeout = 10;
  157. /**
  158. * Sets SMTP class debugging on or off.
  159. * @var bool
  160. */
  161. var $SMTPDebug = false;
  162. /**
  163. * Prevents the SMTP connection from being closed after each mail
  164. * sending. If this is set to true then to close the connection
  165. * requires an explicit call to SmtpClose().
  166. * @var bool
  167. */
  168. var $SMTPKeepAlive = false;
  169. /**#@+
  170. * @access private
  171. */
  172. var $smtp = NULL;
  173. var $to = array();
  174. var $cc = array();
  175. var $bcc = array();
  176. var $ReplyTo = array();
  177. var $attachment = array();
  178. var $CustomHeader = array();
  179. var $message_type = "";
  180. var $boundary = array();
  181. var $language = array();
  182. var $error_count = 0;
  183. var $LE = "\n";
  184. /**#@-*/
  185. var $site_name ;
  186. var $mail_server_id = 0;
  187. function mail_sender()
  188. {
  189. //构造
  190. $this->site_name = app_conf("SITE_NAME");
  191. $sql = "select * from ".DB_PREFIX."mail_server where is_effect = 1 and ((use_limit > total_use and use_limit > 0) or (use_limit = 0)) order by id asc";
  192. $smtp_servers = $GLOBALS['db']->getAll($sql); //取出所有可用的服务器
  193. if(!$smtp_servers)
  194. {
  195. $GLOBALS['db']->query("update ".DB_PREFIX."mail_server set total_use = 0 where is_reset = 1 and is_effect = 1 and use_limit <= total_use and use_limit > 0"); //将所有可清零的服务器清零
  196. $smtp_servers = $GLOBALS['db']->getAll($sql);
  197. }
  198. foreach($smtp_servers as $k=>$v)
  199. {
  200. if($v['use_limit'] > $v['total_use']||$v['use_limit']==0)
  201. {
  202. $smtp_item = $v;
  203. break;
  204. }
  205. }
  206. $this->WordWrap = 50;
  207. $this->Host = $smtp_item['smtp_server'];
  208. $this->Port = $smtp_item['smtp_port'];
  209. $this->Username = $smtp_item['smtp_name'];
  210. $this->Password = $smtp_item['smtp_pwd'];
  211. $this->SMTPAuth = $smtp_item['is_verify'];
  212. $this->From = $smtp_item['smtp_name'];
  213. $this->mail_server_id = $smtp_item['id'];
  214. if($smtp_item["is_ssl"]==1)
  215. {
  216. $this->Host = "ssl://" .$this->Host;
  217. }
  218. $this->FromName = $this->site_name;
  219. $this->AddReplyTo(app_conf("REPLY_ADDRESS"));
  220. $this->CharSet = 'utf-8';
  221. $this->IsSMTP(); // 设置使用 SMTP
  222. }
  223. /////////////////////////////////////////////////
  224. // VARIABLE METHODS
  225. /////////////////////////////////////////////////
  226. /**
  227. * Sets message type to HTML.
  228. * @param bool $bool
  229. * @return void
  230. */
  231. function IsHTML($bool) {
  232. if($bool == true)
  233. $this->ContentType = "text/html";
  234. else
  235. $this->ContentType = "text/plain";
  236. }
  237. /**
  238. * Sets Mailer to send message using SMTP.
  239. * @return void
  240. */
  241. function IsSMTP() {
  242. $this->Mailer = "smtp";
  243. }
  244. /**
  245. * Sets Mailer to send message using PHP mail() function.
  246. * @return void
  247. */
  248. function IsMail() {
  249. $this->Mailer = "mail";
  250. }
  251. /**
  252. * Sets Mailer to send message using the $Sendmail program.
  253. * @return void
  254. */
  255. function IsSendmail() {
  256. $this->Mailer = "sendmail";
  257. }
  258. /**
  259. * Sets Mailer to send message using the qmail MTA.
  260. * @return void
  261. */
  262. function IsQmail() {
  263. $this->Sendmail = "/var/qmail/bin/sendmail";
  264. $this->Mailer = "sendmail";
  265. }
  266. /////////////////////////////////////////////////
  267. // RECIPIENT METHODS
  268. /////////////////////////////////////////////////
  269. /**
  270. * Adds a "To" address.
  271. * @param string $address
  272. * @param string $name
  273. * @return void
  274. */
  275. function AddAddress($address, $name = "") {
  276. $cur = count($this->to);
  277. $this->to[$cur][0] = trim($address);
  278. $this->to[$cur][1] = $name;
  279. }
  280. /**
  281. * Adds a "Cc" address. Note: this function works
  282. * with the SMTP mailer on win32, not with the "mail"
  283. * mailer.
  284. * @param string $address
  285. * @param string $name
  286. * @return void
  287. */
  288. function AddCC($address, $name = "") {
  289. $cur = count($this->cc);
  290. $this->cc[$cur][0] = trim($address);
  291. $this->cc[$cur][1] = $name;
  292. }
  293. /**
  294. * Adds a "Bcc" address. Note: this function works
  295. * with the SMTP mailer on win32, not with the "mail"
  296. * mailer.
  297. * @param string $address
  298. * @param string $name
  299. * @return void
  300. */
  301. function AddBCC($address, $name = "") {
  302. $cur = count($this->bcc);
  303. $this->bcc[$cur][0] = trim($address);
  304. $this->bcc[$cur][1] = $name;
  305. }
  306. /**
  307. * Adds a "Reply-to" address.
  308. * @param string $address
  309. * @param string $name
  310. * @return void
  311. */
  312. function AddReplyTo($address, $name = "") {
  313. $cur = count($this->ReplyTo);
  314. $this->ReplyTo[$cur][0] = trim($address);
  315. $this->ReplyTo[$cur][1] = $name;
  316. }
  317. /////////////////////////////////////////////////
  318. // MAIL SENDING METHODS
  319. /////////////////////////////////////////////////
  320. /**
  321. * Creates message and assigns Mailer. If the message is
  322. * not sent successfully then it returns false. Use the ErrorInfo
  323. * variable to view description of the error.
  324. * @return bool
  325. */
  326. function Send() {
  327. if(app_conf("MAIL_ON")==0)
  328. {
  329. $this->SetError("网站邮件功能未开启");
  330. return false;
  331. }
  332. $header = "";
  333. $body = "";
  334. $result = true;
  335. if((count($this->to) + count($this->cc) + count($this->bcc)) < 1)
  336. {
  337. $this->SetError($this->Lang("provide_address"));
  338. return false;
  339. }
  340. // Set whether the message is multipart/alternative
  341. if(!empty($this->AltBody))
  342. $this->ContentType = "multipart/alternative";
  343. $this->error_count = 0; // reset errors
  344. $this->SetMessageType();
  345. $header .= $this->CreateHeader();
  346. $body = $this->CreateBody();
  347. if($body == "") { return false; }
  348. // Choose the mailer
  349. switch($this->Mailer)
  350. {
  351. case "sendmail":
  352. $result = $this->SendmailSend($header, $body);
  353. break;
  354. case "mail":
  355. $result = $this->MailSend($header, $body);
  356. break;
  357. case "smtp":
  358. $result = $this->SmtpSend($header, $body);
  359. break;
  360. default:
  361. $this->SetError($this->Mailer . $this->Lang("mailer_not_supported"));
  362. $result = false;
  363. break;
  364. }
  365. //当为列表中的发件服务器时。加一
  366. if($this->mail_server_id!=0)
  367. {
  368. $GLOBALS['db']->query("update ".DB_PREFIX."mail_server set total_use = total_use + 1 where id =".$this->mail_server_id);
  369. //M("Smtp")->setInc("batch_count","id=".$this->mail_server_id);
  370. }
  371. return $result;
  372. }
  373. /**
  374. * Sends mail using the $Sendmail program.
  375. * @access private
  376. * @return bool
  377. */
  378. function SendmailSend($header, $body) {
  379. if ($this->Sender != "")
  380. $sendmail = sprintf("%s -oi -f %s -t", $this->Sendmail, $this->Sender);
  381. else
  382. $sendmail = sprintf("%s -oi -t", $this->Sendmail);
  383. if(!@$mail = popen($sendmail, "w"))
  384. {
  385. $this->SetError($this->Lang("execute") . $this->Sendmail);
  386. return false;
  387. }
  388. fputs($mail, $header);
  389. fputs($mail, $body);
  390. $result = pclose($mail) >> 8 & 0xFF;
  391. if($result != 0)
  392. {
  393. $this->SetError($this->Lang("execute") . $this->Sendmail);
  394. return false;
  395. }
  396. return true;
  397. }
  398. /**
  399. * Sends mail using the PHP mail() function.
  400. * @access private
  401. * @return bool
  402. */
  403. function MailSend($header, $body) {
  404. $to = "";
  405. for($i = 0; $i < count($this->to); $i++)
  406. {
  407. if($i != 0) { $to .= ", "; }
  408. $to .= $this->to[$i][0];
  409. }
  410. if ($this->Sender != "" && strlen(ini_get("safe_mode"))< 1)
  411. {
  412. $old_from = ini_get("sendmail_from");
  413. ini_set("sendmail_from", $this->Sender);
  414. $params = sprintf("-oi -f %s", $this->Sender);
  415. $rt = @mail($to, $this->EncodeHeader($this->Subject), $body,
  416. $header, $params);
  417. }
  418. else
  419. $rt = @mail($to, $this->EncodeHeader($this->Subject), $body, $header);
  420. if (isset($old_from))
  421. ini_set("sendmail_from", $old_from);
  422. if(!$rt)
  423. {
  424. $this->SetError($this->Lang("instantiate"));
  425. return false;
  426. }
  427. return true;
  428. }
  429. /**
  430. * Sends mail via SMTP using PhpSMTP (Author:
  431. * Chris Ryan). Returns bool. Returns false if there is a
  432. * bad MAIL FROM, RCPT, or DATA input.
  433. * @access private
  434. * @return bool
  435. */
  436. function SmtpSend($header, $body) {
  437. $error = "";
  438. $bad_rcpt = array();
  439. if(!$this->SmtpConnect())
  440. return false;
  441. $smtp_from = ($this->Sender == "") ? $this->From : $this->Sender;
  442. if(!$this->smtp->Mail($smtp_from))
  443. {
  444. $error = $this->Lang("from_failed") . $smtp_from;
  445. $this->SetError($error);
  446. $this->smtp->Reset();
  447. return false;
  448. }
  449. // Attempt to send attach all recipients
  450. for($i = 0; $i < count($this->to); $i++)
  451. {
  452. if(!$this->smtp->Recipient($this->to[$i][0]))
  453. $bad_rcpt[] = $this->to[$i][0];
  454. }
  455. for($i = 0; $i < count($this->cc); $i++)
  456. {
  457. if(!$this->smtp->Recipient($this->cc[$i][0]))
  458. $bad_rcpt[] = $this->cc[$i][0];
  459. }
  460. for($i = 0; $i < count($this->bcc); $i++)
  461. {
  462. if(!$this->smtp->Recipient($this->bcc[$i][0]))
  463. $bad_rcpt[] = $this->bcc[$i][0];
  464. }
  465. if(count($bad_rcpt) > 0) // Create error message
  466. {
  467. for($i = 0; $i < count($bad_rcpt); $i++)
  468. {
  469. if($i != 0) { $error .= ", "; }
  470. $error .= $bad_rcpt[$i];
  471. }
  472. $error = $this->Lang("recipients_failed") . $error;
  473. $this->SetError($error);
  474. $this->smtp->Reset();
  475. return false;
  476. }
  477. if(!$this->smtp->Data($header . $body))
  478. {
  479. $this->SetError($this->Lang("data_not_accepted"));
  480. $this->smtp->Reset();
  481. return false;
  482. }
  483. if($this->SMTPKeepAlive == true)
  484. $this->smtp->Reset();
  485. else
  486. $this->SmtpClose();
  487. return true;
  488. }
  489. /**
  490. * Initiates a connection to an SMTP server. Returns false if the
  491. * operation failed.
  492. * @access private
  493. * @return bool
  494. */
  495. function SmtpConnect() {
  496. if($this->smtp == NULL) { $this->smtp = new SMTP(); }
  497. $this->smtp->do_debug = $this->SMTPDebug;
  498. $hosts = explode(";", $this->Host);
  499. $index = 0;
  500. $connection = ($this->smtp->Connected());
  501. // Retry while there is no connection
  502. while($index < count($hosts) && $connection == false)
  503. {
  504. // if(strstr($hosts[$index], ":"))
  505. // list($host, $port) = explode(":", $hosts[$index]);
  506. //else
  507. {
  508. $host = $hosts[$index];
  509. $port = $this->Port;
  510. }
  511. if($this->smtp->Connect($host, $port, $this->Timeout))
  512. {
  513. if ($this->Helo != '')
  514. $this->smtp->Hello($this->Helo);
  515. else
  516. $this->smtp->Hello($this->ServerHostname());
  517. if($this->SMTPAuth)
  518. {
  519. if(!$this->smtp->Authenticate($this->Username,
  520. $this->Password))
  521. {
  522. $this->SetError($this->Lang("authenticate"));
  523. $this->smtp->Reset();
  524. $connection = false;
  525. }
  526. }
  527. $connection = true;
  528. }
  529. $index++;
  530. }
  531. if(!$connection)
  532. $this->SetError($this->Lang("connect_host"));
  533. return $connection;
  534. }
  535. /**
  536. * Closes the active SMTP session if one exists.
  537. * @return void
  538. */
  539. function SmtpClose() {
  540. if($this->smtp != NULL)
  541. {
  542. if($this->smtp->Connected())
  543. {
  544. $this->smtp->Quit();
  545. $this->smtp->Close();
  546. }
  547. }
  548. }
  549. /**
  550. * 载入邮件的系统语言包
  551. */
  552. function SetLanguage() {
  553. $this->language = array(
  554. 'provide_address' => '收件人地址不能为空',
  555. 'mailer_not_supported' => '邮件服务不支持',
  556. 'execute' => '无法执行',
  557. 'instantiate' => '无法初始化',
  558. 'authenticate' => 'SMTP验证失败',
  559. 'from_failed' => '发件失败的发件人地址',
  560. 'recipients_failed' => '发件失败的收件人地址',
  561. 'data_not_accepted' => '数据不支持',
  562. 'connect_host' => '无法连接SMTP服务器',
  563. 'file_access' => '程序执行权限不足',
  564. 'file_open' => '文件打开失败',
  565. 'encoding' => '未知的编码格式',
  566. );
  567. return true;
  568. }
  569. /////////////////////////////////////////////////
  570. // MESSAGE CREATION METHODS
  571. /////////////////////////////////////////////////
  572. /**
  573. * Creates recipient headers.
  574. * @access private
  575. * @return string
  576. */
  577. function AddrAppend($type, $addr) {
  578. $addr_str = $type . ": ";
  579. $addr_str .= $this->AddrFormat($addr[0]);
  580. if(count($addr) > 1)
  581. {
  582. for($i = 1; $i < count($addr); $i++)
  583. $addr_str .= ", " . $this->AddrFormat($addr[$i]);
  584. }
  585. $addr_str .= $this->LE;
  586. return $addr_str;
  587. }
  588. /**
  589. * Formats an address correctly.
  590. * @access private
  591. * @return string
  592. */
  593. function AddrFormat($addr) {
  594. if(empty($addr[1]))
  595. $formatted = $addr[0];
  596. else
  597. {
  598. $formatted = $this->EncodeHeader($addr[1], 'phrase') . " <" .
  599. $addr[0] . ">";
  600. }
  601. return $formatted;
  602. }
  603. /**
  604. * Wraps message for use with mailers that do not
  605. * automatically perform wrapping and for quoted-printable.
  606. * Original written by philippe.
  607. * @access private
  608. * @return string
  609. */
  610. function WrapText($message, $length, $qp_mode = false) {
  611. $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;
  612. $message = $this->FixEOL($message);
  613. if (substr($message, -1) == $this->LE)
  614. $message = substr($message, 0, -1);
  615. $line = explode($this->LE, $message);
  616. $message = "";
  617. for ($i=0 ;$i < count($line); $i++)
  618. {
  619. $line_part = explode(" ", $line[$i]);
  620. $buf = "";
  621. for ($e = 0; $e<count($line_part); $e++)
  622. {
  623. $word = $line_part[$e];
  624. if ($qp_mode and (strlen($word) > $length))
  625. {
  626. $space_left = $length - strlen($buf) - 1;
  627. if ($e != 0)
  628. {
  629. if ($space_left > 20)
  630. {
  631. $len = $space_left;
  632. if (substr($word, $len - 1, 1) == "=")
  633. $len--;
  634. elseif (substr($word, $len - 2, 1) == "=")
  635. $len -= 2;
  636. $part = substr($word, 0, $len);
  637. $word = substr($word, $len);
  638. $buf .= " " . $part;
  639. $message .= $buf . sprintf("=%s", $this->LE);
  640. }
  641. else
  642. {
  643. $message .= $buf . $soft_break;
  644. }
  645. $buf = "";
  646. }
  647. while (strlen($word) > 0)
  648. {
  649. $len = $length;
  650. if (substr($word, $len - 1, 1) == "=")
  651. $len--;
  652. elseif (substr($word, $len - 2, 1) == "=")
  653. $len -= 2;
  654. $part = substr($word, 0, $len);
  655. $word = substr($word, $len);
  656. if (strlen($word) > 0)
  657. $message .= $part . sprintf("=%s", $this->LE);
  658. else
  659. $buf = $part;
  660. }
  661. }
  662. else
  663. {
  664. $buf_o = $buf;
  665. $buf .= ($e == 0) ? $word : (" " . $word);
  666. if (strlen($buf) > $length and $buf_o != "")
  667. {
  668. $message .= $buf_o . $soft_break;
  669. $buf = $word;
  670. }
  671. }
  672. }
  673. $message .= $buf . $this->LE;
  674. }
  675. return $message;
  676. }
  677. /**
  678. * Set the body wrapping.
  679. * @access private
  680. * @return void
  681. */
  682. function SetWordWrap() {
  683. if($this->WordWrap < 1)
  684. return;
  685. switch($this->message_type)
  686. {
  687. case "alt":
  688. // fall through
  689. case "alt_attachments":
  690. $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
  691. break;
  692. default:
  693. $this->Body = $this->WrapText($this->Body, $this->WordWrap);
  694. break;
  695. }
  696. }
  697. /**
  698. * Assembles message header.
  699. * @access private
  700. * @return string
  701. */
  702. function CreateHeader() {
  703. $result = "";
  704. // Set the boundaries
  705. $uniq_id = md5(uniqid(time()));
  706. $this->boundary[1] = "b1_" . $uniq_id;
  707. $this->boundary[2] = "b2_" . $uniq_id;
  708. $result .= $this->HeaderLine("Date", $this->RFCDate());
  709. if($this->Sender == "")
  710. $result .= $this->HeaderLine("Return-Path", trim($this->From));
  711. else
  712. $result .= $this->HeaderLine("Return-Path", trim($this->Sender));
  713. // To be created automatically by mail()
  714. if($this->Mailer != "mail")
  715. {
  716. if(count($this->to) > 0)
  717. $result .= $this->AddrAppend("To", $this->to);
  718. else if (count($this->cc) == 0)
  719. $result .= $this->HeaderLine("To", "undisclosed-recipients:;");
  720. if(count($this->cc) > 0)
  721. $result .= $this->AddrAppend("Cc", $this->cc);
  722. }
  723. $from = array();
  724. $from[0][0] = trim($this->From);
  725. $from[0][1] = $this->site_name;
  726. $result .= $this->AddrAppend("From", $from);
  727. // sendmail and mail() extract Bcc from the header before sending
  728. if((($this->Mailer == "sendmail") || ($this->Mailer == "mail")) && (count($this->bcc) > 0))
  729. $result .= $this->AddrAppend("Bcc", $this->bcc);
  730. if(count($this->ReplyTo) > 0)
  731. $result .= $this->AddrAppend("Reply-to", $this->ReplyTo);
  732. // mail() sets the subject itself
  733. if($this->Mailer != "mail")
  734. $result .= $this->HeaderLine("Subject", $this->EncodeHeader(trim($this->Subject)));
  735. $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
  736. $result .= $this->HeaderLine("X-Priority", $this->Priority);
  737. $result .= $this->HeaderLine("X-Mailer", "easeMailer");
  738. if($this->ConfirmReadingTo != "")
  739. {
  740. $result .= $this->HeaderLine("Disposition-Notification-To",
  741. "<" . trim($this->ConfirmReadingTo) . ">");
  742. }
  743. // Add custom headers
  744. for($index = 0; $index < count($this->CustomHeader); $index++)
  745. {
  746. $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]),
  747. $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
  748. }
  749. $result .= $this->HeaderLine("MIME-Version", "1.0");
  750. switch($this->message_type)
  751. {
  752. case "plain":
  753. $result .= $this->HeaderLine("Content-Transfer-Encoding", $this->Encoding);
  754. $result .= sprintf("Content-Type: %s; charset=\"%s\"",
  755. $this->ContentType, $this->CharSet);
  756. break;
  757. case "attachments":
  758. // fall through
  759. case "alt_attachments":
  760. if($this->InlineImageExists())
  761. {
  762. $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s",
  763. "multipart/related", $this->LE, $this->LE,
  764. $this->boundary[1], $this->LE);
  765. }
  766. else
  767. {
  768. $result .= $this->HeaderLine("Content-Type", "multipart/mixed;");
  769. $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
  770. }
  771. break;
  772. case "alt":
  773. $result .= $this->HeaderLine("Content-Type", "multipart/alternative;");
  774. $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
  775. break;
  776. }
  777. if($this->Mailer != "mail")
  778. $result .= $this->LE.$this->LE;
  779. return $result;
  780. }
  781. /**
  782. * Assembles the message body. Returns an empty string on failure.
  783. * @access private
  784. * @return string
  785. */
  786. function CreateBody() {
  787. $result = "";
  788. $this->SetWordWrap();
  789. switch($this->message_type)
  790. {
  791. case "alt":
  792. $result .= $this->GetBoundary($this->boundary[1], "",
  793. "text/plain", "");
  794. $result .= $this->EncodeString($this->AltBody, $this->Encoding);
  795. $result .= $this->LE.$this->LE;
  796. $result .= $this->GetBoundary($this->boundary[1], "",
  797. "text/html", "");
  798. $result .= $this->EncodeString($this->Body, $this->Encoding);
  799. $result .= $this->LE.$this->LE;
  800. $result .= $this->EndBoundary($this->boundary[1]);
  801. break;
  802. case "plain":
  803. $result .= $this->EncodeString($this->Body, $this->Encoding);
  804. break;
  805. case "attachments":
  806. $result .= $this->GetBoundary($this->boundary[1], "", "", "");
  807. $result .= $this->EncodeString($this->Body, $this->Encoding);
  808. $result .= $this->LE;
  809. $result .= $this->AttachAll();
  810. break;
  811. case "alt_attachments":
  812. $result .= sprintf("--%s%s", $this->boundary[1], $this->LE);
  813. $result .= sprintf("Content-Type: %s;%s" .
  814. "\tboundary=\"%s\"%s",
  815. "multipart/alternative", $this->LE,
  816. $this->boundary[2], $this->LE.$this->LE);
  817. // Create text body
  818. $result .= $this->GetBoundary($this->boundary[2], "",
  819. "text/plain", "") . $this->LE;
  820. $result .= $this->EncodeString($this->AltBody, $this->Encoding);
  821. $result .= $this->LE.$this->LE;
  822. // Create the HTML body
  823. $result .= $this->GetBoundary($this->boundary[2], "",
  824. "text/html", "") . $this->LE;
  825. $result .= $this->EncodeString($this->Body, $this->Encoding);
  826. $result .= $this->LE.$this->LE;
  827. $result .= $this->EndBoundary($this->boundary[2]);
  828. $result .= $this->AttachAll();
  829. break;
  830. }
  831. if($this->IsError())
  832. $result = "";
  833. return $result;
  834. }
  835. /**
  836. * Returns the start of a message boundary.
  837. * @access private
  838. */
  839. function GetBoundary($boundary, $charSet, $contentType, $encoding) {
  840. $result = "";
  841. if($charSet == "") { $charSet = $this->CharSet; }
  842. if($contentType == "") { $contentType = $this->ContentType; }
  843. if($encoding == "") { $encoding = $this->Encoding; }
  844. $result .= $this->TextLine("--" . $boundary);
  845. $result .= sprintf("Content-Type: %s; charset = \"%s\"",
  846. $contentType, $charSet);
  847. $result .= $this->LE;
  848. $result .= $this->HeaderLine("Content-Transfer-Encoding", $encoding);
  849. $result .= $this->LE;
  850. return $result;
  851. }
  852. /**
  853. * Returns the end of a message boundary.
  854. * @access private
  855. */
  856. function EndBoundary($boundary) {
  857. return $this->LE . "--" . $boundary . "--" . $this->LE;
  858. }
  859. /**
  860. * Sets the message type.
  861. * @access private
  862. * @return void
  863. */
  864. function SetMessageType() {
  865. if(count($this->attachment) < 1 && strlen($this->AltBody) < 1)
  866. $this->message_type = "plain";
  867. else
  868. {
  869. if(count($this->attachment) > 0)
  870. $this->message_type = "attachments";
  871. if(strlen($this->AltBody) > 0 && count($this->attachment) < 1)
  872. $this->message_type = "alt";
  873. if(strlen($this->AltBody) > 0 && count($this->attachment) > 0)
  874. $this->message_type = "alt_attachments";
  875. }
  876. }
  877. /**
  878. * Returns a formatted header line.
  879. * @access private
  880. * @return string
  881. */
  882. function HeaderLine($name, $value) {
  883. return $name . ": " . $value . $this->LE;
  884. }
  885. /**
  886. * Returns a formatted mail line.
  887. * @access private
  888. * @return string
  889. */
  890. function TextLine($value) {
  891. return $value . $this->LE;
  892. }
  893. /////////////////////////////////////////////////
  894. // ATTACHMENT METHODS
  895. /////////////////////////////////////////////////
  896. /**
  897. * Adds an attachment from a path on the filesystem.
  898. * Returns false if the file could not be found
  899. * or accessed.
  900. * @param string $path Path to the attachment.
  901. * @param string $name Overrides the attachment name.
  902. * @param string $encoding File encoding (see $Encoding).
  903. * @param string $type File extension (MIME) type.
  904. * @return bool
  905. */
  906. function AddAttachment($path, $name = "", $encoding = "base64",
  907. $type = "application/octet-stream") {
  908. if(!@is_file($path))
  909. {
  910. $this->SetError($this->Lang("file_access") . $path);
  911. return false;
  912. }
  913. $filename = basename($path);
  914. if($name == "")
  915. $name = $filename;
  916. $cur = count($this->attachment);
  917. $this->attachment[$cur][0] = $path;
  918. $this->attachment[$cur][1] = $filename;
  919. $this->attachment[$cur][2] = $name;
  920. $this->attachment[$cur][3] = $encoding;
  921. $this->attachment[$cur][4] = $type;
  922. $this->attachment[$cur][5] = false; // isStringAttachment
  923. $this->attachment[$cur][6] = "attachment";
  924. $this->attachment[$cur][7] = 0;
  925. return true;
  926. }
  927. /**
  928. * Attaches all fs, string, and binary attachments to the message.
  929. * Returns an empty string on failure.
  930. * @access private
  931. * @return string
  932. */
  933. function AttachAll() {
  934. // Return text of body
  935. $mime = array();
  936. // Add all attachments
  937. for($i = 0; $i < count($this->attachment); $i++)
  938. {
  939. // Check for string attachment
  940. $bString = $this->attachment[$i][5];
  941. if ($bString)
  942. $string = $this->attachment[$i][0];
  943. else
  944. $path = $this->attachment[$i][0];
  945. $filename = $this->attachment[$i][1];
  946. $name = $this->attachment[$i][2];
  947. $encoding = $this->attachment[$i][3];
  948. $type = $this->attachment[$i][4];
  949. $disposition = $this->attachment[$i][6];
  950. $cid = $this->attachment[$i][7];
  951. $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
  952. $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE);
  953. $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);
  954. if($disposition == "inline")
  955. $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
  956. $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s",
  957. $disposition, $name, $this->LE.$this->LE);
  958. // Encode as string attachment
  959. if($bString)
  960. {
  961. $mime[] = $this->EncodeString($string, $encoding);
  962. if($this->IsError()) { return ""; }
  963. $mime[] = $this->LE.$this->LE;
  964. }
  965. else
  966. {
  967. $mime[] = $this->EncodeFile($path, $encoding);
  968. if($this->IsError()) { return ""; }
  969. $mime[] = $this->LE.$this->LE;
  970. }
  971. }
  972. $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);
  973. return join("", $mime);
  974. }
  975. /**
  976. * Encodes attachment in requested format. Returns an
  977. * empty string on failure.
  978. * @access private
  979. * @return string
  980. */
  981. function EncodeFile ($path, $encoding = "base64") {
  982. if(!@$fd = fopen($path, "rb"))
  983. {
  984. $this->SetError($this->Lang("file_open") . $path);
  985. return "";
  986. }
  987. $magic_quotes = get_magic_quotes_runtime();
  988. set_magic_quotes_runtime(0);
  989. $file_buffer = fread($fd, filesize($path));
  990. $file_buffer = $this->EncodeString($file_buffer, $encoding);
  991. fclose($fd);
  992. set_magic_quotes_runtime($magic_quotes);
  993. return $file_buffer;
  994. }
  995. /**
  996. * Encodes string to requested format. Returns an
  997. * empty string on failure.
  998. * @access private
  999. * @return string
  1000. */
  1001. function EncodeString ($str, $encoding = "base64") {
  1002. $encoded = "";
  1003. switch(strtolower($encoding)) {
  1004. case "base64":
  1005. // chunk_split is found in PHP >= 3.0.6
  1006. $encoded = chunk_split(base64_encode($str), 76, $this->LE);
  1007. break;
  1008. case "7bit":
  1009. case "8bit":
  1010. $encoded = $this->FixEOL($str);
  1011. if (substr($encoded, -(strlen($this->LE))) != $this->LE)
  1012. $encoded .= $this->LE;
  1013. break;
  1014. case "binary":
  1015. $encoded = $str;
  1016. break;
  1017. case "quoted-printable":
  1018. $encoded = $this->EncodeQP($str);
  1019. break;
  1020. default:
  1021. $this->SetError($this->Lang("encoding") . $encoding);
  1022. break;
  1023. }
  1024. return $encoded;
  1025. }
  1026. /**
  1027. * Encode a header string to best of Q, B, quoted or none.
  1028. * @access private
  1029. * @return string
  1030. */
  1031. function EncodeHeader ($str, $position = 'text') {
  1032. $x = 0;
  1033. switch (strtolower($position)) {
  1034. case 'phrase':
  1035. if (!preg_match('/[\200-\377]/', $str)) {
  1036. // Can't use addslashes as we don't know what value has magic_quotes_sybase.
  1037. $encoded = addcslashes($str, "\0..\37\177\\\"");
  1038. if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str))
  1039. return ($encoded);
  1040. else
  1041. return ("\"$encoded\"");
  1042. }
  1043. $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
  1044. break;
  1045. case 'comment':
  1046. $x = preg_match_all('/[()"]/', $str, $matches);
  1047. // Fall-through
  1048. case 'text':
  1049. default:
  1050. $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
  1051. break;
  1052. }
  1053. if ($x == 0)
  1054. return ($str);
  1055. $maxlen = 75 - 7 - strlen($this->CharSet);
  1056. // Try to select the encoding which should produce the shortest output
  1057. if (strlen($str)/3 < $x) {
  1058. $encoding = 'B';
  1059. $encoded = base64_encode($str);
  1060. $maxlen -= $maxlen % 4;
  1061. $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
  1062. } else {
  1063. $encoding = 'Q';
  1064. $encoded = $this->EncodeQ($str, $position);
  1065. $encoded = $this->WrapText($encoded, $maxlen, true);
  1066. $encoded = str_replace("=".$this->LE, "\n", trim($encoded));
  1067. }
  1068. $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
  1069. $encoded = trim(str_replace("\n", $this->LE, $encoded));
  1070. return $encoded;
  1071. }
  1072. /**
  1073. * Encode string to quoted-printable.
  1074. * @access private
  1075. * @return string
  1076. */
  1077. function EncodeQP ($str) {
  1078. $encoded = $this->FixEOL($str);
  1079. if (substr($encoded, -(strlen($this->LE))) != $this->LE)
  1080. $encoded .= $this->LE;
  1081. // Replace every high ascii, control and = characters
  1082. $encoded = preg_replace('/([\000-\010\013\014\016-\037\075\177-\377])/e',
  1083. "'='.sprintf('%02X', ord('\\1'))", $encoded);
  1084. // Replace every spaces and tabs when it's the last character on a line
  1085. $encoded = preg_replace("/([\011\040])".$this->LE."/e",
  1086. "'='.sprintf('%02X', ord('\\1')).'".$this->LE."'", $encoded);
  1087. // Maximum line length of 76 characters before CRLF (74 + space + '=')
  1088. $encoded = $this->WrapText($encoded, 74, true);
  1089. return $encoded;
  1090. }
  1091. /**
  1092. * Encode string to q encoding.
  1093. * @access private
  1094. * @return string
  1095. */
  1096. function EncodeQ ($str, $position = "text") {
  1097. // There should not be any EOL in the string
  1098. $encoded = preg_replace("[\r\n]", "", $str);
  1099. switch (strtolower($position)) {
  1100. case "phrase":
  1101. $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
  1102. break;
  1103. case "comment":
  1104. $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
  1105. case "text":
  1106. default:
  1107. // Replace every high ascii, control =, ? and _ characters
  1108. $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
  1109. "'='.sprintf('%02X', ord('\\1'))", $encoded);
  1110. break;
  1111. }
  1112. // Replace every spaces to _ (more readable than =20)
  1113. $encoded = str_replace(" ", "_", $encoded);
  1114. return $encoded;
  1115. }
  1116. /**
  1117. * Adds a string or binary attachment (non-filesystem) to the list.
  1118. * This method can be used to attach ascii or binary data,
  1119. * such as a BLOB record from a database.
  1120. * @param string $string String attachment data.
  1121. * @param string $filename Name of the attachment.
  1122. * @param string $encoding File encoding (see $Encoding).
  1123. * @param string $type File extension (MIME) type.
  1124. * @return void
  1125. */
  1126. function AddStringAttachment($string, $filename, $encoding = "base64",
  1127. $type = "application/octet-stream") {
  1128. // Append to $attachment array
  1129. $cur = count($this->attachment);
  1130. $this->attachment[$cur][0] = $string;
  1131. $this->attachment[$cur][1] = $filename;
  1132. $this->attachment[$cur][2] = $filename;
  1133. $this->attachment[$cur][3] = $encoding;
  1134. $this->attachment[$cur][4] = $type;
  1135. $this->attachment[$cur][5] = true; // isString
  1136. $this->attachment[$cur][6] = "attachment";
  1137. $this->attachment[$cur][7] = 0;
  1138. }
  1139. /**
  1140. * Adds an embedded attachment. This can include images, sounds, and
  1141. * just about any other document. Make sure to set the $type to an
  1142. * image type. For JPEG images use "image/jpeg" and for GIF images
  1143. * use "image/gif".
  1144. * @param string $path Path to the attachment.
  1145. * @param string $cid Content ID of the attachment. Use this to identify
  1146. * the Id for accessing the image in an HTML form.
  1147. * @param string $name Overrides the attachment name.
  1148. * @param string $encoding File encoding (see $Encoding).
  1149. * @param string $type File extension (MIME) type.
  1150. * @return bool
  1151. */
  1152. function AddEmbeddedImage($path, $cid, $name = "", $encoding = "base64",
  1153. $type = "application/octet-stream") {
  1154. if(!@is_file($path))
  1155. {
  1156. $this->SetError($this->Lang("file_access") . $path);
  1157. return false;
  1158. }
  1159. $filename = basename($path);
  1160. if($name == "")
  1161. $name = $filename;
  1162. // Append to $attachment array
  1163. $cur = count($this->attachment);
  1164. $this->attachment[$cur][0] = $path;
  1165. $this->attachment[$cur][1] = $filename;
  1166. $this->attachment[$cur][2] = $name;
  1167. $this->attachment[$cur][3] = $encoding;
  1168. $this->attachment[$cur][4] = $type;
  1169. $this->attachment[$cur][5] = false; // isStringAttachment
  1170. $this->attachment[$cur][6] = "inline";
  1171. $this->attachment[$cur][7] = $cid;
  1172. return true;
  1173. }
  1174. /**
  1175. * Returns true if an inline attachment is present.
  1176. * @access private
  1177. * @return bool
  1178. */
  1179. function InlineImageExists() {
  1180. $result = false;
  1181. for($i = 0; $i < count($this->attachment); $i++)
  1182. {
  1183. if($this->attachment[$i][6] == "inline")
  1184. {
  1185. $result = true;
  1186. break;
  1187. }
  1188. }
  1189. return $result;
  1190. }
  1191. /////////////////////////////////////////////////
  1192. // MESSAGE RESET METHODS
  1193. /////////////////////////////////////////////////
  1194. /**
  1195. * Clears all recipients assigned in the TO array. Returns void.
  1196. * @return void
  1197. */
  1198. function ClearAddresses() {
  1199. $this->to = array();
  1200. }
  1201. /**
  1202. * Clears all recipients assigned in the CC array. Returns void.
  1203. * @return void
  1204. */
  1205. function ClearCCs() {
  1206. $this->cc = array();
  1207. }
  1208. /**
  1209. * Clears all recipients assigned in the BCC array. Returns void.
  1210. * @return void
  1211. */
  1212. function ClearBCCs() {
  1213. $this->bcc = array();
  1214. }
  1215. /**
  1216. * Clears all recipients assigned in the ReplyTo array. Returns void.
  1217. * @return void
  1218. */
  1219. function ClearReplyTos() {
  1220. $this->ReplyTo = array();
  1221. }
  1222. /**
  1223. * Clears all recipients assigned in the TO, CC and BCC
  1224. * array. Returns void.
  1225. * @return void
  1226. */
  1227. function ClearAllRecipients() {
  1228. $this->to = array();
  1229. $this->cc = array();
  1230. $this->bcc = array();
  1231. }
  1232. /**
  1233. * Clears all previously set filesystem, string, and binary
  1234. * attachments. Returns void.
  1235. * @return void
  1236. */
  1237. function ClearAttachments() {
  1238. $this->attachment = array();
  1239. }
  1240. /**
  1241. * Clears all custom headers. Returns void.
  1242. * @return void
  1243. */
  1244. function ClearCustomHeaders() {
  1245. $this->CustomHeader = array();
  1246. }
  1247. /////////////////////////////////////////////////
  1248. // MISCELLANEOUS METHODS
  1249. /////////////////////////////////////////////////
  1250. /**
  1251. * Adds the error message to the error container.
  1252. * Returns void.
  1253. * @access private
  1254. * @return void
  1255. */
  1256. function SetError($msg) {
  1257. $this->error_count++;
  1258. $this->ErrorInfo = $msg;
  1259. }
  1260. /**
  1261. * Returns the proper RFC 822 formatted date.
  1262. * @access private
  1263. * @return string
  1264. */
  1265. function RFCDate() {
  1266. $tz = date("Z");
  1267. $tzs = ($tz < 0) ? "-" : "+";
  1268. $tz = abs($tz);
  1269. $tz = ($tz/3600)*100 + ($tz%3600)/60;
  1270. $result = sprintf("%s %s%04d", date("D, j M Y H:i:s"), $tzs, $tz);
  1271. return $result;
  1272. }
  1273. /**
  1274. * Returns the appropriate server variable. Should work with both
  1275. * PHP 4.1.0+ as well as older versions. Returns an empty string
  1276. * if nothing is found.
  1277. * @access private
  1278. * @return mixed
  1279. */
  1280. function ServerVar($varName) {
  1281. global $HTTP_SERVER_VARS;
  1282. global $HTTP_ENV_VARS;
  1283. if(!isset($_SERVER))
  1284. {
  1285. $_SERVER = $HTTP_SERVER_VARS;
  1286. if(!isset($_SERVER["REMOTE_ADDR"]))
  1287. $_SERVER = $HTTP_ENV_VARS; // must be Apache
  1288. }
  1289. if(isset($_SERVER[$varName]))
  1290. return $_SERVER[$varName];
  1291. else
  1292. return "";
  1293. }
  1294. /**
  1295. * Returns the server hostname or 'localhost.localdomain' if unknown.
  1296. * @access private
  1297. * @return string
  1298. */
  1299. function ServerHostname() {
  1300. if ($this->Hostname != "")
  1301. $result = $this->Hostname;
  1302. elseif ($this->ServerVar('SERVER_NAME') != "")
  1303. $result = $this->ServerVar('SERVER_NAME');
  1304. else
  1305. $result = "localhost.localdomain";
  1306. return $result;
  1307. }
  1308. /**
  1309. * Returns a message in the appropriate language.
  1310. * @access private
  1311. * @return string
  1312. */
  1313. function Lang($key) {
  1314. if(count($this->language) < 1)
  1315. $this->SetLanguage(); // set the default language
  1316. if(isset($this->language[$key]))
  1317. return $this->language[$key];
  1318. else
  1319. return "Language string failed to load: " . $key;
  1320. }
  1321. /**
  1322. * Returns true if an error occurred.
  1323. * @return bool
  1324. */
  1325. function IsError() {
  1326. return ($this->error_count > 0);
  1327. }
  1328. /**
  1329. * Changes every end of line from CR or LF to CRLF.
  1330. * @access private
  1331. * @return string
  1332. */
  1333. function FixEOL($str) {
  1334. $str = str_replace("\r\n", "\n", $str);
  1335. $str = str_replace("\r", "\n", $str);
  1336. $str = str_replace("\n", $this->LE, $str);
  1337. return $str;
  1338. }
  1339. /**
  1340. * Adds a custom header.
  1341. * @return void
  1342. */
  1343. function AddCustomHeader($custom_header) {
  1344. $this->CustomHeader[] = explode(":", $custom_header, 2);
  1345. }
  1346. }
  1347. class SMTP
  1348. {
  1349. /**
  1350. * SMTP server port
  1351. * @var int
  1352. */
  1353. var $SMTP_PORT = 25;
  1354. /**
  1355. * SMTP reply line ending
  1356. * @var string
  1357. */
  1358. var $CRLF = "\r\n";
  1359. /**
  1360. * Sets whether debugging is turned on
  1361. * @var bool
  1362. */
  1363. var $do_debug; # the level of debug to perform
  1364. /**#@+
  1365. * @access private
  1366. */
  1367. var $smtp_conn; # the socket to the server
  1368. var $error; # error if any on the last call
  1369. var $helo_rply; # the reply the server sent to us for HELO
  1370. /**#@-*/
  1371. /**
  1372. * Initialize the class so that the data is in a known state.
  1373. * @access public
  1374. * @return void
  1375. */
  1376. function SMTP() {
  1377. $this->smtp_conn = 0;
  1378. $this->error = null;
  1379. $this->helo_rply = null;
  1380. $this->do_debug = 0;
  1381. }
  1382. /*************************************************************
  1383. * CONNECTION FUNCTIONS *
  1384. ***********************************************************/
  1385. /**
  1386. * Connect to the server specified on the port specified.
  1387. * If the port is not specified use the default SMTP_PORT.
  1388. * If tval is specified then a connection will try and be
  1389. * established with the server for that number of seconds.
  1390. * If tval is not specified the default is 30 seconds to
  1391. * try on the connection.
  1392. *
  1393. * SMTP CODE SUCCESS: 220
  1394. * SMTP CODE FAILURE: 421
  1395. * @access public
  1396. * @return bool
  1397. */
  1398. function Connect($host,$port=0,$tval=30) {
  1399. # set the error val to null so there is no confusion
  1400. $this->error = null;
  1401. # make sure we are __not__ connected
  1402. if($this->connected()) {
  1403. # ok we are connected! what should we do?
  1404. # for now we will just give an error saying we
  1405. # are already connected
  1406. $this->error =
  1407. array("error" => "Already connected to a server");
  1408. return false;
  1409. }
  1410. if(empty($port)) {
  1411. $port = $this->SMTP_PORT;
  1412. }
  1413. #connect to the smtp server
  1414. $this->smtp_conn = fsockopen($host, # the host of the server
  1415. $port, # the port to use
  1416. $errno, # error number if any
  1417. $errstr, # error message if any
  1418. $tval); # give up after ? secs
  1419. # verify we connected properly
  1420. if(empty($this->smtp_conn)) {
  1421. $this->error = array("error" => "Failed to connect to server",
  1422. "errno" => $errno,
  1423. "errstr" => $errstr);
  1424. if($this->do_debug >= 1) {
  1425. echo "SMTP -> ERROR: " . $this->error["error"] .
  1426. ": $errstr ($errno)" . $this->CRLF;
  1427. }
  1428. return false;
  1429. }
  1430. # sometimes the SMTP server takes a little longer to respond
  1431. # so we will give it a longer timeout for the first read
  1432. // Windows still does not have support for this timeout function
  1433. if(substr(PHP_OS, 0, 3) != "WIN")
  1434. socket_set_timeout($this->smtp_conn, $tval, 0);
  1435. # get any announcement stuff
  1436. $announce = $this->get_lines();
  1437. # set the timeout of any socket functions at 1/10 of a second
  1438. //if(function_exists("socket_set_timeout"))
  1439. // socket_set_timeout($this->smtp_conn, 0, 100000);
  1440. if($this->do_debug >= 2) {
  1441. echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce;
  1442. }
  1443. return true;
  1444. }
  1445. /**
  1446. * Performs SMTP authentication. Must be run after running the
  1447. * Hello() method. Returns true if successfully authenticated.
  1448. * @access public
  1449. * @return bool
  1450. */
  1451. function Authenticate($username, $password) {
  1452. // Start authentication
  1453. fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF);
  1454. $rply = $this->get_lines();
  1455. $code = substr($rply,0,3);
  1456. if($code != 334) {
  1457. $this->error =
  1458. array("error" => "AUTH not accepted from server",
  1459. "smtp_code" => $code,
  1460. "smtp_msg" => substr($rply,4));
  1461. if($this->do_debug >= 1) {
  1462. echo "SMTP -> ERROR: " . $this->error["error"] .
  1463. ": " . $rply . $this->CRLF;
  1464. }
  1465. return false;
  1466. }
  1467. // Send encoded username
  1468. fputs($this->smtp_conn, base64_encode($username) . $this->CRLF);
  1469. $rply = $this->get_lines();
  1470. $code = substr($rply,0,3);
  1471. if($code != 334) {
  1472. $this->error =
  1473. array("error" => "Username not accepted from server",
  1474. "smtp_code" => $code,
  1475. "smtp_msg" => substr($rply,4));
  1476. if($this->do_debug >= 1) {
  1477. echo "SMTP -> ERROR: " . $this->error["error"] .
  1478. ": " . $rply . $this->CRLF;
  1479. }
  1480. return false;
  1481. }
  1482. // Send encoded password
  1483. fputs($this->smtp_conn, base64_encode($password) . $this->CRLF);
  1484. $rply = $this->get_lines();
  1485. $code = substr($rply,0,3);
  1486. if($code != 235) {
  1487. $this->error =
  1488. array("error" => "Password not accepted from server",
  1489. "smtp_code" => $code,
  1490. "smtp_msg" => substr($rply,4));
  1491. if($this->do_debug >= 1) {
  1492. echo "SMTP -> ERROR: " . $this->error["error"] .
  1493. ": " . $rply . $this->CRLF;
  1494. }
  1495. return false;
  1496. }
  1497. return true;
  1498. }
  1499. /**
  1500. * Returns true if connected to a server otherwise false
  1501. * @access private
  1502. * @return bool
  1503. */
  1504. function Connected() {
  1505. if(!empty($this->smtp_conn)) {
  1506. $sock_status = socket_get_status($this->smtp_conn);
  1507. if($sock_status["eof"]) {
  1508. # hmm this is an odd situation... the socket is
  1509. # valid but we aren't connected anymore
  1510. if($this->do_debug >= 1) {
  1511. echo "SMTP -> NOTICE:" . $this->CRLF .
  1512. "EOF caught while checking if connected";
  1513. }
  1514. $this->Close();
  1515. return false;
  1516. }
  1517. return true; # everything looks good
  1518. }
  1519. return false;
  1520. }
  1521. /**
  1522. * Closes the socket and cleans up the state of the class.
  1523. * It is not considered good to use this function without
  1524. * first trying to use QUIT.
  1525. * @access public
  1526. * @return void
  1527. */
  1528. function Close() {
  1529. $this->error = null; # so there is no confusion
  1530. $this->helo_rply = null;
  1531. if(!empty($this->smtp_conn)) {
  1532. # close the connection and cleanup
  1533. fclose($this->smtp_conn);
  1534. $this->smtp_conn = 0;
  1535. }
  1536. }
  1537. /***************************************************************
  1538. * SMTP COMMANDS *
  1539. *************************************************************/
  1540. /**
  1541. * Issues a data command and sends the msg_data to the server
  1542. * finializing the mail transaction. $msg_data is the message
  1543. * that is to be send with the headers. Each header needs to be
  1544. * on a single line followed by a <CRLF> with the message headers
  1545. * and the message body being seperated by and additional <CRLF>.
  1546. *
  1547. * Implements rfc 821: DATA <CRLF>
  1548. *
  1549. * SMTP CODE INTERMEDIATE: 354
  1550. * [data]
  1551. * <CRLF>.<CRLF>
  1552. * SMTP CODE SUCCESS: 250
  1553. * SMTP CODE FAILURE: 552,554,451,452
  1554. * SMTP CODE FAILURE: 451,554
  1555. * SMTP CODE ERROR : 500,501,503,421
  1556. * @access public
  1557. * @return bool
  1558. */
  1559. function Data($msg_data) {
  1560. $this->error = null; # so no confusion is caused
  1561. if(!$this->connected()) {
  1562. $this->error = array(
  1563. "error" => "Called Data() without being connected");
  1564. return false;
  1565. }
  1566. fputs($this->smtp_conn,"DATA" . $this->CRLF);
  1567. $rply = $this->get_lines();
  1568. $code = substr($rply,0,3);
  1569. if($this->do_debug >= 2) {
  1570. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  1571. }
  1572. if($code != 354) {
  1573. $this->error =
  1574. array("error" => "DATA command not accepted from server",
  1575. "smtp_code" => $code,
  1576. "smtp_msg" => substr($rply,4));
  1577. if($this->do_debug >= 1) {
  1578. echo "SMTP -> ERROR: " . $this->error["error"] .
  1579. ": " . $rply . $this->CRLF;
  1580. }
  1581. return false;
  1582. }
  1583. # the server is ready to accept data!
  1584. # according to rfc 821 we should not send more than 1000
  1585. # including the CRLF
  1586. # characters on a single line so we will break the data up
  1587. # into lines by \r and/or \n then if needed we will break
  1588. # each of those into smaller lines to fit within the limit.
  1589. # in addition we will be looking for lines that start with
  1590. # a period '.' and append and additional period '.' to that
  1591. # line. NOTE: this does not count towards are limit.
  1592. # normalize the line breaks so we know the explode works
  1593. $msg_data = str_replace("\r\n","\n",$msg_data);
  1594. $msg_data = str_replace("\r","\n",$msg_data);
  1595. $lines = explode("\n",$msg_data);
  1596. # we need to find a good way to determine is headers are
  1597. # in the msg_data or if it is a straight msg body
  1598. # currently I'm assuming rfc 822 definitions of msg headers
  1599. # and if the first field of the first line (':' sperated)
  1600. # does not contain a space then it _should_ be a header
  1601. # and we can process all lines before a blank "" line as
  1602. # headers.
  1603. $field = substr($lines[0],0,strpos($lines[0],":"));
  1604. $in_headers = false;
  1605. if(!empty($field) && !strstr($field," ")) {
  1606. $in_headers = true;
  1607. }
  1608. $max_line_length = 998; # used below; set here for ease in change
  1609. while(list(,$line) = @each($lines)) {
  1610. $lines_out = null;
  1611. if($line == "" && $in_headers) {
  1612. $in_headers = false;
  1613. }
  1614. # ok we need to break this line up into several
  1615. # smaller lines
  1616. while(strlen($line) > $max_line_length) {
  1617. $pos = strrpos(substr($line,0,$max_line_length)," ");
  1618. # Patch to fix DOS attack
  1619. if(!$pos) {
  1620. $pos = $max_line_length - 1;
  1621. }
  1622. $lines_out[] = substr($line,0,$pos);
  1623. $line = substr($line,$pos + 1);
  1624. # if we are processing headers we need to
  1625. # add a LWSP-char to the front of the new line
  1626. # rfc 822 on long msg headers
  1627. if($in_headers) {
  1628. $line = "\t" . $line;
  1629. }
  1630. }
  1631. $lines_out[] = $line;
  1632. # now send the lines to the server
  1633. while(list(,$line_out) = @each($lines_out)) {
  1634. if(strlen($line_out) > 0)
  1635. {
  1636. if(substr($line_out, 0, 1) == ".") {
  1637. $line_out = "." . $line_out;
  1638. }
  1639. }
  1640. fputs($this->smtp_conn,$line_out . $this->CRLF);
  1641. }
  1642. }
  1643. # ok all the message data has been sent so lets get this
  1644. # over with aleady
  1645. fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF);
  1646. $rply = $this->get_lines();
  1647. $code = substr($rply,0,3);
  1648. if($this->do_debug >= 2) {
  1649. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  1650. }
  1651. if($code != 250) {
  1652. $this->error =
  1653. array("error" => "DATA not accepted from server",
  1654. "smtp_code" => $code,
  1655. "smtp_msg" => substr($rply,4));
  1656. if($this->do_debug >= 1) {
  1657. echo "SMTP -> ERROR: " . $this->error["error"] .
  1658. ": " . $rply . $this->CRLF;
  1659. }
  1660. return false;
  1661. }
  1662. return true;
  1663. }
  1664. /**
  1665. * Expand takes the name and asks the server to list all the
  1666. * people who are members of the _list_. Expand will return
  1667. * back and array of the result or false if an error occurs.
  1668. * Each value in the array returned has the format of:
  1669. * [ <full-name> <sp> ] <path>
  1670. * The definition of <path> is defined in rfc 821
  1671. *
  1672. * Implements rfc 821: EXPN <SP> <string> <CRLF>
  1673. *
  1674. * SMTP CODE SUCCESS: 250
  1675. * SMTP CODE FAILURE: 550
  1676. * SMTP CODE ERROR : 500,501,502,504,421
  1677. * @access public
  1678. * @return string array
  1679. */
  1680. function Expand($name) {
  1681. $this->error = null; # so no confusion is caused
  1682. if(!$this->connected()) {
  1683. $this->error = array(
  1684. "error" => "Called Expand() without being connected");
  1685. return false;
  1686. }
  1687. fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF);
  1688. $rply = $this->get_lines();
  1689. $code = substr($rply,0,3);
  1690. if($this->do_debug >= 2) {
  1691. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  1692. }
  1693. if($code != 250) {
  1694. $this->error =
  1695. array("error" => "EXPN not accepted from server",
  1696. "smtp_code" => $code,
  1697. "smtp_msg" => substr($rply,4));
  1698. if($this->do_debug >= 1) {
  1699. echo "SMTP -> ERROR: " . $this->error["error"] .
  1700. ": " . $rply . $this->CRLF;
  1701. }
  1702. return false;
  1703. }
  1704. # parse the reply and place in our array to return to user
  1705. $entries = explode($this->CRLF,$rply);
  1706. while(list(,$l) = @each($entries)) {
  1707. $list[] = substr($l,4);
  1708. }
  1709. return $list;
  1710. }
  1711. /**
  1712. * Sends the HELO command to the smtp server.
  1713. * This makes sure that we and the server are in
  1714. * the same known state.
  1715. *
  1716. * Implements from rfc 821: HELO <SP> <domain> <CRLF>
  1717. *
  1718. * SMTP CODE SUCCESS: 250
  1719. * SMTP CODE ERROR : 500, 501, 504, 421
  1720. * @access public
  1721. * @return bool
  1722. */
  1723. function Hello($host="") {
  1724. $this->error = null; # so no confusion is caused
  1725. if(!$this->connected()) {
  1726. $this->error = array(
  1727. "error" => "Called Hello() without being connected");
  1728. return false;
  1729. }
  1730. # if a hostname for the HELO wasn't specified determine
  1731. # a suitable one to send
  1732. if(empty($host)) {
  1733. # we need to determine some sort of appopiate default
  1734. # to send to the server
  1735. $host = "localhost";
  1736. }
  1737. // Send extended hello first (RFC 2821)
  1738. if(!$this->SendHello("EHLO", $host))
  1739. {
  1740. if(!$this->SendHello("HELO", $host))
  1741. return false;
  1742. }
  1743. return true;
  1744. }
  1745. /**
  1746. * Sends a HELO/EHLO command.
  1747. * @access private
  1748. * @return bool
  1749. */
  1750. function SendHello($hello, $host) {
  1751. fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF);
  1752. $rply = $this->get_lines();
  1753. $code = substr($rply,0,3);
  1754. if($this->do_debug >= 2) {
  1755. echo "SMTP -> FROM SERVER: " . $this->CRLF . $rply;
  1756. }
  1757. if($code != 250) {
  1758. $this->error =
  1759. array("error" => $hello . " not accepted from server",
  1760. "smtp_code" => $code,
  1761. "smtp_msg" => substr($rply,4));
  1762. if($this->do_debug >= 1) {
  1763. echo "SMTP -> ERROR: " . $this->error["error"] .
  1764. ": " . $rply . $this->CRLF;
  1765. }
  1766. return false;
  1767. }
  1768. $this->helo_rply = $rply;
  1769. return true;
  1770. }
  1771. /**
  1772. * Gets help information on the keyword specified. If the keyword
  1773. * is not specified then returns generic help, ussually contianing
  1774. * A list of keywords that help is available on. This function
  1775. * returns the results back to the user. It is up to the user to
  1776. * handle the returned data. If an error occurs then false is
  1777. * returned with $this->error set appropiately.
  1778. *
  1779. * Implements rfc 821: HELP [ <SP> <string> ] <CRLF>
  1780. *
  1781. * SMTP CODE SUCCESS: 211,214
  1782. * SMTP CODE ERROR : 500,501,502,504,421
  1783. * @access public
  1784. * @return string
  1785. */
  1786. function Help($keyword="") {
  1787. $this->error = null; # to avoid confusion
  1788. if(!$this->connected()) {
  1789. $this->error = array(
  1790. "error" => "Called Help() without being connected");
  1791. return false;
  1792. }
  1793. $extra = "";
  1794. if(!empty($keyword)) {
  1795. $extra = " " . $keyword;
  1796. }
  1797. fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF);
  1798. $rply = $this->get_lines();
  1799. $code = substr($rply,0,3);
  1800. if($this->do_debug >= 2) {
  1801. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  1802. }
  1803. if($code != 211 && $code != 214) {
  1804. $this->error =
  1805. array("error" => "HELP not accepted from server",
  1806. "smtp_code" => $code,
  1807. "smtp_msg" => substr($rply,4));
  1808. if($this->do_debug >= 1) {
  1809. echo "SMTP -> ERROR: " . $this->error["error"] .
  1810. ": " . $rply . $this->CRLF;
  1811. }
  1812. return false;
  1813. }
  1814. return $rply;
  1815. }
  1816. /**
  1817. * Starts a mail transaction from the email address specified in
  1818. * $from. Returns true if successful or false otherwise. If True
  1819. * the mail transaction is started and then one or more Recipient
  1820. * commands may be called followed by a Data command.
  1821. *
  1822. * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
  1823. *
  1824. * SMTP CODE SUCCESS: 250
  1825. * SMTP CODE SUCCESS: 552,451,452
  1826. * SMTP CODE SUCCESS: 500,501,421
  1827. * @access public
  1828. * @return bool
  1829. */
  1830. function Mail($from) {
  1831. $this->error = null; # so no confusion is caused
  1832. if(!$this->connected()) {
  1833. $this->error = array(
  1834. "error" => "Called Mail() without being connected");
  1835. return false;
  1836. }
  1837. fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $this->CRLF);
  1838. $rply = $this->get_lines();
  1839. $code = substr($rply,0,3);
  1840. if($this->do_debug >= 2) {
  1841. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  1842. }
  1843. if($code != 250) {
  1844. $this->error =
  1845. array("error" => "MAIL not accepted from server",
  1846. "smtp_code" => $code,
  1847. "smtp_msg" => substr($rply,4));
  1848. if($this->do_debug >= 1) {
  1849. echo "SMTP -> ERROR: " . $this->error["error"] .
  1850. ": " . $rply . $this->CRLF;
  1851. }
  1852. return false;
  1853. }
  1854. return true;
  1855. }
  1856. /**
  1857. * Sends the command NOOP to the SMTP server.
  1858. *
  1859. * Implements from rfc 821: NOOP <CRLF>
  1860. *
  1861. * SMTP CODE SUCCESS: 250
  1862. * SMTP CODE ERROR : 500, 421
  1863. * @access public
  1864. * @return bool
  1865. */
  1866. function Noop() {
  1867. $this->error = null; # so no confusion is caused
  1868. if(!$this->connected()) {
  1869. $this->error = array(
  1870. "error" => "Called Noop() without being connected");
  1871. return false;
  1872. }
  1873. fputs($this->smtp_conn,"NOOP" . $this->CRLF);
  1874. $rply = $this->get_lines();
  1875. $code = substr($rply,0,3);
  1876. if($this->do_debug >= 2) {
  1877. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  1878. }
  1879. if($code != 250) {
  1880. $this->error =
  1881. array("error" => "NOOP not accepted from server",
  1882. "smtp_code" => $code,
  1883. "smtp_msg" => substr($rply,4));
  1884. if($this->do_debug >= 1) {
  1885. echo "SMTP -> ERROR: " . $this->error["error"] .
  1886. ": " . $rply . $this->CRLF;
  1887. }
  1888. return false;
  1889. }
  1890. return true;
  1891. }
  1892. /**
  1893. * Sends the quit command to the server and then closes the socket
  1894. * if there is no error or the $close_on_error argument is true.
  1895. *
  1896. * Implements from rfc 821: QUIT <CRLF>
  1897. *
  1898. * SMTP CODE SUCCESS: 221
  1899. * SMTP CODE ERROR : 500
  1900. * @access public
  1901. * @return bool
  1902. */
  1903. function Quit($close_on_error=true) {
  1904. $this->error = null; # so there is no confusion
  1905. if(!$this->connected()) {
  1906. $this->error = array(
  1907. "error" => "Called Quit() without being connected");
  1908. return false;
  1909. }
  1910. # send the quit command to the server
  1911. fputs($this->smtp_conn,"quit" . $this->CRLF);
  1912. # get any good-bye messages
  1913. $byemsg = $this->get_lines();
  1914. if($this->do_debug >= 2) {
  1915. echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg;
  1916. }
  1917. $rval = true;
  1918. $e = null;
  1919. $code = substr($byemsg,0,3);
  1920. if($code != 221) {
  1921. # use e as a tmp var cause Close will overwrite $this->error
  1922. $e = array("error" => "SMTP server rejected quit command",
  1923. "smtp_code" => $code,
  1924. "smtp_rply" => substr($byemsg,4));
  1925. $rval = false;
  1926. if($this->do_debug >= 1) {
  1927. echo "SMTP -> ERROR: " . $e["error"] . ": " .
  1928. $byemsg . $this->CRLF;
  1929. }
  1930. }
  1931. if(empty($e) || $close_on_error) {
  1932. $this->Close();
  1933. }
  1934. return $rval;
  1935. }
  1936. /**
  1937. * Sends the command RCPT to the SMTP server with the TO: argument of $to.
  1938. * Returns true if the recipient was accepted false if it was rejected.
  1939. *
  1940. * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
  1941. *
  1942. * SMTP CODE SUCCESS: 250,251
  1943. * SMTP CODE FAILURE: 550,551,552,553,450,451,452
  1944. * SMTP CODE ERROR : 500,501,503,421
  1945. * @access public
  1946. * @return bool
  1947. */
  1948. function Recipient($to) {
  1949. $this->error = null; # so no confusion is caused
  1950. if(!$this->connected()) {
  1951. $this->error = array(
  1952. "error" => "Called Recipient() without being connected");
  1953. return false;
  1954. }
  1955. fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF);
  1956. $rply = $this->get_lines();
  1957. $code = substr($rply,0,3);
  1958. if($this->do_debug >= 2) {
  1959. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  1960. }
  1961. if($code != 250 && $code != 251) {
  1962. $this->error =
  1963. array("error" => "RCPT not accepted from server",
  1964. "smtp_code" => $code,
  1965. "smtp_msg" => substr($rply,4));
  1966. if($this->do_debug >= 1) {
  1967. echo "SMTP -> ERROR: " . $this->error["error"] .
  1968. ": " . $rply . $this->CRLF;
  1969. }
  1970. return false;
  1971. }
  1972. return true;
  1973. }
  1974. /**
  1975. * Sends the RSET command to abort and transaction that is
  1976. * currently in progress. Returns true if successful false
  1977. * otherwise.
  1978. *
  1979. * Implements rfc 821: RSET <CRLF>
  1980. *
  1981. * SMTP CODE SUCCESS: 250
  1982. * SMTP CODE ERROR : 500,501,504,421
  1983. * @access public
  1984. * @return bool
  1985. */
  1986. function Reset() {
  1987. $this->error = null; # so no confusion is caused
  1988. if(!$this->connected()) {
  1989. $this->error = array(
  1990. "error" => "Called Reset() without being connected");
  1991. return false;
  1992. }
  1993. fputs($this->smtp_conn,"RSET" . $this->CRLF);
  1994. $rply = $this->get_lines();
  1995. $code = substr($rply,0,3);
  1996. if($this->do_debug >= 2) {
  1997. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  1998. }
  1999. if($code != 250) {
  2000. $this->error =
  2001. array("error" => "RSET failed",
  2002. "smtp_code" => $code,
  2003. "smtp_msg" => substr($rply,4));
  2004. if($this->do_debug >= 1) {
  2005. echo "SMTP -> ERROR: " . $this->error["error"] .
  2006. ": " . $rply . $this->CRLF;
  2007. }
  2008. return false;
  2009. }
  2010. return true;
  2011. }
  2012. /**
  2013. * Starts a mail transaction from the email address specified in
  2014. * $from. Returns true if successful or false otherwise. If True
  2015. * the mail transaction is started and then one or more Recipient
  2016. * commands may be called followed by a Data command. This command
  2017. * will send the message to the users terminal if they are logged
  2018. * in.
  2019. *
  2020. * Implements rfc 821: SEND <SP> FROM:<reverse-path> <CRLF>
  2021. *
  2022. * SMTP CODE SUCCESS: 250
  2023. * SMTP CODE SUCCESS: 552,451,452
  2024. * SMTP CODE SUCCESS: 500,501,502,421
  2025. * @access public
  2026. * @return bool
  2027. */
  2028. function Send($from) {
  2029. $this->error = null; # so no confusion is caused
  2030. if(!$this->connected()) {
  2031. $this->error = array(
  2032. "error" => "Called Send() without being connected");
  2033. return false;
  2034. }
  2035. fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF);
  2036. $rply = $this->get_lines();
  2037. $code = substr($rply,0,3);
  2038. if($this->do_debug >= 2) {
  2039. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  2040. }
  2041. if($code != 250) {
  2042. $this->error =
  2043. array("error" => "SEND not accepted from server",
  2044. "smtp_code" => $code,
  2045. "smtp_msg" => substr($rply,4));
  2046. if($this->do_debug >= 1) {
  2047. echo "SMTP -> ERROR: " . $this->error["error"] .
  2048. ": " . $rply . $this->CRLF;
  2049. }
  2050. return false;
  2051. }
  2052. return true;
  2053. }
  2054. /**
  2055. * Starts a mail transaction from the email address specified in
  2056. * $from. Returns true if successful or false otherwise. If True
  2057. * the mail transaction is started and then one or more Recipient
  2058. * commands may be called followed by a Data command. This command
  2059. * will send the message to the users terminal if they are logged
  2060. * in and send them an email.
  2061. *
  2062. * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
  2063. *
  2064. * SMTP CODE SUCCESS: 250
  2065. * SMTP CODE SUCCESS: 552,451,452
  2066. * SMTP CODE SUCCESS: 500,501,502,421
  2067. * @access public
  2068. * @return bool
  2069. */
  2070. function SendAndMail($from) {
  2071. $this->error = null; # so no confusion is caused
  2072. if(!$this->connected()) {
  2073. $this->error = array(
  2074. "error" => "Called SendAndMail() without being connected");
  2075. return false;
  2076. }
  2077. fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF);
  2078. $rply = $this->get_lines();
  2079. $code = substr($rply,0,3);
  2080. if($this->do_debug >= 2) {
  2081. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  2082. }
  2083. if($code != 250) {
  2084. $this->error =
  2085. array("error" => "SAML not accepted from server",
  2086. "smtp_code" => $code,
  2087. "smtp_msg" => substr($rply,4));
  2088. if($this->do_debug >= 1) {
  2089. echo "SMTP -> ERROR: " . $this->error["error"] .
  2090. ": " . $rply . $this->CRLF;
  2091. }
  2092. return false;
  2093. }
  2094. return true;
  2095. }
  2096. /**
  2097. * Starts a mail transaction from the email address specified in
  2098. * $from. Returns true if successful or false otherwise. If True
  2099. * the mail transaction is started and then one or more Recipient
  2100. * commands may be called followed by a Data command. This command
  2101. * will send the message to the users terminal if they are logged
  2102. * in or mail it to them if they are not.
  2103. *
  2104. * Implements rfc 821: SOML <SP> FROM:<reverse-path> <CRLF>
  2105. *
  2106. * SMTP CODE SUCCESS: 250
  2107. * SMTP CODE SUCCESS: 552,451,452
  2108. * SMTP CODE SUCCESS: 500,501,502,421
  2109. * @access public
  2110. * @return bool
  2111. */
  2112. function SendOrMail($from) {
  2113. $this->error = null; # so no confusion is caused
  2114. if(!$this->connected()) {
  2115. $this->error = array(
  2116. "error" => "Called SendOrMail() without being connected");
  2117. return false;
  2118. }
  2119. fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF);
  2120. $rply = $this->get_lines();
  2121. $code = substr($rply,0,3);
  2122. if($this->do_debug >= 2) {
  2123. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  2124. }
  2125. if($code != 250) {
  2126. $this->error =
  2127. array("error" => "SOML not accepted from server",
  2128. "smtp_code" => $code,
  2129. "smtp_msg" => substr($rply,4));
  2130. if($this->do_debug >= 1) {
  2131. echo "SMTP -> ERROR: " . $this->error["error"] .
  2132. ": " . $rply . $this->CRLF;
  2133. }
  2134. return false;
  2135. }
  2136. return true;
  2137. }
  2138. /**
  2139. * This is an optional command for SMTP that this class does not
  2140. * support. This method is here to make the RFC821 Definition
  2141. * complete for this class and __may__ be implimented in the future
  2142. *
  2143. * Implements from rfc 821: TURN <CRLF>
  2144. *
  2145. * SMTP CODE SUCCESS: 250
  2146. * SMTP CODE FAILURE: 502
  2147. * SMTP CODE ERROR : 500, 503
  2148. * @access public
  2149. * @return bool
  2150. */
  2151. function Turn() {
  2152. $this->error = array("error" => "This method, TURN, of the SMTP ".
  2153. "is not implemented");
  2154. if($this->do_debug >= 1) {
  2155. echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF;
  2156. }
  2157. return false;
  2158. }
  2159. /**
  2160. * Verifies that the name is recognized by the server.
  2161. * Returns false if the name could not be verified otherwise
  2162. * the response from the server is returned.
  2163. *
  2164. * Implements rfc 821: VRFY <SP> <string> <CRLF>
  2165. *
  2166. * SMTP CODE SUCCESS: 250,251
  2167. * SMTP CODE FAILURE: 550,551,553
  2168. * SMTP CODE ERROR : 500,501,502,421
  2169. * @access public
  2170. * @return int
  2171. */
  2172. function Verify($name) {
  2173. $this->error = null; # so no confusion is caused
  2174. if(!$this->connected()) {
  2175. $this->error = array(
  2176. "error" => "Called Verify() without being connected");
  2177. return false;
  2178. }
  2179. fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF);
  2180. $rply = $this->get_lines();
  2181. $code = substr($rply,0,3);
  2182. if($this->do_debug >= 2) {
  2183. echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
  2184. }
  2185. if($code != 250 && $code != 251) {
  2186. $this->error =
  2187. array("error" => "VRFY failed on name '$name'",
  2188. "smtp_code" => $code,
  2189. "smtp_msg" => substr($rply,4));
  2190. if($this->do_debug >= 1) {
  2191. echo "SMTP -> ERROR: " . $this->error["error"] .
  2192. ": " . $rply . $this->CRLF;
  2193. }
  2194. return false;
  2195. }
  2196. return $rply;
  2197. }
  2198. /*******************************************************************
  2199. * INTERNAL FUNCTIONS *
  2200. ******************************************************************/
  2201. /**
  2202. * Read in as many lines as possible
  2203. * either before eof or socket timeout occurs on the operation.
  2204. * With SMTP we can tell if we have more lines to read if the
  2205. * 4th character is '-' symbol. If it is a space then we don't
  2206. * need to read anything else.
  2207. * @access private
  2208. * @return string
  2209. */
  2210. function get_lines() {
  2211. set_time_limit(0);
  2212. $data = "";
  2213. while($str = fgets($this->smtp_conn,515)) {
  2214. if($this->do_debug >= 4) {
  2215. echo "SMTP -> get_lines(): \$data was \"$data\"" .
  2216. $this->CRLF;
  2217. echo "SMTP -> get_lines(): \$str is \"$str\"" .
  2218. $this->CRLF;
  2219. }
  2220. $data .= $str;
  2221. if($this->do_debug >= 4) {
  2222. echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF;
  2223. }
  2224. # if the 4th character is a space then we are done reading
  2225. # so just break the loop
  2226. if(substr($str,3,1) == " ") { break; }
  2227. }
  2228. return $data;
  2229. }
  2230. }
  2231. ?>