Skip to content

Commit e4afca8

Browse files
committed
#3 transport URL
1 parent ea4b51e commit e4afca8

File tree

8 files changed

+276
-36
lines changed

8 files changed

+276
-36
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ $message = \bashkarev\email\Parser::email($file);
6262
$attachment = $message->getAttachments()[0];
6363
header("Content-Type: {$attachment->getMimeType()};");
6464
header("Content-Disposition: attachment; filename=\"{$attachment->getFileName('undefined')}\"");
65-
$attachment->getStream()->onFilter(fopen('php://output', 'c'));
65+
$attachment->getStream()->copy(fopen('php://output', 'c'));
6666
```
6767

6868
## message/partial

src/Mime.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,23 @@ public function isAttachment()
7171
}
7272

7373
/**
74-
* @return Stream
74+
* @return Stream|null
75+
* @throws \Exception
7576
*/
7677
public function getStream()
7778
{
7879
if ($this->stream === null) {
79-
$this->stream = new Stream($this);
80+
if ($this->getMimeType() === 'message/external-body') {
81+
if (
82+
($type = $this->getAccessType()) === null
83+
|| !isset(Parser::$transport[$type])
84+
) {
85+
throw new \Exception("Not Supported transport: {$type}");
86+
}
87+
$this->stream = new Parser::$transport[$type]($this);
88+
} else {
89+
$this->stream = new Stream($this);
90+
}
8091
}
8192
return $this->stream;
8293
}
@@ -158,6 +169,19 @@ public function getMimeType()
158169
return null;
159170
}
160171

172+
/**
173+
* @return null|string
174+
*/
175+
public function getAccessType()
176+
{
177+
foreach ($this->getHeader('content-type') as $value) {
178+
if (strpos($value, 'access-type') !== false) {
179+
return mb_strtolower(str_replace(['access-type', '"', ' ', '='], '', $value));
180+
}
181+
}
182+
return null;
183+
}
184+
161185
/**
162186
* @return null|string
163187
*/
@@ -234,7 +258,7 @@ public function getFileName($default = null)
234258
*/
235259
public function save($filename)
236260
{
237-
return (bool)$this->getStream()->onFilter(fopen($filename, 'wb'));
261+
return (bool)$this->getStream()->copy(fopen($filename, 'wb'));
238262
}
239263

240264
/**

src/Parser.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ class Parser
2323
public static $map = [
2424
'message/feedback-report' => 'bashkarev\email\messages\Feedback'
2525
];
26+
/**
27+
* @var array transport class map
28+
*/
29+
public static $transport = [
30+
'url' => 'bashkarev\email\transports\Url'
31+
];
2632

2733
/**
2834
* @param mixed $handles

src/Stream.php

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
/**
1111
* @author Dmitriy Bashkarev <dmitriy@bashkarev.com>
1212
*/
13-
class Stream
13+
class Stream extends Transport
1414
{
1515
/**
1616
* @var string
@@ -38,7 +38,7 @@ public function __construct(Mime $mime)
3838
}
3939

4040
/**
41-
* Close handle
41+
* @inheritdoc
4242
*/
4343
public function close()
4444
{
@@ -48,8 +48,15 @@ public function close()
4848
}
4949

5050
/**
51-
* @param $data
52-
* @return $this
51+
* @inheritdoc
52+
*/
53+
public function getHandle()
54+
{
55+
return $this->handle;
56+
}
57+
58+
/**
59+
* @inheritdoc
5360
*/
5461
public function write($data)
5562
{
@@ -58,15 +65,10 @@ public function write($data)
5865
}
5966

6067
/**
61-
* @param resource $handle
62-
* @param null|int $length
63-
* @return int
68+
* @inheritdoc
6469
*/
65-
public function onFilter($handle, $length = null)
70+
public function copy($handle, $length = null)
6671
{
67-
if ($length === null) {
68-
$length = Parser::$buffer;
69-
}
7072
switch ($this->encoded) {
7173
case 'base64':
7274
stream_filter_append($handle, 'convert.base64-decode');
@@ -75,32 +77,23 @@ public function onFilter($handle, $length = null)
7577
stream_filter_append($handle, 'convert.quoted-printable-decode');
7678
break;
7779
}
78-
rewind($this->handle);
79-
while (feof($this->handle) === false) {
80-
fwrite($handle, fread($this->handle, $length));
81-
}
82-
$i = ftell($handle);
83-
fclose($handle);
84-
return $i;
80+
return parent::copy($handle, $length);
8581
}
8682

8783
/**
88-
* @return string
84+
* @inheritdoc
8985
*/
9086
public function getContents()
9187
{
92-
ob_start();
93-
ob_implicit_flush(false);
94-
$this->onFilter(fopen('php://output', 'cb'));
95-
$contents = ob_get_clean();
88+
$contents = parent::getContents();
9689
if ($this->charset !== Parser::$charset) {
9790
$contents = mb_convert_encoding($contents, Parser::$charset, $this->charset);
9891
}
9992
return $contents;
10093
}
10194

10295
/**
103-
* @return string
96+
* @inheritdoc
10497
*/
10598
public function getBase64()
10699
{
@@ -112,12 +105,4 @@ public function getBase64()
112105
}
113106
}
114107

115-
/**
116-
* @return resource
117-
*/
118-
public function getHandle()
119-
{
120-
return $this->handle;
121-
}
122-
123108
}

src/Transport.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
/**
3+
* @copyright Copyright (c) 2017 Dmitriy Bashkarev
4+
* @license https://github.com/bashkarev/email/blob/master/LICENSE
5+
* @link https://github.com/bashkarev/email#readme
6+
*/
7+
8+
namespace bashkarev\email;
9+
10+
/**
11+
* @author Dmitriy Bashkarev <dmitriy@bashkarev.com>
12+
*/
13+
abstract class Transport
14+
{
15+
16+
/**
17+
* Transport constructor.
18+
* @param Mime $mime
19+
*/
20+
abstract public function __construct(Mime $mime);
21+
22+
/**
23+
* @return resource
24+
*/
25+
abstract public function getHandle();
26+
27+
/**
28+
* Close handle
29+
*/
30+
abstract public function close();
31+
32+
/**
33+
* @param $data
34+
* @return $this
35+
*/
36+
abstract public function write($data);
37+
38+
/**
39+
* @param resource $handle
40+
* @param null|int $length
41+
* @return int
42+
*/
43+
public function copy($handle, $length = null)
44+
{
45+
if ($length === null) {
46+
$length = Parser::$buffer;
47+
}
48+
$mainHandle = $this->getHandle();
49+
rewind($mainHandle);
50+
while (feof($mainHandle) === false) {
51+
fwrite($handle, fread($mainHandle, $length));
52+
}
53+
$i = ftell($handle);
54+
fclose($handle);
55+
return $i;
56+
}
57+
58+
/**
59+
* @return string
60+
*/
61+
public function getContents()
62+
{
63+
ob_start();
64+
ob_implicit_flush(false);
65+
$this->copy(fopen('php://output', 'cb'));
66+
return ob_get_clean();
67+
}
68+
69+
/**
70+
* @return string
71+
*/
72+
public function getBase64()
73+
{
74+
return base64_encode($this->getContents());
75+
}
76+
77+
}

src/transports/Url.php

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
/**
3+
* @copyright Copyright (c) 2017 Dmitriy Bashkarev
4+
* @license https://github.com/bashkarev/email/blob/master/LICENSE
5+
* @link https://github.com/bashkarev/email#readme
6+
*/
7+
8+
namespace bashkarev\email\transports;
9+
10+
use bashkarev\email\Mime;
11+
use bashkarev\email\Transport;
12+
13+
/**
14+
* @see https://tools.ietf.org/rfc/rfc2017.txt
15+
* @author Dmitriy Bashkarev <dmitriy@bashkarev.com>
16+
*/
17+
class Url extends Transport
18+
{
19+
/**
20+
* @var string
21+
*/
22+
protected $url;
23+
/**
24+
* @var resource
25+
*/
26+
protected $handle;
27+
28+
/**
29+
* @inheritdoc
30+
*/
31+
public function __construct(Mime $mime)
32+
{
33+
$this->setUrl($mime->getHeader('content-type'));
34+
if ($this->url === null) {
35+
throw new \Exception('Required option URL not found');
36+
}
37+
}
38+
39+
/**
40+
* @inheritdoc
41+
*/
42+
public function getHandle()
43+
{
44+
if ($this->handle === null) {
45+
$this->handle = fopen('php://temp', 'rb+');
46+
$this->download();
47+
}
48+
return $this->handle;
49+
}
50+
51+
/**
52+
* @inheritdoc
53+
*/
54+
public function write($data)
55+
{
56+
//toDo Content-type, filename etc...
57+
return $this;
58+
}
59+
60+
/**
61+
* @inheritdoc
62+
*/
63+
public function close()
64+
{
65+
if (is_resource($this->handle)) {
66+
fclose($this->handle);
67+
}
68+
}
69+
70+
/**
71+
* @throws \Exception
72+
*/
73+
protected function download()
74+
{
75+
if (!extension_loaded('curl')) {
76+
throw new \Exception('extension curl not found');
77+
}
78+
79+
$ch = curl_init($this->url);
80+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
81+
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
82+
83+
curl_setopt($ch, CURLOPT_FILE, $this->handle);
84+
curl_setopt($ch, CURLOPT_HEADER, 0);
85+
86+
curl_exec($ch);
87+
88+
// check cURL error
89+
$error = curl_errno($ch);
90+
$message = curl_error($ch);
91+
92+
if ($error > 0) {
93+
throw new \Exception("Curl error: #{$error}: {$message}");
94+
}
95+
96+
curl_close($ch);
97+
}
98+
99+
/**
100+
* @param array $headers
101+
*/
102+
protected function setUrl($headers)
103+
{
104+
foreach ($headers as $head) {
105+
if (strncasecmp($head, 'url', 3) === 0) {
106+
$url = preg_replace('/^url=/iu', '', $head);
107+
$this->url = trim($url, " \"'");
108+
return;
109+
}
110+
}
111+
}
112+
113+
}

tests/fixtures/external-url.eml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
MIME-Version: 1.0
2+
Content-type: message/external-body; access-type=URL;
3+
URL="https://ya.ru/"
4+
5+
Content-type: text/html
6+
Content-Transfer-Encoding: binary
7+
8+
THIS IS NOT REALLY THE BODY!

0 commit comments

Comments
 (0)