Migrate RainLoop Webmail to use Facebook Graph API instead of FQL

This commit is contained in:
RainLoop Team 2014-07-04 00:11:47 +04:00
parent 9fcaf00875
commit d0dbb26548
120 changed files with 18817 additions and 1746 deletions

View file

@ -0,0 +1,292 @@
<?php
namespace GuzzleHttp\Post;
use GuzzleHttp\Stream;
/**
* Stream that when read returns bytes for a streaming multipart/form-data body
*/
class MultipartBody implements Stream\StreamInterface
{
/** @var Stream\StreamInterface */
private $files;
private $fields;
private $size;
private $buffer;
private $bufferedHeaders = [];
private $pos = 0;
private $currentFile = 0;
private $currentField = 0;
private $sentLast;
private $boundary;
/**
* @param array $fields Associative array of field names to values where
* each value is a string.
* @param array $files Associative array of PostFileInterface objects
* @param string $boundary You can optionally provide a specific boundary
* @throws \InvalidArgumentException
*/
public function __construct(
array $fields = [],
array $files = [],
$boundary = null
) {
$this->boundary = $boundary ?: uniqid();
$this->fields = $fields;
$this->files = $files;
// Ensure each file is a PostFileInterface
foreach ($this->files as $file) {
if (!$file instanceof PostFileInterface) {
throw new \InvalidArgumentException('All POST fields must '
. 'implement PostFieldInterface');
}
}
}
public function __toString()
{
$this->seek(0);
return $this->getContents();
}
public function getContents($maxLength = -1)
{
$buffer = '';
while (!$this->eof()) {
if ($maxLength === -1) {
$read = 1048576;
} else {
$len = strlen($buffer);
if ($len == $maxLength) {
break;
}
$read = min(1048576, $maxLength - $len);
}
$buffer .= $this->read($read);
}
return $buffer;
}
/**
* Get the boundary
*
* @return string
*/
public function getBoundary()
{
return $this->boundary;
}
public function close()
{
$this->detach();
}
public function detach()
{
$this->fields = $this->files = [];
}
/**
* The stream has reached an EOF when all of the fields and files have been
* read.
* {@inheritdoc}
*/
public function eof()
{
return $this->currentField == count($this->fields) &&
$this->currentFile == count($this->files);
}
public function tell()
{
return $this->pos;
}
public function isReadable()
{
return true;
}
public function isWritable()
{
return false;
}
/**
* The steam is seekable by default, but all attached files must be
* seekable too.
* {@inheritdoc}
*/
public function isSeekable()
{
foreach ($this->files as $file) {
if (!$file->getContent()->isSeekable()) {
return false;
}
}
return true;
}
public function getSize()
{
if ($this->size === null) {
foreach ($this->files as $file) {
// We must be able to ascertain the size of each attached file
if (null === ($size = $file->getContent()->getSize())) {
return null;
}
$this->size += strlen($this->getFileHeaders($file)) + $size;
}
foreach (array_keys($this->fields) as $key) {
$this->size += strlen($this->getFieldString($key));
}
$this->size += strlen("\r\n--{$this->boundary}--");
}
return $this->size;
}
public function read($length)
{
$content = '';
if ($this->buffer && !$this->buffer->eof()) {
$content .= $this->buffer->read($length);
}
if ($delta = $length - strlen($content)) {
$content .= $this->readData($delta);
}
if ($content === '' && !$this->sentLast) {
$this->sentLast = true;
$content = "\r\n--{$this->boundary}--";
}
return $content;
}
public function seek($offset, $whence = SEEK_SET)
{
if ($offset != 0 || $whence != SEEK_SET || !$this->isSeekable()) {
return false;
}
foreach ($this->files as $file) {
if (!$file->getContent()->seek(0)) {
throw new \RuntimeException('Rewind on multipart file failed '
. 'even though it shouldn\'t have');
}
}
$this->buffer = $this->sentLast = null;
$this->pos = $this->currentField = $this->currentFile = 0;
$this->bufferedHeaders = [];
return true;
}
public function write($string)
{
return false;
}
/**
* No data is in the read buffer, so more needs to be pulled in from fields
* and files.
*
* @param int $length Amount of data to read
*
* @return string
*/
private function readData($length)
{
$result = '';
if ($this->currentField < count($this->fields)) {
$result = $this->readField($length);
}
if ($result === '' && $this->currentFile < count($this->files)) {
$result = $this->readFile($length);
}
return $result;
}
/**
* Create a new stream buffer and inject form-data
*
* @param int $length Amount of data to read from the stream buffer
*
* @return string
*/
private function readField($length)
{
$name = array_keys($this->fields)[++$this->currentField - 1];
$this->buffer = Stream\create($this->getFieldString($name));
return $this->buffer->read($length);
}
/**
* Read data from a POST file, fill the read buffer with any overflow
*
* @param int $length Amount of data to read from the file
*
* @return string
*/
private function readFile($length)
{
$current = $this->files[$this->currentFile];
// Got to the next file and recursively return the read value, or bail
// if no more data can be read.
if ($current->getContent()->eof()) {
return ++$this->currentFile == count($this->files)
? ''
: $this->readFile($length);
}
// If this is the start of a file, then send the headers to the read
// buffer.
if (!isset($this->bufferedHeaders[$this->currentFile])) {
$this->buffer = Stream\create($this->getFileHeaders($current));
$this->bufferedHeaders[$this->currentFile] = true;
}
// More data needs to be read to meet the limit, so pull from the file
$content = $this->buffer ? $this->buffer->read($length) : '';
if (($remaining = $length - strlen($content)) > 0) {
$content .= $current->getContent()->read($remaining);
}
return $content;
}
private function getFieldString($key)
{
return sprintf(
"--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n",
$this->boundary,
$key,
$this->fields[$key]
);
}
private function getFileHeaders(PostFileInterface $file)
{
$headers = '';
foreach ($file->getHeaders() as $key => $value) {
$headers .= "{$key}: {$value}\r\n";
}
return "--{$this->boundary}\r\n" . trim($headers) . "\r\n\r\n";
}
}

