Merge changes from develop
diff --git a/.travis.yml b/.travis.yml
index fa9d5e5..27fe3c6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,7 @@
- 5.3
- 5.4
- 5.5
+ - hhvm
env:
- DB=mysql
@@ -22,6 +23,10 @@
script: phpunit --coverage-text --configuration tests/travis/$DB.phpunit.xml
+matrix:
+ allow_failures:
+ - php: hhvm
+
branches:
only:
- develop
diff --git a/application/config/config.php b/application/config/config.php
index c468393..ae748de 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -141,15 +141,18 @@
| Allowed URL Characters
|--------------------------------------------------------------------------
|
-| This lets you specify with a regular expression which characters are permitted
-| within your URLs. When someone tries to submit a URL with disallowed
-| characters they will get a warning message.
+| This lets you specify which characters are permitted within your URLs.
+| When someone tries to submit a URL with disallowed characters they will
+| get a warning message.
|
| As a security measure you are STRONGLY encouraged to restrict URLs to
| as few characters as possible. By default only these are allowed: a-z 0-9~%.:_-
|
| Leave blank to allow all characters -- but only if you are insane.
|
+| The configured value is actually a regular expression character group
+| and it will be executed as: ! preg_match('/^[<permitted_uri_chars>]+$/i
+|
| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
|
*/
diff --git a/application/config/user_agents.php b/application/config/user_agents.php
index 2af70bf..819e42b 100644
--- a/application/config/user_agents.php
+++ b/application/config/user_agents.php
@@ -90,6 +90,7 @@
'Opera' => 'Opera',
'MSIE' => 'Internet Explorer',
'Internet Explorer' => 'Internet Explorer',
+ 'Trident.* rv' => 'Internet Explorer',
'Shiira' => 'Shiira',
'Firefox' => 'Firefox',
'Chimera' => 'Chimera',
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php
index 45c3485..cc12f14 100644
--- a/system/core/CodeIgniter.php
+++ b/system/core/CodeIgniter.php
@@ -163,12 +163,6 @@
*/
$RTR =& load_class('Router', 'core');
- // Set any routing overrides that may exist in the main index file
- if (isset($routing))
- {
- $RTR->_set_overrides($routing);
- }
-
/*
* ------------------------------------------------------
* Instantiate the output class
diff --git a/system/core/Log.php b/system/core/Log.php
index b2327b8..63fef20 100644
--- a/system/core/Log.php
+++ b/system/core/Log.php
@@ -175,10 +175,18 @@
return FALSE;
}
- $message .= $level.' '.($level === 'INFO' ? ' -' : '-').' '.date($this->_date_fmt).' --> '.$msg."\n";
+ $message .= $level.' - '.date($this->_date_fmt).' --> '.$msg."\n";
flock($fp, LOCK_EX);
- fwrite($fp, $message);
+
+ for ($written = 0, $length = strlen($message); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($fp, substr($message, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
flock($fp, LOCK_UN);
fclose($fp);
@@ -187,7 +195,7 @@
@chmod($filepath, FILE_WRITE_MODE);
}
- return TRUE;
+ return is_int($result);
}
}
diff --git a/system/core/Output.php b/system/core/Output.php
index cdb92b3..2ad8e90 100644
--- a/system/core/Output.php
+++ b/system/core/Output.php
@@ -591,7 +591,16 @@
'headers' => $this->headers
));
- fwrite($fp, $cache_info.'ENDCI--->'.$output);
+ $output = $cache_info.'ENDCI--->'.$output;
+
+ for ($written = 0, $length = strlen($output); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($fp, substr($output, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
flock($fp, LOCK_UN);
}
else
@@ -601,12 +610,20 @@
}
fclose($fp);
- @chmod($cache_path, FILE_WRITE_MODE);
- log_message('debug', 'Cache file written: '.$cache_path);
+ if (is_int($result))
+ {
+ @chmod($cache_path, FILE_WRITE_MODE);
+ log_message('debug', 'Cache file written: '.$cache_path);
- // Send HTTP cache-control headers to browser to match file cache settings.
- $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
+ // Send HTTP cache-control headers to browser to match file cache settings.
+ $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
+ }
+ else
+ {
+ @unlink($cache_path);
+ log_message('error', 'Unable to write the complete cache content at: '.$cache_path);
+ }
}
// --------------------------------------------------------------------
diff --git a/system/core/Router.php b/system/core/Router.php
index d467d60..71530ff 100644
--- a/system/core/Router.php
+++ b/system/core/Router.php
@@ -102,9 +102,32 @@
*/
public function __construct()
{
+ global $routing;
+
$this->config =& load_class('Config', 'core');
$this->uri =& load_class('URI', 'core');
$this->_set_routing();
+
+ // Set any routing overrides that may exist in the main index file
+ if (isset($routing) && is_array($routing))
+ {
+ if (isset($routing['directory']))
+ {
+ $this->set_directory($routing['directory']);
+ }
+
+ if ( ! empty($routing['controller']))
+ {
+ $this->set_class($routing['controller']);
+ }
+
+ if (isset($routing['function']))
+ {
+ $routing['function'] = empty($routing['function']) ? 'index' : $routing['function'];
+ $this->set_method($routing['function']);
+ }
+ }
+
log_message('debug', 'Router Class Initialized');
}
@@ -131,16 +154,16 @@
{
if (isset($_GET[$this->config->item('directory_trigger')]) && is_string($_GET[$this->config->item('directory_trigger')]))
{
- $this->set_directory(trim($this->uri->_filter_uri($_GET[$this->config->item('directory_trigger')])));
+ $this->set_directory(trim($this->uri->filter_uri($_GET[$this->config->item('directory_trigger')])));
$segments[] = $this->directory;
}
- $this->set_class(trim($this->uri->_filter_uri($_GET[$this->config->item('controller_trigger')])));
+ $this->set_class(trim($this->uri->filter_uri($_GET[$this->config->item('controller_trigger')])));
$segments[] = $this->class;
if ( ! empty($_GET[$this->config->item('function_trigger')]) && is_string($_GET[$this->config->item('function_trigger')]))
{
- $this->set_method(trim($this->uri->_filter_uri($_GET[$this->config->item('function_trigger')])));
+ $this->set_method(trim($this->uri->filter_uri($_GET[$this->config->item('function_trigger')])));
$segments[] = $this->method;
}
}
@@ -519,38 +542,6 @@
return $this->directory;
}
- // --------------------------------------------------------------------
-
- /**
- * Set controller overrides
- *
- * @param array $routing Route overrides
- * @return void
- */
- public function _set_overrides($routing)
- {
- if ( ! is_array($routing))
- {
- return;
- }
-
- if (isset($routing['directory']))
- {
- $this->set_directory($routing['directory']);
- }
-
- if ( ! empty($routing['controller']))
- {
- $this->set_class($routing['controller']);
- }
-
- if (isset($routing['function']))
- {
- $routing['function'] = empty($routing['function']) ? 'index' : $routing['function'];
- $this->set_method($routing['function']);
- }
- }
-
}
/* End of file Router.php */
diff --git a/system/core/URI.php b/system/core/URI.php
index 5e4c80a..3d6d202 100644
--- a/system/core/URI.php
+++ b/system/core/URI.php
@@ -70,6 +70,15 @@
public $rsegments = array();
/**
+ * Permitted URI chars
+ *
+ * PCRE character group allowed in URI segments
+ *
+ * @var string
+ */
+ protected $_permitted_uri_chars;
+
+ /**
* Class constructor
*
* Simply globalizes the $RTR object. The front
@@ -81,6 +90,12 @@
public function __construct()
{
$this->config =& load_class('Config', 'core');
+
+ if ($this->config->item('enable_query_strings') !== TRUE OR is_cli())
+ {
+ $this->_permitted_uri_chars = $this->config->item('permitted_uri_chars');
+ }
+
log_message('debug', 'URI Class Initialized');
}
@@ -303,23 +318,19 @@
* @param string $str
* @return string
*/
- public function _filter_uri($str)
+ public function filter_uri($str)
{
- if ($str !== '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') === FALSE)
+ if ( ! empty($str) && ! empty($this->_permitted_uri_chars) && ! preg_match('/^['.$this->_permitted_uri_chars.']+$/i', $str))
{
- // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
- // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
- if ( ! preg_match('|^['.str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-')).']+$|i', $str))
- {
- show_error('The URI you submitted has disallowed characters.', 400);
- }
+ show_error('The URI you submitted has disallowed characters.', 400);
}
// Convert programatic characters to entities and return
return str_replace(
- array('$', '(', ')', '%28', '%29'), // Bad
- array('$', '(', ')', '(', ')'), // Good
- $str);
+ array('$', '(', ')', '%28', '%29'), // Bad
+ array('$', '(', ')', '(', ')'), // Good
+ $str
+ );
}
// --------------------------------------------------------------------
@@ -365,7 +376,7 @@
foreach (explode('/', preg_replace('|/*(.+?)/*$|', '\\1', $this->uri_string)) as $val)
{
// Filter segments for security
- $val = trim($this->_filter_uri($val));
+ $val = trim($this->filter_uri($val));
if ($val !== '')
{
diff --git a/system/helpers/file_helper.php b/system/helpers/file_helper.php
index 4b45a62..0587740 100644
--- a/system/helpers/file_helper.php
+++ b/system/helpers/file_helper.php
@@ -79,11 +79,19 @@
}
flock($fp, LOCK_EX);
- fwrite($fp, $data);
+
+ for ($written = 0, $length = strlen($data); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($fp, substr($data, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
flock($fp, LOCK_UN);
fclose($fp);
- return TRUE;
+ return is_int($result);
}
}
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index efdbfd7..f4efff8 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -710,18 +710,42 @@
/**
* Assign file attachments
*
- * @param string $filename
+ * @param string $file Can be local path, URL or buffered content
* @param string $disposition = 'attachment'
* @param string $newname = NULL
* @param string $mime = ''
* @return CI_Email
*/
- public function attach($filename, $disposition = '', $newname = NULL, $mime = '')
+ public function attach($file, $disposition = '', $newname = NULL, $mime = '')
{
+ if ($mime === '')
+ {
+ if (strpos($file, '://') === FALSE && ! file_exists($file))
+ {
+ $this->_set_error_message('lang:email_attachment_missing', $file);
+ return FALSE;
+ }
+
+ if ( ! $fp = @fopen($file, FOPEN_READ))
+ {
+ $this->_set_error_message('lang:email_attachment_unreadable', $file);
+ return FALSE;
+ }
+
+ $file_content = stream_get_contents($fp);
+ $mime = $this->_mime_types(pathinfo($file, PATHINFO_EXTENSION));
+ fclose($fp);
+ }
+ else
+ {
+ $file_content =& $file; // buffered file
+ }
+
$this->_attachments[] = array(
- 'name' => array($filename, $newname),
+ 'name' => array($file, $newname),
'disposition' => empty($disposition) ? 'attachment' : $disposition, // Can also be 'inline' Not sure if it matters
- 'type' => $mime
+ 'type' => $mime,
+ 'content' => chunk_split(base64_encode($file_content))
);
return $this;
@@ -730,6 +754,35 @@
// --------------------------------------------------------------------
/**
+ * Set and return attachment Content-ID
+ *
+ * Useful for attached inline pictures
+ *
+ * @param string $filename
+ * @return string
+ */
+ public function attachment_cid($filename)
+ {
+ if ($this->multipart !== 'related')
+ {
+ $this->multipart = 'related'; // Thunderbird need this for inline images
+ }
+
+ for ($i = 0, $c = count($this->_attachments); $i < $c; $i++)
+ {
+ if ($this->_attachments[$i]['name'][0] === $filename)
+ {
+ $this->_attachments[$i]['cid'] = uniqid(basename($this->_attachments[$i]['name'][0]).'@');
+ return $this->_attachments[$i]['cid'];
+ }
+ }
+
+ return FALSE;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Add a Header Item
*
* @param string
@@ -1361,47 +1414,22 @@
$filename = $this->_attachments[$i]['name'][0];
$basename = ($this->_attachments[$i]['name'][1] === NULL)
? basename($filename) : $this->_attachments[$i]['name'][1];
- $ctype = $this->_attachments[$i]['type'];
- $file_content = '';
-
- if ($ctype === '')
- {
- if ( ! file_exists($filename))
- {
- $this->_set_error_message('lang:email_attachment_missing', $filename);
- return FALSE;
- }
-
- $file = filesize($filename) +1;
-
- if ( ! $fp = fopen($filename, FOPEN_READ))
- {
- $this->_set_error_message('lang:email_attachment_unreadable', $filename);
- return FALSE;
- }
-
- $ctype = $this->_mime_types(pathinfo($filename, PATHINFO_EXTENSION));
- $file_content = fread($fp, $file);
- fclose($fp);
- }
- else
- {
- $file_content =& $this->_attachments[$i]['name'][0];
- }
$attachment[$z++] = '--'.$this->_atc_boundary.$this->newline
- .'Content-type: '.$ctype.'; '
+ .'Content-type: '.$this->_attachments[$i]['type'].'; '
.'name="'.$basename.'"'.$this->newline
.'Content-Disposition: '.$this->_attachments[$i]['disposition'].';'.$this->newline
- .'Content-Transfer-Encoding: base64'.$this->newline;
+ .'Content-Transfer-Encoding: base64'.$this->newline
+ .(empty($this->_attachments[$i]['cid']) ? '' : 'Content-ID: <'.$this->_attachments[$i]['cid'].'>'.$this->newline);
- $attachment[$z++] = chunk_split(base64_encode($file_content));
+ $attachment[$z++] = $this->_attachments[$i]['content'];
}
$body .= implode($this->newline, $attachment).$this->newline.'--'.$this->_atc_boundary.'--';
$this->_finalbody = ($this->_get_protocol() === 'mail')
? $body
: $hdr.$this->newline.$this->newline.$body;
+
return TRUE;
}
@@ -2069,7 +2097,16 @@
*/
protected function _send_data($data)
{
- if ( ! fwrite($this->_smtp_connect, $data.$this->newline))
+ $data .= $this->newline;
+ for ($written = 0, $length = strlen($data); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($this->_smtp_connect, substr($data, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
+ if ($result === FALSE)
{
$this->_set_error_message('lang:email_smtp_data_failure', $data);
return FALSE;
diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php
index 2fd1259..ab907e7 100644
--- a/system/libraries/Xmlrpc.php
+++ b/system/libraries/Xmlrpc.php
@@ -724,7 +724,15 @@
.'Content-Length: '.strlen($msg->payload).$r.$r
.$msg->payload;
- if ( ! fwrite($fp, $op, strlen($op)))
+ for ($written = 0, $length = strlen($op); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($fp, substr($op, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
+ if ($result === FALSE)
{
error_log($this->xmlrpcstr['http_error']);
return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']);
diff --git a/system/libraries/Zip.php b/system/libraries/Zip.php
index 250ee02..b10b0bb 100644
--- a/system/libraries/Zip.php
+++ b/system/libraries/Zip.php
@@ -403,11 +403,19 @@
}
flock($fp, LOCK_EX);
- fwrite($fp, $this->get_zip());
+
+ for ($written = 0, $data = $this->get_zip(), $length = strlen($data); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($fp, substr($data, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
flock($fp, LOCK_UN);
fclose($fp);
- return TRUE;
+ return is_int($result);
}
// --------------------------------------------------------------------
diff --git a/tests/codeigniter/core/URI_test.php b/tests/codeigniter/core/URI_test.php
index 7fa0e62..99d79bb 100644
--- a/tests/codeigniter/core/URI_test.php
+++ b/tests/codeigniter/core/URI_test.php
@@ -112,11 +112,10 @@
public function test_filter_uri()
{
- $this->uri->config->set_item('enable_query_strings', FALSE);
- $this->uri->config->set_item('permitted_uri_chars', 'a-z 0-9~%.:_\-');
+ $this->uri->_set_permitted_uri_chars('a-z 0-9~%.:_\-');
$str_in = 'abc01239~%.:_-';
- $str = $this->uri->_filter_uri($str_in);
+ $str = $this->uri->filter_uri($str_in);
$this->assertEquals($str, $str_in);
}
@@ -126,11 +125,9 @@
public function test_filter_uri_escaping()
{
// ensure escaping even if dodgey characters are permitted
+ $this->uri->_set_permitted_uri_chars('a-z 0-9~%.:_\-()$');
- $this->uri->config->set_item('enable_query_strings', FALSE);
- $this->uri->config->set_item('permitted_uri_chars', 'a-z 0-9~%.:_\-()$');
-
- $str = $this->uri->_filter_uri('$destroy_app(foo)');
+ $str = $this->uri->filter_uri('$destroy_app(foo)');
$this->assertEquals($str, '$destroy_app(foo)');
}
@@ -142,8 +139,8 @@
$this->setExpectedException('RuntimeException');
$this->uri->config->set_item('enable_query_strings', FALSE);
- $this->uri->config->set_item('permitted_uri_chars', 'a-z 0-9~%.:_\-');
- $this->uri->_filter_uri('$this()');
+ $this->uri->_set_permitted_uri_chars('a-z 0-9~%.:_\-');
+ $this->uri->filter_uri('$this()');
}
// --------------------------------------------------------------------
diff --git a/tests/mocks/autoloader.php b/tests/mocks/autoloader.php
index 3d216da..cc0a2e2 100644
--- a/tests/mocks/autoloader.php
+++ b/tests/mocks/autoloader.php
@@ -89,21 +89,7 @@
if ( ! file_exists($file))
{
- $trace = debug_backtrace();
-
- if ($trace[2]['function'] === 'class_exists' OR $trace[2]['function'] === 'file_exists')
- {
- // If the autoload call came from `class_exists` or `file_exists`,
- // we skipped and return FALSE
- return FALSE;
- }
- elseif (($autoloader = spl_autoload_functions()) && end($autoloader) !== __FUNCTION__)
- {
- // If there was other custom autoloader, passed away
- return FALSE;
- }
-
- throw new InvalidArgumentException("Unable to load {$class}.");
+ return FALSE;
}
include_once($file);
diff --git a/tests/mocks/core/uri.php b/tests/mocks/core/uri.php
index 1107858..96ec5af 100644
--- a/tests/mocks/core/uri.php
+++ b/tests/mocks/core/uri.php
@@ -10,12 +10,23 @@
// set predictable config values
$test->ci_set_config(array(
'index_page' => 'index.php',
- 'base_url' => 'http://example.com/',
- 'subclass_prefix' => 'MY_'
+ 'base_url' => 'http://example.com/',
+ 'subclass_prefix' => 'MY_',
+ 'enable_query_strings' => FALSE,
+ 'permitted_uri_chars' => 'a-z 0-9~%.:_\-'
));
$this->config = new $cls;
+ if ($this->config->item('enable_query_strings') !== TRUE OR is_cli())
+ {
+ $this->_permitted_uri_chars = $this->config->item('permitted_uri_chars');
+ }
+ }
+
+ public function _set_permitted_uri_chars($value)
+ {
+ $this->_permitted_uri_chars = $value;
}
}
\ No newline at end of file
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 0e656a1..5a779cc 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -332,10 +332,12 @@
- :doc:`Email Library <libraries/email>` changes include:
- - Added custom filename to ``Email::attach()`` as ``$this->email->attach($filename, $disposition, $newname)``.
- - Added possibility to send attachment as buffer string in ``Email::attach()`` as ``$this->email->attach($buffer, $disposition, $newname, $mime)``.
+ - Added a custom filename parameter to ``attach()`` as ``$this->email->attach($filename, $disposition, $newname)``.
+ - Added possibility to send attachment as buffer string in ``attach()`` as ``$this->email->attach($buffer, $disposition, $newname, $mime)``.
+ - Added possibility to attach remote files by passing a URL.
+ - Added method ``attachment_cid()`` to enable embedding inline attachments into HTML.
- Added dsn (delivery status notification) option.
- - Renamed method _set_header() to set_header() and made it public to enable adding custom headers in the :doc:`Email Library <libraries/email>`.
+ - Renamed method ``_set_header()`` to ``set_header()`` and made it public to enable adding custom headers.
- Successfully sent emails will automatically clear the parameters.
- Added a *return_path* parameter to the ``from()`` method.
- Removed the second parameter (character limit) from internal method ``_prep_quoted_printable()`` as it is never used.
@@ -344,7 +346,7 @@
- Removed unused protected method ``_get_ip()`` (:doc:`Input Library <libraries/input>`'s ``ip_address()`` should be used anyway).
- Internal method ``_prep_q_encoding()`` now utilizes PHP's *mbstring* and *iconv* extensions (when available) and no longer has a second (``$from``) argument.
- Added an optional parameter to ``print_debugger()`` to allow specifying which parts of the message should be printed ('headers', 'subject', 'body').
- - Added SMTP keepalive option to avoid opening the connection for each ``Email::send()``. Accessible as ``$smtp_keepalive``.
+ - Added SMTP keepalive option to avoid opening the connection for each ``send()`` call. Accessible as ``$smtp_keepalive``.
- Public method ``set_header()`` now filters the input by removing all "\\r" and "\\n" characters.
- :doc:`Pagination Library <libraries/pagination>` changes include:
@@ -389,6 +391,7 @@
- :doc:`URI Library <libraries/uri>` changes include:
+ - Renamed method ``_filter_uri()`` to ``filter_uri()`` and removed the ``preg_quote()`` call from it.
- Changed private methods to protected so that MY_URI can override them.
- Renamed internal method ``_parse_cli_args()`` to ``_parse_argv()``.
- Renamed internal method ``_detect_uri()`` to ``_parse_request_uri()``.
@@ -461,6 +464,7 @@
- Added possibility to route requests using callbacks.
- Added a new reserved route (*translate_uri_dashes*) to allow usage of dashes in the controller and method URI segments.
- Deprecated methods ``fetch_directory()``, ``fetch_class()`` and ``fetch_method()`` in favor of their respective public properties.
+ - Removed method ``_set_overrides()`` and moved its logic to the class constructor.
- :doc:`Language Library <libraries/language>` changes include:
@@ -666,6 +670,7 @@
- Fixed a bug (#346) - with ``$config['global_xss_filtering']`` turned on, the ``$_GET``, ``$_POST``, ``$_COOKIE`` and ``$_SERVER`` superglobals were overwritten during initialization time, resulting in XSS filtering being either performed twice or there was no possible way to get the original data, even though options for this do exist.
- Fixed an edge case (#555) - incorrect browser version was reported for Opera 10+ due to a non-standard user-agent string.
- Fixed a bug (#133) - :doc:`Text Helper <helpers/text_helper>` :func:`ascii_to_entities()` stripped the last character if it happens to be in the extended ASCII group.
+- Fixed a bug (#2822) - ``fwrite()`` was used incorrectly throughout the whole framework, allowing incomplete writes when writing to a network stream and possibly a few other edge cases.
Version 2.1.4
=============
diff --git a/user_guide_src/source/libraries/email.rst b/user_guide_src/source/libraries/email.rst
index 39629ec..86f440a 100644
--- a/user_guide_src/source/libraries/email.rst
+++ b/user_guide_src/source/libraries/email.rst
@@ -247,8 +247,8 @@
----------------------
Enables you to send an attachment. Put the file path/name in the first
-parameter. Note: Use a file path, not a URL. For multiple attachments
-use the method multiple times. For example::
+parameter. For multiple attachments use the method multiple times.
+For example::
$this->email->attach('/path/to/photo1.jpg');
$this->email->attach('/path/to/photo2.jpg');
@@ -259,6 +259,10 @@
$this->email->attach('image.jpg', 'inline');
+You can use URL::
+
+ $this->email->attach('http://example.com/filename.pdf');
+
If you'd like to use a custom file name, you can use the third paramater::
$this->email->attach('filename.pdf', 'attachment', 'report.pdf');
@@ -269,6 +273,26 @@
$this->email->attach($buffer, 'attachment', 'report.pdf', 'application/pdf');
+$this->email->attachment_cid()
+------------------------------
+
+Sets and returns an attachment's Content-ID, which enables your to embed an inline
+(picture) attachment into HTML. First parameter must be attached file.
+
+::
+
+ $filename = '/img/photo1.jpg';
+ $this->email->attach($filename);
+ foreach ($list as $address)
+ {
+ $this->email->to($address);
+ $cid = $this->email->attach_cid($filename);
+ $this->email->message('<img src='cid:". $cid ."' alt="photo1" />');
+ $this->email->send();
+ }
+
+CID for each Email have to be create again to be unique.
+
$this->email->print_debugger()
------------------------------