PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/web/plugin/tools/plainoldsendmail/lib/external/mimemessage/smtp/smtp.php

https://bitbucket.org/wildanm/oplaysms
PHP | 617 lines | 582 code | 24 blank | 11 comment | 122 complexity | 6c0ae40b176761328eca0df83d1d89eb MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, GPL-2.0
  1. <?php
  2. /*
  3. * smtp.php
  4. *
  5. * @(#) $Header: /home/mlemos/cvsroot/PHPlibrary/smtp.php,v 1.25 2003/03/25 23:13:15 mlemos Exp $
  6. *
  7. */
  8. class smtp_class
  9. {
  10. var $user="";
  11. var $realm="";
  12. var $password="";
  13. var $host_name="";
  14. var $host_port=25;
  15. var $localhost="";
  16. var $timeout=0;
  17. var $direct_delivery=0;
  18. var $error="";
  19. var $debug=0;
  20. var $esmtp=1;
  21. var $esmtp_host="";
  22. var $esmtp_extensions=array();
  23. var $maximum_piped_recipients=100;
  24. var $exclude_address="";
  25. var $getmxrr="GetMXRR";
  26. /* private variables - DO NOT ACCESS */
  27. var $state="Disconnected";
  28. var $connection=0;
  29. var $pending_recipients=0;
  30. var $next_token="";
  31. var $direct_sender="";
  32. var $connected_domain="";
  33. /* Private methods - DO NOT CALL */
  34. Function Tokenize($string,$separator="")
  35. {
  36. if(!strcmp($separator,""))
  37. {
  38. $separator=$string;
  39. $string=$this->next_token;
  40. }
  41. for($character=0;$character<strlen($separator);$character++)
  42. {
  43. if(GetType($position=strpos($string,$separator[$character]))=="integer")
  44. $found=(IsSet($found) ? min($found,$position) : $position);
  45. }
  46. if(IsSet($found))
  47. {
  48. $this->next_token=substr($string,$found+1);
  49. return(substr($string,0,$found));
  50. }
  51. else
  52. {
  53. $this->next_token="";
  54. return($string);
  55. }
  56. }
  57. Function OutputDebug($message)
  58. {
  59. echo $message,"\n";
  60. }
  61. Function GetLine()
  62. {
  63. for($line="";;)
  64. {
  65. if(feof($this->connection))
  66. {
  67. $this->error="reached the end of stream while reading from socket";
  68. return(0);
  69. }
  70. if(GetType($data=fgets($this->connection,100))!="string"
  71. || strlen($data)==0)
  72. {
  73. $this->error="it was not possible to read line from socket";
  74. return(0);
  75. }
  76. $line.=$data;
  77. $length=strlen($line);
  78. if($length>=2
  79. && substr($line,$length-2,2)=="\r\n")
  80. {
  81. $line=substr($line,0,$length-2);
  82. if($this->debug)
  83. $this->OutputDebug("S $line");
  84. return($line);
  85. }
  86. }
  87. }
  88. Function PutLine($line)
  89. {
  90. if($this->debug)
  91. $this->OutputDebug("C $line");
  92. if(!fputs($this->connection,"$line\r\n"))
  93. {
  94. $this->error="it was not possible to write line to socket";
  95. return(0);
  96. }
  97. return(1);
  98. }
  99. Function PutData(&$data)
  100. {
  101. if(strlen($data))
  102. {
  103. if($this->debug)
  104. $this->OutputDebug("C $data");
  105. if(!fputs($this->connection,$data))
  106. {
  107. $this->error="it was not possible to write data to socket";
  108. return(0);
  109. }
  110. }
  111. return(1);
  112. }
  113. Function VerifyResultLines($code,&$responses)
  114. {
  115. $responses=array();
  116. Unset($match_code);
  117. while(($line=$this->GetLine($this->connection)))
  118. {
  119. if(IsSet($match_code))
  120. {
  121. if(strcmp($this->Tokenize($line," -"),$match_code))
  122. {
  123. $this->error=$line;
  124. return(0);
  125. }
  126. }
  127. else
  128. {
  129. $match_code=$this->Tokenize($line," -");
  130. if(GetType($code)=="array")
  131. {
  132. for($codes=0;$codes<count($code) && strcmp($match_code,$code[$codes]);$codes++);
  133. if($codes>=count($code))
  134. {
  135. $this->error=$line;
  136. return(0);
  137. }
  138. }
  139. else
  140. {
  141. if(strcmp($match_code,$code))
  142. {
  143. $this->error=$line;
  144. return(0);
  145. }
  146. }
  147. }
  148. $responses[]=$this->Tokenize("");
  149. if(!strcmp($match_code,$this->Tokenize($line," ")))
  150. return(1);
  151. }
  152. return(-1);
  153. }
  154. Function FlushRecipients()
  155. {
  156. if($this->pending_sender)
  157. {
  158. if($this->VerifyResultLines("250",$responses)<=0)
  159. return(0);
  160. $this->pending_sender=0;
  161. }
  162. for(;$this->pending_recipients;$this->pending_recipients--)
  163. {
  164. if($this->VerifyResultLines(array("250","251"),$responses)<=0)
  165. return(0);
  166. }
  167. return(1);
  168. }
  169. /* Public methods */
  170. Function Connect($domain="")
  171. {
  172. $this->error=$error="";
  173. $this->esmtp_host="";
  174. $this->esmtp_extensions=array();
  175. $hosts=array();
  176. if($this->direct_delivery)
  177. {
  178. if(strlen($domain)==0)
  179. return(1);
  180. $hosts=$weights=$mxhosts=array();
  181. $getmxrr=$this->getmxrr;
  182. if(function_exists($getmxrr)
  183. && $getmxrr($domain,$hosts,$weights))
  184. {
  185. for($host=0;$host<count($hosts);$host++)
  186. $mxhosts[$weights[$host]]=$hosts[$host];
  187. KSort($mxhosts);
  188. for(Reset($mxhosts),$host=0;$host<count($mxhosts);Next($mxhosts),$host++)
  189. $hosts[$host]=$mxhosts[Key($mxhosts)];
  190. }
  191. else
  192. {
  193. if(strcmp(@gethostbyname($domain),$domain)!=0)
  194. $hosts[]=$domain;
  195. }
  196. }
  197. else
  198. {
  199. if(strlen($this->host_name))
  200. $hosts[]=$this->host_name;
  201. }
  202. if(count($hosts)==0)
  203. {
  204. $this->error="could not determine the SMTP to connect";
  205. return(0);
  206. }
  207. if(strcmp($this->state,"Disconnected"))
  208. {
  209. $this->error="connection is already established";
  210. return(0);
  211. }
  212. for($host=0;;$host++)
  213. {
  214. $domain=$hosts[$host];
  215. // fixme anton - ereg is depreceted in php 5.3
  216. // if(ereg('^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$',$domain))
  217. if(preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/',$domain))
  218. $ip=$domain;
  219. else
  220. {
  221. if($this->debug)
  222. $this->OutputDebug("Resolving SMTP server domain \"$domain\"...");
  223. if(!strcmp($ip=@gethostbyname($domain),$domain))
  224. $ip="";
  225. }
  226. if(strlen($ip)==0
  227. || (strlen($this->exclude_address)
  228. && !strcmp(@gethostbyname($this->exclude_address),$ip)))
  229. {
  230. if($host==count($hosts)-1)
  231. {
  232. $this->error="could not resolve the host domain \"".$domain."\"";
  233. return(0);
  234. }
  235. continue;
  236. }
  237. if($this->debug)
  238. $this->OutputDebug("Connecting to SMTP server ip \"$ip\"...");
  239. if(($this->connection=($this->timeout ? fsockopen($ip,$this->host_port,$errno,$error,$this->timeout) : fsockopen($ip,$this->host_port))))
  240. break;
  241. if($host==count($hosts)-1)
  242. {
  243. switch($this->timeout ? strval($error) : "??")
  244. {
  245. case "-3":
  246. $this->error="-3 socket could not be created";
  247. break;
  248. case "-4":
  249. $this->error="-4 dns lookup on hostname \"".$domain."\" failed";
  250. break;
  251. case "-5":
  252. $this->error="-5 connection refused or timed out";
  253. break;
  254. case "-6":
  255. $this->error="-6 fdopen() call failed";
  256. break;
  257. case "-7":
  258. $this->error="-7 setvbuf() call failed";
  259. break;
  260. default:
  261. $this->error=$error." could not connect to the host \"".$domain."\"";
  262. break;
  263. }
  264. return(0);
  265. }
  266. }
  267. if($this->debug)
  268. $this->OutputDebug("Connected to SMTP server ip \"$ip\".");
  269. if(!strcmp($localhost=$this->localhost,"")
  270. && !strcmp($localhost=getenv("SERVER_NAME"),"")
  271. && !strcmp($localhost=getenv("HOST"),""))
  272. $localhost="localhost";
  273. $success=0;
  274. if($this->VerifyResultLines("220",$responses)>0)
  275. {
  276. $fallback=1;
  277. if($this->esmtp
  278. || strlen($this->user))
  279. {
  280. if($this->PutLine("EHLO $localhost"))
  281. {
  282. if(($success_code=$this->VerifyResultLines("250",$responses))>0)
  283. {
  284. $this->esmtp_host=$this->Tokenize($responses[0]," ");
  285. for($response=1;$response<count($responses);$response++)
  286. {
  287. $extension=strtoupper($this->Tokenize($responses[$response]," "));
  288. $this->esmtp_extensions[$extension]=$this->Tokenize("");
  289. }
  290. $success=1;
  291. $fallback=0;
  292. }
  293. else
  294. {
  295. if($success_code==0)
  296. {
  297. $code=$this->Tokenize($this->error," -");
  298. switch($code)
  299. {
  300. case "421":
  301. $fallback=0;
  302. break;
  303. }
  304. }
  305. }
  306. }
  307. else
  308. $fallback=0;
  309. }
  310. if($fallback)
  311. {
  312. if($this->PutLine("HELO $localhost")
  313. && $this->VerifyResultLines("250",$responses)>0)
  314. $success=1;
  315. }
  316. if($success
  317. && strlen($this->user))
  318. {
  319. if(!IsSet($this->esmtp_extensions["AUTH"]))
  320. {
  321. $this->error="server does not require authentication";
  322. $success=0;
  323. }
  324. else
  325. {
  326. for($authentication=$this->Tokenize($this->esmtp_extensions["AUTH"]," ");strlen($authentication);$authentication=$this->Tokenize(" "))
  327. {
  328. switch($authentication)
  329. {
  330. case "PLAIN":
  331. $success=($this->PutLine("AUTH PLAIN ".base64_encode($this->user.chr(0).$this->user.(strlen($this->realm) ? "@".$this->realm : "").chr(0).$this->password))
  332. && $this->VerifyResultLines("235",$responses));
  333. break 2;
  334. case "LOGIN":
  335. $success=($this->PutLine("AUTH LOGIN")
  336. && $this->VerifyResultLines("334",$responses)
  337. && $this->PutLine(base64_encode($this->user.(strlen($this->realm) ? "@".$this->realm : "")))
  338. && $this->VerifyResultLines("334",$responses)
  339. && $this->PutLine(base64_encode($this->password))
  340. && $this->VerifyResultLines("235",$responses));
  341. break 2;
  342. }
  343. }
  344. if($success
  345. && strlen($authentication)==0)
  346. {
  347. $this->error="the server does not require a supported authentication method";
  348. $success=0;
  349. }
  350. }
  351. }
  352. }
  353. if($success)
  354. {
  355. $this->state="Connected";
  356. $this->connected_domain=$domain;
  357. }
  358. else
  359. {
  360. fclose($this->connection);
  361. $this->connection=0;
  362. }
  363. return($success);
  364. }
  365. Function MailFrom($sender)
  366. {
  367. if($this->direct_delivery)
  368. {
  369. switch($this->state)
  370. {
  371. case "Disconnected":
  372. $this->direct_sender=$sender;
  373. return(1);
  374. case "Connected":
  375. $sender=$this->direct_sender;
  376. break;
  377. default:
  378. $this->error="direct delivery connection is already established and sender is already set";
  379. return(0);
  380. }
  381. }
  382. else
  383. {
  384. if(strcmp($this->state,"Connected"))
  385. {
  386. $this->error="connection is not in the initial state";
  387. return(0);
  388. }
  389. }
  390. $this->error="";
  391. if(!$this->PutLine("MAIL FROM:<$sender>"))
  392. return(0);
  393. if(!IsSet($this->esmtp_extensions["PIPELINING"])
  394. && $this->VerifyResultLines("250",$responses)<=0)
  395. return(0);
  396. $this->state="SenderSet";
  397. if(IsSet($this->esmtp_extensions["PIPELINING"]))
  398. $this->pending_sender=1;
  399. $this->pending_recipients=0;
  400. return(1);
  401. }
  402. Function SetRecipient($recipient)
  403. {
  404. if($this->direct_delivery)
  405. {
  406. if(GetType($at=strrpos($recipient,"@"))!="integer")
  407. return("it was not specified a valid direct recipient");
  408. $domain=substr($recipient,$at+1);
  409. switch($this->state)
  410. {
  411. case "Disconnected":
  412. if(!$this->Connect($domain))
  413. return(0);
  414. if(!$this->MailFrom(""))
  415. {
  416. $error=$this->error;
  417. $this->Disconnect();
  418. $this->error=$error;
  419. return(0);
  420. }
  421. break;
  422. case "SenderSet":
  423. case "RecipientSet":
  424. if(strcmp($this->connected_domain,$domain))
  425. {
  426. $this->error="it is not possible to deliver directly to recipients of different domains";
  427. return(0);
  428. }
  429. break;
  430. default:
  431. $this->error="connection is already established and the recipient is already set";
  432. return(0);
  433. }
  434. }
  435. else
  436. {
  437. switch($this->state)
  438. {
  439. case "SenderSet":
  440. case "RecipientSet":
  441. break;
  442. default:
  443. $this->error="connection is not in the recipient setting state";
  444. return(0);
  445. }
  446. }
  447. $this->error="";
  448. if(!$this->PutLine("RCPT TO:<$recipient>"))
  449. return(0);
  450. if(IsSet($this->esmtp_extensions["PIPELINING"]))
  451. {
  452. $this->pending_recipients++;
  453. if($this->pending_recipients>=$this->maximum_piped_recipients)
  454. {
  455. if(!$this->FlushRecipients())
  456. return(0);
  457. }
  458. }
  459. else
  460. {
  461. if($this->VerifyResultLines(array("250","251"),$responses)<=0)
  462. return(0);
  463. }
  464. $this->state="RecipientSet";
  465. return(1);
  466. }
  467. Function StartData()
  468. {
  469. if(strcmp($this->state,"RecipientSet"))
  470. {
  471. $this->error="connection is not in the start sending data state";
  472. return(0);
  473. }
  474. $this->error="";
  475. if(!$this->PutLine("DATA"))
  476. return(0);
  477. if($this->pending_recipients)
  478. {
  479. if(!$this->FlushRecipients())
  480. return(0);
  481. }
  482. if($this->VerifyResultLines("354",$responses)<=0)
  483. return(0);
  484. $this->state="SendingData";
  485. return(1);
  486. }
  487. Function PrepareData(&$data,&$output)
  488. {
  489. if(function_exists("preg_replace"))
  490. $output=preg_replace(array("/(^|[^\r])\n/","/\r([^\n])/","/\\.(\r|\$)/"),array("\\1\r\n","\r\n\\1","..\\1"),$data);
  491. else
  492. $output=ereg_replace("\\.(\r|\$)","..\\1",ereg_replace("\r([^\n])","\r\n\\1",ereg_replace("(^|[^\r])\n","\\1\r\n",$data)));
  493. }
  494. Function SendData($data)
  495. {
  496. if(strcmp($this->state,"SendingData"))
  497. {
  498. $this->error="connection is not in the sending data state";
  499. return(0);
  500. }
  501. $this->error="";
  502. return($this->PutData($data));
  503. }
  504. Function EndSendingData()
  505. {
  506. if(strcmp($this->state,"SendingData"))
  507. {
  508. $this->error="connection is not in the sending data state";
  509. return(0);
  510. }
  511. $this->error="";
  512. if(!$this->PutLine("\r\n.")
  513. || $this->VerifyResultLines("250",$responses)<=0)
  514. return(0);
  515. $this->state="Connected";
  516. return(1);
  517. }
  518. Function ResetConnection()
  519. {
  520. switch($this->state)
  521. {
  522. case "Connected":
  523. return(1);
  524. case "SendingData":
  525. $this->error="can not reset the connection while sending data";
  526. return(0);
  527. case "Disconnected":
  528. $this->error="can not reset the connection before it is established";
  529. return(0);
  530. }
  531. $this->error="";
  532. if(!$this->PutLine("RSET")
  533. || $this->VerifyResultLines("250",$responses)<=0)
  534. return(0);
  535. $this->state="Connected";
  536. return(1);
  537. }
  538. Function Disconnect($quit=1)
  539. {
  540. if(!strcmp($this->state,"Disconnected"))
  541. {
  542. $this->error="it was not previously established a SMTP connection";
  543. return(0);
  544. }
  545. $this->error="";
  546. if(!strcmp($this->state,"Connected")
  547. && $quit
  548. && (!$this->PutLine("QUIT")
  549. || $this->VerifyResultLines("221",$responses)<=0))
  550. return(0);
  551. fclose($this->connection);
  552. $this->connection=0;
  553. $this->state="Disconnected";
  554. if($this->debug)
  555. $this->OutputDebug("Disconnected.");
  556. return(1);
  557. }
  558. Function SendMessage($sender,$recipients,$headers,$body)
  559. {
  560. if(($success=$this->Connect()))
  561. {
  562. if(($success=$this->MailFrom($sender)))
  563. {
  564. for($recipient=0;$recipient<count($recipients);$recipient++)
  565. {
  566. if(!($success=$this->SetRecipient($recipients[$recipient])))
  567. break;
  568. }
  569. if($success
  570. && ($success=$this->StartData()))
  571. {
  572. for($header_data="",$header=0;$header<count($headers);$header++)
  573. $header_data.=$headers[$header]."\r\n";
  574. if(($success=$this->SendData($header_data."\r\n")))
  575. {
  576. $this->PrepareData($body,$body_data);
  577. $success=$this->SendData($body_data);
  578. }
  579. if($success)
  580. $success=$this->EndSendingData();
  581. }
  582. }
  583. $error=$this->error;
  584. $disconnect_success=$this->Disconnect($success);
  585. if($success)
  586. $success=$disconnect_success;
  587. else
  588. $this->error=$error;
  589. }
  590. return($success);
  591. }
  592. };
  593. ?>