View file

@ -0,0 +1,282 @@
<?php
namespace GuzzleHttp\Post;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Stream;
use GuzzleHttp\Query;
/**
* Holds POST fields and files and creates a streaming body when read methods
* are called on the object.
*/
class PostBody implements PostBodyInterface
{
/** @var Stream\StreamInterface */
private $body;
/** @var callable */
private $aggregator;
private $fields = [];
/** @var PostFileInterface[] */
private $files = [];
private $forceMultipart = false;
/**
* Applies request headers to a request based on the POST state
*
* @param RequestInterface $request Request to update
*/
public function applyRequestHeaders(RequestInterface $request)
{
if ($this->files || $this->forceMultipart) {
$request->setHeader(
'Content-Type',
'multipart/form-data; boundary=' . $this->getBody()->getBoundary()
);
} elseif ($this->fields) {
$request->setHeader('Content-Type', 'application/x-www-form-urlencoded');
}
if ($size = $this->getSize()) {
$request->setHeader('Content-Length', $size);
}
}
public function forceMultipartUpload($force)
{
$this->forceMultipart = $force;
return $this;
}
public function setAggregator(callable $aggregator)
{
$this->aggregator = $aggregator;
}
public function setField($name, $value)
{
$this->fields[$name] = $value;
$this->mutate();
return $this;
}
public function replaceFields(array $fields)
{
$this->fields = $fields;
$this->mutate();
return $this;
}
public function getField($name)
{
return isset($this->fields[$name]) ? $this->fields[$name] : null;
}
public function removeField($name)
{
unset($this->fields[$name]);
$this->mutate();
return $this;
}
public function getFields($asString = false)
{
if (!$asString) {
return $this->fields;
}
return (string) (new Query($this->fields))
->setEncodingType(Query::RFC1738)
->setAggregator($this->getAggregator());
}
public function hasField($name)
{
return isset($this->fields[$name]);
}
public function getFile($name)
{
foreach ($this->files as $file) {
if ($file->getName() == $name) {
return $file;
}
}
return null;
}
public function getFiles()
{
return $this->files;
}
public function addFile(PostFileInterface $file)
{
$this->files[] = $file;
$this->mutate();
return $this;
}
public function clearFiles()
{
$this->files = [];
$this->mutate();
return $this;
}
/**
* Returns the numbers of fields + files
*
* @return int
*/
public function count()
{
return count($this->files) + count($this->fields);
}
public function __toString()
{
return (string) $this->getBody();
}
public function getContents($maxLength = -1)
{
return $this->getBody()->getContents();
}
public function close()
{
return $this->body ? $this->body->close() : true;
}
public function detach()
{
$this->body = null;
$this->fields = $this->files = [];
return $this;
}
public function eof()
{
return $this->getBody()->eof();
}
public function tell()
{
return $this->body ? $this->body->tell() : 0;
}
public function isSeekable()
{
return true;
}
public function isReadable()
{
return true;
}
public function isWritable()
{
return false;
}
public function getSize()
{
return $this->getBody()->getSize();
}
public function seek($offset, $whence = SEEK_SET)
{
return $this->getBody()->seek($offset, $whence);
}
public function read($length)
{
return $this->getBody()->read($length);
}
public function write($string)
{
return false;
}
/**
* Return a stream object that is built from the POST fields and files.
*
* If one has already been created, the previously created stream will be
* returned.
*/
private function getBody()
{
if ($this->body) {
return $this->body;
} elseif ($this->files || $this->forceMultipart) {
return $this->body = $this->createMultipart();
} elseif ($this->fields) {
return $this->body = $this->createUrlEncoded();
} else {
return $this->body = Stream\create();
}
}
/**
* Get the aggregator used to join multi-valued field parameters
*
* @return callable
*/
final protected function getAggregator()
{
if (!$this->aggregator) {
$this->aggregator = Query::phpAggregator();
}
return $this->aggregator;
}
/**
* Creates a multipart/form-data body stream
*
* @return MultipartBody
*/
private function createMultipart()
{
// Flatten the nested query string values using the correct aggregator
$query = (string) (new Query($this->fields))
->setEncodingType(false)
->setAggregator($this->getAggregator());
// Convert the flattened query string back into an array
$fields = Query::fromString($query)->toArray();
return new MultipartBody($fields, $this->files);
}
/**
* Creates an application/x-www-form-urlencoded stream body
*
* @return Stream\StreamInterface
*/
private function createUrlEncoded()
{
return Stream\create($this->getFields(true));
}
/**
* Get rid of any cached data
*/
private function mutate()
{
$this->body = null;
}
}

