source: trunk/Packages/Common/Mail.php

Last change on this file was 8, checked in by chronos, 12 months ago
  • Modified: Updated Common package.
  • Modified: Form types made as separate FormManager package.
  • Fixed: PHP 8.1 support.
File size: 8.2 KB
Line 
1<?php
2
3define('DISPOSITION_INLINE', 'inline');
4define('DISPOSITION_ATTACHMENT', 'attachment');
5
6define('CHARSET_ASCII', 'us-ascii');
7define('CHARSET_UTF8', 'utf-8');
8
9class Mail
10{
11 public string $Subject;
12 public string $From;
13 public array $Recipients;
14 public array $Bodies;
15 public array $Attachments;
16 public string $AgentIdent;
17 public string $Organization;
18 public string $ReplyTo;
19 public array $Headers;
20 private array $Priorities;
21 private string $Boundary;
22 public bool $TestMode;
23 public string $SenderAddress;
24 public string $SenderName;
25
26 function __construct()
27 {
28 $this->Priorities = array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');
29 $this->Boundary = md5(date('r', time()));
30 $this->AgentIdent = 'PHP/Mail';
31 $this->TestMode = false;
32 $this->Clear();
33 }
34
35 function Clear(): void
36 {
37 $this->Bodies = array();
38 $this->Attachments = array();
39 $this->Recipients = array();
40 $this->SenderAddress = '';
41 $this->SenderName = '';
42 $this->Subject = '';
43 $this->Organization = '';
44 $this->ReplyTo = '';
45 $this->Headers = array();
46 }
47
48 function AddToCombined(string $Address): void
49 {
50 $this->Recipients[] = array('Address' => $Address, 'Type' => 'SendCombined');
51 }
52
53 function AddTo(string $Address, string $Name): void
54 {
55 $this->Recipients[] = array('Address' => $Address, 'Name' => $Name, 'Type' => 'Send');
56 }
57
58 function AddCc(string $Address, string $Name): void
59 {
60 $this->Recipients[] = array('Address' => $Address, 'Name' => $Name, 'Type' => 'Copy');
61 }
62
63 function AddBcc(string $Address, string $Name): void
64 {
65 $this->Recipients[] = array('Address' => $Address, 'Name' => $Name, 'Type' => 'HiddenCopy');
66 }
67
68 function AddBody(string $Content, string $MimeType = 'text/plain', string $Charset = 'utf-8'): void
69 {
70 $this->Bodies[] = array('Content' => $Content, 'Type' => strtolower($MimeType),
71 'Charset' => strtolower($Charset));
72 }
73
74 function Organization(string $org): void
75 {
76 if (trim($org != '')) $this->Headers['Organization'] = $org;
77 }
78
79 function Priority(int $Priority): bool
80 {
81 if (!intval($Priority)) return false;
82
83 if (!isset($this->Priorities[$Priority - 1])) return false;
84
85 $this->Headers['X-Priority'] = $this->Priorities[$Priority - 1];
86 return true;
87 }
88
89 function AttachFile($FileName, $FileType, $Disposition = DISPOSITION_ATTACHMENT): void
90 {
91 $this->Attachments[] = array('FileName' => $FileName, 'FileType' => $FileType,
92 'Disposition' => $Disposition, 'Type' => 'File');
93 }
94
95 function AttachData($FileName, $FileType, $Data, $Disposition = DISPOSITION_ATTACHMENT): void
96 {
97 $this->Attachments[] = array('FileName' => $FileName, 'FileType' => $FileType,
98 'Disposition' => $Disposition, 'Type' => 'Data', 'Data' => $Data);
99 }
100
101 function Send(): bool
102 {
103 if (count($this->Bodies) == 0) throw new Exception(T('Mail message need at least one text body'));
104
105 $Body = $this->BuildAttachment($this->BuildBody());
106
107 $To = '';
108 $this->Headers['CC'] = '';
109 $this->Headers['BCC'] = '';
110 foreach ($this->Recipients as $Index => $Recipient)
111 {
112 if ($Recipient['Type'] == 'SendCombined')
113 {
114 if ($Index > 0) $To .= ', ';
115 $To .= $Recipient['Address'];
116 } else
117 if ($Recipient['Type'] == 'Send')
118 {
119 if ($Index > 0) $To .= ', ';
120 $To .= $Recipient['Name'].' <'.$Recipient['Address'].'>';
121 } else
122 if ($Recipient['Type'] == 'Copy')
123 {
124 if ($Index > 0) $this->Headers['CC'] .= ', ';
125 $this->Headers['CC'] .= $Recipient['Name'].' <'.$To['Address'].'>';
126 } else
127 if ($Recipient['Type'] == 'HiddenCopy')
128 {
129 if ($Index > 0) $this->Headers['BCC'] .= ', ';
130 $this->Headers['BCC'] .= $Recipient['Name'].' <'.$To['Address'].'>';
131 }
132 }
133 if ($To == '') throw new Exception(T('Mail message need at least one recipient address'));
134
135 $this->Headers['Mime-Version'] = '1.0';
136
137 if ($this->AgentIdent != '') $this->Headers['X-Mailer'] = $this->AgentIdent;
138 if ($this->ReplyTo != '') $this->Headers['Reply-To'] = $this->ReplyTo;
139 if ($this->From != '')
140 {
141 $IndexStart = strpos($this->From, '<');
142 if ($IndexStart !== false)
143 {
144 $this->Headers['From'] = '=?utf-8?Q?'.quoted_printable_encode(trim(substr($this->From, 0, $IndexStart))).'?= '.substr($this->From, $IndexStart);
145 } else
146 {
147 $this->Headers['From'] = $this->From;
148 }
149 }
150
151 $Headers = '';
152 foreach ($this->Headers as $Header => $Value)
153 {
154 if (($Header != 'Subject') and ($Value != '')) $Headers .= $Header.': '.$Value."\n";
155 }
156
157 $this->Subject = strtr($this->Subject, "\r\n", ' ');
158
159 if ($this->Subject == '') throw new Exception(T('Mail message missing Subject'));
160
161 if ($this->TestMode)
162 {
163 echo('to: '.$To.', subject: '.$this->Subject.', body: '.$Body.', headers: '.$Headers);
164 $res = true;
165 } else
166 {
167 $res = mail($To, $this->Subject, $Body, $Headers);
168 }
169 return $res;
170 }
171
172 function ValidEmail(string $Address): bool
173 {
174 if (preg_match(".*<(.+)>", $Address, $regs)) $Address = $regs[1];
175 if (preg_match("^[^@ ]+@([a-zA-Z0-9\-]+\.)+([a-zA-Z0-9\-]{2}|net|com|gov|mil|org|edu|int)\$", $Address))
176 return true;
177 else return false;
178 }
179
180 function CheckAdresses(array $Addresses): void
181 {
182 foreach ($Addresses as $Address)
183 {
184 if (!$this->ValidEmail($Address))
185 {
186 throw new Exception(sprintf(T('Mail message invalid address %s'), $Address));
187 }
188 }
189 }
190
191 private function ContentEncoding($Charset): string
192 {
193 if ($Charset != CHARSET_ASCII) return '8bit';
194 else return '7bit';
195 }
196
197 private function BuildAttachment($Body): string
198 {
199 if (count($this->Attachments) > 0)
200 {
201 $this->Headers['Content-Type'] = "multipart/mixed;\n boundary=\"PHP-mixed-".$this->Boundary."\"";
202 $Result = "This is a multi-part message in MIME format.\n".
203 "--PHP-mixed-".$this->Boundary."\n";
204 $Result .= $Body;
205
206 foreach ($this->Attachments as $Attachment)
207 {
208 $FileName = $Attachment['FileName'];
209 $BaseName = basename($FileName);
210 $ContentType = $Attachment['FileType'];
211 $Disposition = $Attachment['Disposition'];
212 if ($Attachment['Type'] == 'File')
213 {
214 if (!file_exists($FileName))
215 throw new Exception(sprintf(T('Mail message attached file %s can\'t be found'), $FileName));
216 $Data = file_get_contents($FileName);
217 } else
218 if ($Attachment['Type'] == 'Data') $Data = $Attachment['Data'];
219 else $Data = '';
220
221 $Result .= "\n".'--PHP-mixed-'.$this->Boundary."\n".
222 "Content-Type: ".$ContentType."; name=\"".$BaseName."\"\n".
223 "Content-Transfer-Encoding: base64\n".
224 "Content-Disposition: ".$Disposition."\n\n";
225 $Result .= chunk_split(base64_encode($Data))."\r\n";
226 }
227 } else $Result = $Body;
228 return $Result;
229 }
230
231 private function BuildBody(): string
232 {
233 $Result = '';
234 if (count($this->Bodies) > 1)
235 {
236 $this->Headers['Content-Type'] = 'multipart/alternative; boundary="PHP-alt-'.$this->Boundary.'"';
237 $Result .= 'Content-Type: multipart/alternative; boundary="PHP-alt-'.$this->Boundary.'"'.
238 "\n\n";
239 } else
240 {
241 $this->Headers['Content-Type'] = $this->Bodies[0]['Type'].'; charset='.$this->Bodies[0]['Charset'];
242 $this->Headers['Content-Transfer-Encoding'] = $this->ContentEncoding($this->Bodies[0]['Charset']);
243 }
244
245 foreach ($this->Bodies as $Body)
246 {
247 if (count($this->Bodies) > 1) $Result .= "\n\n".'--PHP-alt-'.$this->Boundary."\n";
248 $Result .= 'Content-Type: '.$Body['Type'].'; charset='.$Body['Charset'].
249 "\nContent-Transfer-Encoding: ".$this->ContentEncoding($Body['Charset'])."\n\n".$Body['Content']."\n";
250 }
251 return $Result;
252 }
253}
254
255/*
256// Example
257$Mail = new Mail();
258$Mail->AddTo('nobody@nodomain', 'Recipient');
259$Mail->From = 'No reply <noreply@nodomain>';
260$Mail->Subject = 'Test message';
261$Mail->AddBody('This is sample message sent by PHP Mail class');
262$Mail->AddBody('This is sample <strong>HTML</strong> message sent by <i>PHP Mail class</i>', 'text/html');
263$Mail->AttachFile('mailtest.php', 'text/plain');
264$Mail->AttachData('DataFile.txt', 'text/plain', 'Sample text');
265$Mail->Send();
266*/
Note: See TracBrowser for help on using the repository browser.