Fix #2822: Incorrect usage of fwrite()

We only used to check (and not always) if the return value of fwrite() is boolean FALSE,
while it is possible that the otherwise returned bytecount is less than the length of
data that we're trying to write. This allowed incomplete writes over network streams
and possibly a few other edge cases.
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index 9487ad4..f4efff8 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -2097,7 +2097,16 @@
 	 */
 	protected function _send_data($data)
 	{
-		if (fwrite($this->_smtp_connect, $data.$this->newline) === FALSE)
+		$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 1f93e69..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)) === FALSE)
+		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);
 	}
 
 	// --------------------------------------------------------------------