View file

@ -0,0 +1,129 @@
<?php
namespace GuzzleHttp\Post;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Stream\StreamInterface;
/**
* Represents a POST body that is sent as either a multipart/form-data stream
* or application/x-www-urlencoded stream.
*/
interface PostBodyInterface extends StreamInterface, \Countable
{
/**
* Apply headers to the request appropriate for the current state of the object
*
* @param RequestInterface $request Request
*/
public function applyRequestHeaders(RequestInterface $request);
/**
* Set a specific field
*
* @param string $name Name of the field to set
* @param string|array $value Value to set
*
* @return $this
*/
public function setField($name, $value);
/**
* Set the aggregation strategy that will be used to turn multi-valued
* fields into a string.
*
* The aggregation function accepts a deeply nested array of query string
* values and returns a flattened associative array of key value pairs.
*
* @param callable $aggregator
*/
public function setAggregator(callable $aggregator);
/**
* Set to true to force a multipart upload even if there are no files.
*
* @param bool $force Set to true to force multipart uploads or false to
* remove this flag.
*
* @return self
*/
public function forceMultipartUpload($force);
/**
* Replace all existing form fields with an array of fields
*
* @param array $fields Associative array of fields to set
*
* @return $this
*/
public function replaceFields(array $fields);
/**
* Get a specific field by name
*
* @param string $name Name of the POST field to retrieve
*
* @return string|null
*/
public function getField($name);
/**
* Remove a field by name
*
* @param string $name Name of the field to remove
*
* @return $this
*/
public function removeField($name);
/**
* Returns an associative array of names to values or a query string.
*
* @param bool $asString Set to true to retrieve the fields as a query
* string.
*
* @return array|string
*/
public function getFields($asString = false);
/**
* Returns true if a field is set
*
* @param string $name Name of the field to set
*
* @return bool
*/
public function hasField($name);
/**
* Get all of the files
*
* @return array Returns an array of PostFileInterface objects
*/
public function getFiles();
/**
* Get a POST file by name.
*
* @param string $name Name of the POST file to retrieve
*
* @return PostFileInterface|null
*/
public function getFile($name);
/**
* Add a file to the POST
*
* @param PostFileInterface $file File to add
*
* @return $this
*/
public function addFile(PostFileInterface $file);
/**
* Remove all files from the collection
*
* @return $this
*/
public function clearFiles();
}

