OSDN Git Service

Добавлена возможность создавать новую запись предмета оборудования на основании уже...
[invent/invent.git] / vendor / swiftmailer / swiftmailer / lib / classes / Swift / Signers / DomainKeySigner.php
1 <?php
2
3 /*
4  * This file is part of SwiftMailer.
5  * (c) 2004-2009 Chris Corbyn
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  * DomainKey Signer used to apply DomainKeys Signature to a message.
13  *
14  * @author     Xavier De Cock <xdecock@gmail.com>
15  */
16 class Swift_Signers_DomainKeySigner implements Swift_Signers_HeaderSigner
17 {
18     /**
19      * PrivateKey.
20      *
21      * @var string
22      */
23     protected $privateKey;
24
25     /**
26      * DomainName.
27      *
28      * @var string
29      */
30     protected $domainName;
31
32     /**
33      * Selector.
34      *
35      * @var string
36      */
37     protected $selector;
38
39     /**
40      * Hash algorithm used.
41      *
42      * @var string
43      */
44     protected $hashAlgorithm = 'rsa-sha1';
45
46     /**
47      * Canonisation method.
48      *
49      * @var string
50      */
51     protected $canon = 'simple';
52
53     /**
54      * Headers not being signed.
55      *
56      * @var array
57      */
58     protected $ignoredHeaders = [];
59
60     /**
61      * Signer identity.
62      *
63      * @var string
64      */
65     protected $signerIdentity;
66
67     /**
68      * Must we embed signed headers?
69      *
70      * @var bool
71      */
72     protected $debugHeaders = false;
73
74     // work variables
75     /**
76      * Headers used to generate hash.
77      *
78      * @var array
79      */
80     private $signedHeaders = [];
81
82     /**
83      * Stores the signature header.
84      *
85      * @var Swift_Mime_Headers_ParameterizedHeader
86      */
87     protected $domainKeyHeader;
88
89     /**
90      * Hash Handler.
91      *
92      * @var resource|null
93      */
94     private $hashHandler;
95
96     private $canonData = '';
97
98     private $bodyCanonEmptyCounter = 0;
99
100     private $bodyCanonIgnoreStart = 2;
101
102     private $bodyCanonSpace = false;
103
104     private $bodyCanonLastChar = null;
105
106     private $bodyCanonLine = '';
107
108     private $bound = [];
109
110     /**
111      * Constructor.
112      *
113      * @param string $privateKey
114      * @param string $domainName
115      * @param string $selector
116      */
117     public function __construct($privateKey, $domainName, $selector)
118     {
119         $this->privateKey = $privateKey;
120         $this->domainName = $domainName;
121         $this->signerIdentity = '@'.$domainName;
122         $this->selector = $selector;
123     }
124
125     /**
126      * Resets internal states.
127      *
128      * @return $this
129      */
130     public function reset()
131     {
132         $this->hashHandler = null;
133         $this->bodyCanonIgnoreStart = 2;
134         $this->bodyCanonEmptyCounter = 0;
135         $this->bodyCanonLastChar = null;
136         $this->bodyCanonSpace = false;
137
138         return $this;
139     }
140
141     /**
142      * Writes $bytes to the end of the stream.
143      *
144      * Writing may not happen immediately if the stream chooses to buffer.  If
145      * you want to write these bytes with immediate effect, call {@link commit()}
146      * after calling write().
147      *
148      * This method returns the sequence ID of the write (i.e. 1 for first, 2 for
149      * second, etc etc).
150      *
151      * @param string $bytes
152      *
153      * @return int
154      *
155      * @throws Swift_IoException
156      *
157      * @return $this
158      */
159     public function write($bytes)
160     {
161         $this->canonicalizeBody($bytes);
162         foreach ($this->bound as $is) {
163             $is->write($bytes);
164         }
165
166         return $this;
167     }
168
169     /**
170      * For any bytes that are currently buffered inside the stream, force them
171      * off the buffer.
172      *
173      * @throws Swift_IoException
174      *
175      * @return $this
176      */
177     public function commit()
178     {
179         // Nothing to do
180         return $this;
181     }
182
183     /**
184      * Attach $is to this stream.
185      *
186      * The stream acts as an observer, receiving all data that is written.
187      * All {@link write()} and {@link flushBuffers()} operations will be mirrored.
188      *
189      * @return $this
190      */
191     public function bind(Swift_InputByteStream $is)
192     {
193         // Don't have to mirror anything
194         $this->bound[] = $is;
195
196         return $this;
197     }
198
199     /**
200      * Remove an already bound stream.
201      *
202      * If $is is not bound, no errors will be raised.
203      * If the stream currently has any buffered data it will be written to $is
204      * before unbinding occurs.
205      *
206      * @return $this
207      */
208     public function unbind(Swift_InputByteStream $is)
209     {
210         // Don't have to mirror anything
211         foreach ($this->bound as $k => $stream) {
212             if ($stream === $is) {
213                 unset($this->bound[$k]);
214
215                 break;
216             }
217         }
218
219         return $this;
220     }
221
222     /**
223      * Flush the contents of the stream (empty it) and set the internal pointer
224      * to the beginning.
225      *
226      * @throws Swift_IoException
227      *
228      * @return $this
229      */
230     public function flushBuffers()
231     {
232         $this->reset();
233
234         return $this;
235     }
236
237     /**
238      * Set hash_algorithm, must be one of rsa-sha256 | rsa-sha1 defaults to rsa-sha256.
239      *
240      * @param string $hash
241      *
242      * @return $this
243      */
244     public function setHashAlgorithm($hash)
245     {
246         $this->hashAlgorithm = 'rsa-sha1';
247
248         return $this;
249     }
250
251     /**
252      * Set the canonicalization algorithm.
253      *
254      * @param string $canon simple | nofws defaults to simple
255      *
256      * @return $this
257      */
258     public function setCanon($canon)
259     {
260         if ('nofws' == $canon) {
261             $this->canon = 'nofws';
262         } else {
263             $this->canon = 'simple';
264         }
265
266         return $this;
267     }
268
269     /**
270      * Set the signer identity.
271      *
272      * @param string $identity
273      *
274      * @return $this
275      */
276     public function setSignerIdentity($identity)
277     {
278         $this->signerIdentity = $identity;
279
280         return $this;
281     }
282
283     /**
284      * Enable / disable the DebugHeaders.
285      *
286      * @param bool $debug
287      *
288      * @return $this
289      */
290     public function setDebugHeaders($debug)
291     {
292         $this->debugHeaders = (bool) $debug;
293
294         return $this;
295     }
296
297     /**
298      * Start Body.
299      */
300     public function startBody()
301     {
302     }
303
304     /**
305      * End Body.
306      */
307     public function endBody()
308     {
309         $this->endOfBody();
310     }
311
312     /**
313      * Returns the list of Headers Tampered by this plugin.
314      *
315      * @return array
316      */
317     public function getAlteredHeaders()
318     {
319         if ($this->debugHeaders) {
320             return ['DomainKey-Signature', 'X-DebugHash'];
321         }
322
323         return ['DomainKey-Signature'];
324     }
325
326     /**
327      * Adds an ignored Header.
328      *
329      * @param string $header_name
330      *
331      * @return $this
332      */
333     public function ignoreHeader($header_name)
334     {
335         $this->ignoredHeaders[strtolower($header_name)] = true;
336
337         return $this;
338     }
339
340     /**
341      * Set the headers to sign.
342      *
343      * @return $this
344      */
345     public function setHeaders(Swift_Mime_SimpleHeaderSet $headers)
346     {
347         $this->startHash();
348         $this->canonData = '';
349         // Loop through Headers
350         $listHeaders = $headers->listAll();
351         foreach ($listHeaders as $hName) {
352             // Check if we need to ignore Header
353             if (!isset($this->ignoredHeaders[strtolower($hName)])) {
354                 if ($headers->has($hName)) {
355                     $tmp = $headers->getAll($hName);
356                     foreach ($tmp as $header) {
357                         if ('' != $header->getFieldBody()) {
358                             $this->addHeader($header->toString());
359                             $this->signedHeaders[] = $header->getFieldName();
360                         }
361                     }
362                 }
363             }
364         }
365         $this->endOfHeaders();
366
367         return $this;
368     }
369
370     /**
371      * Add the signature to the given Headers.
372      *
373      * @return $this
374      */
375     public function addSignature(Swift_Mime_SimpleHeaderSet $headers)
376     {
377         // Prepare the DomainKey-Signature Header
378         $params = ['a' => $this->hashAlgorithm, 'b' => chunk_split(base64_encode($this->getEncryptedHash()), 73, ' '), 'c' => $this->canon, 'd' => $this->domainName, 'h' => implode(': ', $this->signedHeaders), 'q' => 'dns', 's' => $this->selector];
379         $string = '';
380         foreach ($params as $k => $v) {
381             $string .= $k.'='.$v.'; ';
382         }
383         $string = trim($string);
384         $headers->addTextHeader('DomainKey-Signature', $string);
385
386         return $this;
387     }
388
389     /* Private helpers */
390
391     protected function addHeader($header)
392     {
393         switch ($this->canon) {
394             case 'nofws':
395                 // Prepare Header and cascade
396                 $exploded = explode(':', $header, 2);
397                 $name = strtolower(trim($exploded[0]));
398                 $value = str_replace("\r\n", '', $exploded[1]);
399                 $value = preg_replace("/[ \t][ \t]+/", ' ', $value);
400                 $header = $name.':'.trim($value)."\r\n";
401                 // no break
402             case 'simple':
403                 // Nothing to do
404         }
405         $this->addToHash($header);
406     }
407
408     protected function endOfHeaders()
409     {
410         $this->bodyCanonEmptyCounter = 1;
411     }
412
413     protected function canonicalizeBody($string)
414     {
415         $len = \strlen($string);
416         $canon = '';
417         $nofws = ('nofws' == $this->canon);
418         for ($i = 0; $i < $len; ++$i) {
419             if ($this->bodyCanonIgnoreStart > 0) {
420                 --$this->bodyCanonIgnoreStart;
421                 continue;
422             }
423             switch ($string[$i]) {
424                 case "\r":
425                     $this->bodyCanonLastChar = "\r";
426                     break;
427                 case "\n":
428                     if ("\r" == $this->bodyCanonLastChar) {
429                         if ($nofws) {
430                             $this->bodyCanonSpace = false;
431                         }
432                         if ('' == $this->bodyCanonLine) {
433                             ++$this->bodyCanonEmptyCounter;
434                         } else {
435                             $this->bodyCanonLine = '';
436                             $canon .= "\r\n";
437                         }
438                     } else {
439                         // Wooops Error
440                         throw new Swift_SwiftException('Invalid new line sequence in mail found \n without preceding \r');
441                     }
442                     break;
443                 case ' ':
444                 case "\t":
445                 case "\x09": //HTAB
446                     if ($nofws) {
447                         $this->bodyCanonSpace = true;
448                         break;
449                     }
450                     // no break
451                 default:
452                     if ($this->bodyCanonEmptyCounter > 0) {
453                         $canon .= str_repeat("\r\n", $this->bodyCanonEmptyCounter);
454                         $this->bodyCanonEmptyCounter = 0;
455                     }
456                     $this->bodyCanonLine .= $string[$i];
457                     $canon .= $string[$i];
458             }
459         }
460         $this->addToHash($canon);
461     }
462
463     protected function endOfBody()
464     {
465         if (\strlen($this->bodyCanonLine) > 0) {
466             $this->addToHash("\r\n");
467         }
468     }
469
470     private function addToHash($string)
471     {
472         $this->canonData .= $string;
473         hash_update($this->hashHandler, $string);
474     }
475
476     private function startHash()
477     {
478         // Init
479         switch ($this->hashAlgorithm) {
480             case 'rsa-sha1':
481                 $this->hashHandler = hash_init('sha1');
482                 break;
483         }
484         $this->bodyCanonLine = '';
485     }
486
487     /**
488      * @throws Swift_SwiftException
489      *
490      * @return string
491      */
492     private function getEncryptedHash()
493     {
494         $signature = '';
495         $pkeyId = openssl_get_privatekey($this->privateKey);
496         if (!$pkeyId) {
497             throw new Swift_SwiftException('Unable to load DomainKey Private Key ['.openssl_error_string().']');
498         }
499         if (openssl_sign($this->canonData, $signature, $pkeyId, OPENSSL_ALGO_SHA1)) {
500             return $signature;
501         }
502         throw new Swift_SwiftException('Unable to sign DomainKey Hash  ['.openssl_error_string().']');
503     }
504 }