View file

@ -0,0 +1,138 @@
<?php
namespace GuzzleHttp\Post;
use GuzzleHttp\Mimetypes;
use GuzzleHttp\Stream\MetadataStreamInterface;
use GuzzleHttp\Stream\StreamInterface;
/**
* Post file upload
*/
class PostFile implements PostFileInterface
{
private $name;
private $filename;
private $content;
private $headers = [];
/**
* @param null $name Name of the form field
* @param mixed $content Data to send
* @param null $filename Filename content-disposition attribute
* @param array $headers Array of headers to set on the file
* (can override any default headers)
* @throws \RuntimeException when filename is not passed or can't be determined
*/
public function __construct(
$name,
$content,
$filename = null,
array $headers = []
) {
$this->headers = $headers;
$this->name = $name;
$this->prepareContent($content);
$this->prepareFilename($filename);
$this->prepareDefaultHeaders();
}
public function getName()
{
return $this->name;
}
public function getFilename()
{
return $this->filename;
}
public function getContent()
{
return $this->content;
}
public function getHeaders()
{
return $this->headers;
}
/**
* Prepares the contents of a POST file.
*
* @param mixed $content Content of the POST file
*/
private function prepareContent($content)
{
$this->content = $content;
if (!($this->content instanceof StreamInterface)) {
$this->content = \GuzzleHttp\Stream\create($this->content);
} elseif ($this->content instanceof MultipartBody) {
if (!$this->hasHeader('Content-Disposition')) {
$disposition = 'form-data; name="' . $this->name .'"';
$this->headers['Content-Disposition'] = $disposition;
}
if (!$this->hasHeader('Content-Type')) {
$this->headers['Content-Type'] = sprintf(
"multipart/form-data; boundary=%s",
$this->content->getBoundary()
);
}
}
}
/**
* Applies a file name to the POST file based on various checks.
*
* @param string|null $filename Filename to apply (or null to guess)
*/
private function prepareFilename($filename)
{
$this->filename = $filename;
if (!$this->filename &&
$this->content instanceof MetadataStreamInterface
) {
$this->filename = $this->content->getMetadata('uri');
}
if (!$this->filename || substr($this->filename, 0, 6) === 'php://') {
$this->filename = $this->name;
}
}
/**
* Applies default Content-Disposition and Content-Type headers if needed.
*/
private function prepareDefaultHeaders()
{
// Set a default content-disposition header if one was no provided
if (!$this->hasHeader('Content-Disposition')) {
$this->headers['Content-Disposition'] = sprintf(
'form-data; filename="%s"; name="%s"',
basename($this->filename),
$this->name
);
}
// Set a default Content-Type if one was not supplied
if (!$this->hasHeader('Content-Type')) {
$this->headers['Content-Type'] = Mimetypes::getInstance()
->fromFilename($this->filename) ?: 'text/plain';
}
}
/**
* Check if a specific header exists on the POST file by name.
*
* @param string $name Case-insensitive header to check
*
* @return bool
*/
private function hasHeader($name)
{
return isset(array_change_key_case($this->headers)[strtolower($name)]);
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace GuzzleHttp\Post;
use GuzzleHttp\Stream\StreamInterface;
/**
* Post file upload interface
*/
interface PostFileInterface
{
/**
* Get the name of the form field
*
* @return string
*/
public function getName();
/**
* Get the full path to the file
*
* @return string
*/
public function getFilename();
/**
* Get the content
*
* @return StreamInterface
*/
public function getContent();
/**
* Gets all POST file headers.
*
* The keys represent the header name as it will be sent over the wire, and
* each value is a string.
*
* @return array Returns an associative array of the file's headers.
*/
public function getHeaders();
}