<?php

class PicturesController extends GalleryAppController
{
    public $components = array('Gallery.Util');
    public $uses = array('Gallery.Album', 'Gallery.Picture');

    public function index()
    {
        $this->Album->id = $this->params['pass'][0];

        return new CakeResponse(
            array(
                'type' => 'application/json',
                'status' => 200,
                'body' => json_encode($this->Album->read(null), true)
            )
        );

        $this->render(false, false);
    }

    public function upload()
    {
        $album_id = $_POST['album_id'];

        # Resize attributes configured in bootstrap.php
        $resize_attrs = $this->Picture->getResizeToSize();

        if ($_FILES) {
            $file = $_FILES['file'];

            try {
                # Check if the file have any errors
                $this->Util->checkFileErrors($file);

                # Get file extention
                $ext = pathinfo($file['name'], PATHINFO_EXTENSION);

                # Validate if the file extention is allowed
                $this->Util->validateExtensions($ext);

                # Generate a random filename
                $filename = $this->Util->getToken();

                $full_name = $filename . "." . $ext;

                # Image Path
                $path = $this->Picture->generateFilePath($album_id, $full_name);

                $main_id = $this->Picture->uploadFile(
                    $path,
                    $album_id,
                    $file['name'],
                    $file['tmp_name'],
                    $resize_attrs['width'],
                    $resize_attrs['height'],
                    $resize_attrs['action'],
                    true
                );

                # Create extra pictures from the original one
                $this->Picture->createExtraImages(
                    Configure::read('GalleryOptions.Pictures.styles'),
                    $file['name'],
                    $file['tmp_name'],
                    $album_id,
                    $main_id,
                    $filename
                );

                $this->Picture->id = $main_id;

                return new CakeResponse(
                    array(
                        'type' => 'application/json',
                        'status' => 200,
                        'body' => json_encode(array(
                            'picture' => $this->Picture->read(null)
                        ), true)
                    )
                );

            } catch (ForbiddenException $e) {
                $response = $e->getMessage();
                return new CakeResponse(
                    array(
                        'status' => 401,
                        'body' => $response
                    )
                );
            }
        }

        $this->render(false, false);
    }

    /**
     * Delete an image and all its versions from database
     * @param $id
     */
    public function delete($id)
    {
        # Delete the picture and all its versions
        $this->Picture->delete($id);

        $this->render(false, false);
    }

    /**
     * Sort pictures from an album
     */
    public function sort()
    {
        if ($this->request->is('post')) {
            $order = explode(",", $_POST['order']);
            $i = 1;
            foreach ($order as $photo) {
                $this->Picture->read(null, $photo);
                $this->Picture->set('order', $i);
                $this->Picture->save();
                $i++;
            }
        }

        $this->render(false, false);
    }

    public function caption()
    {

        if ($this->request->is('post')) {

            $picture_id = $this->request->data['pk'];
            $caption = $this->request->data['value'];

            $this->Picture->id = $picture_id;

            $this->Picture->saveField('caption', $caption);
        }

        $this->render(false, false);
    }

}

?>


<?php

/*
 * This file is part of the PhpMob package.
 *
 * (c) Ishmael Doss <nukboon@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace PhpMob\SettingsBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
use Symfony\Component\Form\Extension\Core\Type\TextType;

/**
 * @author Ishmael Doss <nukboon@gmail.com>
 */
class Configuration implements ConfigurationInterface
{
    /**
     * {@inheritdoc}
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('phpmob_settings');

        $rootNode
            ->children()
                ->arrayNode('cache')
                    ->addDefaultsIfNotSet()
                    ->children()
                        ->scalarNode('service')->defaultNull()->end()
                        ->integerNode('lifetime')->defaultValue(86400)->end()
                    ->end()
                ->end()
                ->arrayNode('schemas')
                    ->useAttributeAsKey('name')
                    // section
                    ->prototype('array')
                        ->children()
                            ->scalarNode('enabled')->defaultTrue()->end()
                            ->scalarNode('owner_aware')->defaultFalse()->end()
                            ->scalarNode('label')->isRequired()->end()
                            ->scalarNode('description')->defaultNull()->end()
                            ->arrayNode('settings')
                                // setting key
                                ->requiresAtLeastOneElement()
                                ->useAttributeAsKey('name')
                                ->prototype('array')
                                    ->isRequired()
                                    ->addDefaultsIfNotSet()
                                    ->children()
                                        ->scalarNode('enabled')->defaultTrue()->end()
                                        ->scalarNode('type')->defaultValue('default')->end()
                                        ->variableNode('value')->defaultNull()->end()
                                        ->scalarNode('label')->isRequired()->end()
                                        ->scalarNode('description')->defaultNull()->end()
                                        ->arrayNode('blueprint')
                                            ->addDefaultsIfNotSet()
                                            ->children()
                                                ->scalarNode('type')->defaultValue(TextType::class)->end()
                                                ->variableNode('options')
                                                    ->info('The options given to form builder.')
                                                    ->defaultValue([])
                                                    ->validate()
                                                        ->always(function ($v) {
                                                            if (!is_array($v)) {
                                                                throw new InvalidTypeException();
                                                            }

                                                            return $v;
                                                        })
                                                    ->end()
                                                ->end()
                                                ->arrayNode('constraints')
                                                    ->info('Use Symfony\Component\Validator\Constraints')
                                                    ->useAttributeAsKey('name')
                                                    ->prototype('variable')
                                                        ->defaultValue([])
                                                        ->validate()
                                                            ->always(function ($v) {
                                                                if (!is_array($v)) {
                                                                    return [];
                                                                }

                                                                return $v;
                                                            })
                                                        ->end()
                                                    ->end()
                                                ->end()
                                            ->end()
                                        ->end()
                                    ->end()
                                ->end()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end();

        return $treeBuilder;
    }
}


<?php
namespace Incremental\YouTube;

/**
 * An easy-to-use wrapper for the non-OAuth YouTube API (v3) endpoints.
 *
 * @package Incremental\YouTube
 */
class YouTube
{
    /**
     * @access  protected
     * @var     string
     */
    protected $apiKey;

    /**
     * @access  private
     * @var     string
     */
    private $baseUri = 'https://www.googleapis.com/youtube/v3';

    /**
     * @access  private
     * @var     string
     */
    private $apiUri;

    /**
     * @access  private
     * @var     array
     */
    private $filters;

    /**
     * @access  public
     * @param   string  $apiKey An API key for communication with the API.
     */
    public function __construct($apiKey)
    {
        $this->apiKey = $apiKey;
    }

    /**
     * Returns a list of channel activity events that match the given channelId.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/activities/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listActivities($parameters)
    {
        $this->apiUri   = $this->baseUri . '/activities';
        $this->filters  = ['channelId'];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a collection of zero or more channel resources that match the request criteria.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/channels/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listChannels($parameters)
    {
        $this->apiUri   = $this->baseUri . '/channels';
        $this->filters  = [
            'categoryId',
            'forUsername',
            'id',
        ];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a list of channelSection resources that match the request criteria.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/channelSections/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listChannelSections($parameters)
    {
        $this->apiUri   = $this->baseUri . '/channelSections';
        $this->filters  = [
            'channelId',
            'id',
        ];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a list of comments that match the request criteria.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/comments/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listComments($parameters)
    {
        $this->apiUri   = $this->baseUri . '/comments';
        $this->filters  = [
            'id',
            'parentId',
        ];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a list of comment threads that match the request criteria.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/commentThreads/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listCommentThreads($parameters)
    {
        $this->apiUri   = $this->baseUri . '/commentThreads';
        $this->filters  = [
            'allThreadsRelatedToChannelId',
            'channelId',
            'id',
            'videoId',
        ];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a list of categories that can be associated with YouTube channels.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/guideCategories/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listGuideCategories($parameters)
    {
        $this->apiUri   = $this->baseUri . '/guideCategories';
        $this->filters  = [
            'id',
            'regionCode',
        ];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a list of application languages that the YouTube website supports.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/i18nLanguages/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listI18nLanguages($parameters)
    {
        $this->apiUri   = $this->baseUri . '/i18nLanguages';
        $this->filters  = [];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a list of content regions that the YouTube website supports.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/i18nRegions/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listI18nRegions($parameters)
    {
        $this->apiUri   = $this->baseUri . '/i18nRegions';
        $this->filters  = [];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a collection of playlist items that match the API request parameters.
     * You can retrieve all of the playlist items in a specified playlist or retrieve
     * one or more playlist items by their unique IDs.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/playlistItems/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listPlaylistItems($parameters)
    {
        $this->apiUri   = $this->baseUri . '/playlistItems';
        $this->filters  = [
            'id',
            'playlistId',
        ];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a collection of playlists that match the API request parameters.
     * For example, you can retrieve one or more playlists by their unique IDs.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/playlists/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listPlaylists($parameters)
    {
        $this->apiUri   = $this->baseUri . '/playlists';
        $this->filters  = [
            'channelId',
            'id',
        ];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a collection of search results that match the query parameters
     * specified in the API request. By default, a search result set identifies
     * matching video, channel, and playlist resources, but you can also configure
     * queries to only retrieve a specific type of resource.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/search/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listSearch($parameters)
    {
        $this->apiUri           = $this->baseUri . '/search';
        $this->filters          = [];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns subscription resources that match the API request criteria.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/subscriptions/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listSubscriptions($parameters)
    {
        $this->apiUri   = $this->baseUri . '/subscriptions';
        $this->filters  = [
            'channelId',
            'id',
        ];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a list of categories that can be associated with YouTube videos.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/videoCategories/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listVideoCategories($parameters)
    {
        $this->apiUri   = $this->baseUri . '/videoCategories';
        $this->filters  = [
            'id',
            'regionCode',
        ];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Returns a list of videos that match the API request parameters.
     *
     * For more in-depth info check out the YouTube API documentation at
     * https://developers.google.com/youtube/v3/docs/videos/list.
     *
     * @access  public
     * @param   array   $parameters An array of parameters to pass to the API.
     * @throws  \InvalidArgumentException
     * @return  array
     */
    public function listVideos($parameters)
    {
        $this->apiUri   = $this->baseUri . '/videos';
        $this->filters  = [
            'chart',
            'id',
        ];

        if (empty($parameters) || !isset($parameters['part'])) {
            throw new \InvalidArgumentException(
                'Missing the required "part" parameter.'
            );
        }

        $response = $this->callApi($parameters);

        return json_decode($response, true);
    }

    /**
     * Perform a cURL call to the YouTube API using the $parameters payload and
     * of method $method.
     *
     * @access  private
     * @param   array   $parameters An array of parameters to pass to the API.
     * @return  string
     */
    private function callApi($parameters)
    {
        $this->checkFilters($parameters);

        $handle     = curl_init();
        $finalUrl   = $this->apiUri . '?' . http_build_query($parameters) . '&key=' . $this->apiKey;

        curl_setopt_array($handle, [
            CURLOPT_HEADER         => false,
            CURLOPT_POST           => false,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_URL            => $finalUrl,
        ]);

        $response = curl_exec($handle);
        curl_close($handle);

        return $response;
    }

    /**
     * Check if at least one of the filters currently set in $this->filters occurs
     * in $parameters.
     *
     * @access  private
     * @param   array   $parameters An array of properties passed to the called method.
     * @throws  \InvalidArgumentException
     */
    private function checkFilters($parameters)
    {
        $atLeastOneFilterSet = false;

        foreach ($this->filters as $filter) {
            if (array_key_exists($filter, $parameters)) {
                $atLeastOneFilterSet = true;
            }
        }

        if (!$atLeastOneFilterSet && !empty($this->filters)) {
            throw new \InvalidArgumentException(
                'Missing a required filter parameter (' . join(', ', $this->filters) . ').'
            );
        }
    }
}


<?php
namespace wapmorgan\Threadable;

/**
 * Class providing basic thread functionality
 */
abstract class Worker extends ForkingThread
{
    const RUNNING = 1;
    const IDLE = 2;
    const TERMINATING = 3;
    const TERMINATED = 4;

	/**
	 * @var bool If multi-threading simulation enabled
	 * Automatically enables when pcntl extension is not supported
	 */
    protected $simulation = false;
	/**
	 * @var array Buffer for payload result when simulation is enabled
	 */
	protected $simulationBuffer = [];

	/**
	 * @var bool Whether child should send signals when it's done
	 */
    protected $useSignals = false;

	/**
	 * @var bool Whether child should stop thread when destructing object
	 */
    protected $selfManaged = true;

    // parent properties
    /** @var int Child (worker) process state */
    public $state = self::IDLE;

    /** @var int Child (worker) process id */
    protected $pid;

    /** @var resource Socket to child (worker) process */
    protected $toChild;

    /** @var int Counter of remaining jobs */
    protected $remainingPayloadsCounter = 0;

    /** @var int Count of sent jobs */
    protected $sentPayloadsCounter = 0;

    /** @var int Count of handled jobs */
    protected $reportedPayloadsCounter = 0;

    // child properties
    /** @var int Id of parent process */
    protected $parentPid;
    /** @var resource Socket to parent process */
    protected $toParent;

    /** @var bool Indicator that SIGTERM has been received */
    protected $termAccepted = false;

    /** @var int Time in microseconds between checks for new payload from parent */
    public $checkMicroTime = 10000;

	/**
	 * Configures worker
	 * @param bool $simulation
	 */
    public function __construct($simulation = false)
    {
    	if ($simulation || !self::supportsForking())
    		$this->simulation = true;
    }

	/**
	 * @return bool
	 */
	public function isSimulated()
	{
		return $this->simulation;
	}

    /**
     * Disables self-management. Use only if you manage workers via WorkersPool.
     */
    public function disableSelfManagement()
    {
        $this->useSignals = true;
        $this->selfManaged = false;
        return $this;
    }

    public function __destruct()
    {
        // this is Worker manager thread
        if ($this->selfManaged && $this->pid !== null && $this->isActive())
            $this->stop(true);
    }

    /**
     * Starts a worker and begins listening for incoming payload.
	 * This method should not be invoked when new object is constructing
	 * because a Worker can be cloned before starting working.
     * @throws \Exception
     */
    public function start()
    {
    	if (!$this->simulation) {
			$sockets = [];
			socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets);
			$pid = getmypid();

			$this->pid = $this->forkThread([$this, 'onWorkerStart'], [$sockets[1], $pid]);
			$this->toChild = $sockets[0];
			socket_set_nonblock($this->toChild);
		}
    }

    /**
     * Stops a worker and ends the process
     * @param boolean $wait If true, this method will hold execution till the process end
     * @return boolean Null, if worker is not active. False, if signal can't be sent to worker.
     * True, if signal sent OR signal sent and worker terminated (if $wait = true).
     */
    public function stop($wait = false)
    {
    	if ($this->simulation) {
			$this->state = self::TERMINATED;
			return true;
		}

        if (!is_dir('/proc/'.$this->pid))
            return null;

        $this->state = self::TERMINATING;

        if (!posix_kill($this->pid, SIGTERM))
            return false;

        if ($wait) {
            pcntl_waitpid($this->pid, $status);
            $this->state = self::TERMINATED;
        } else {
            if (!is_dir('/proc/'.$this->pid))
                $this->state = self::TERMINATED;
        }
        return true;
    }

    /**
     * Stops a worker and kills the process
     * @param boolean $wait If true, this method will hold execution till the process end
     * @return boolean Null, if worker is not active. False, if signal can't be sent to worker.
     * True, if signal sent OR signal sent and worker terminated (if $wait = true).
     */
    public function kill($wait = false)
    {
		if ($this->simulation) {
			$this->state = self::TERMINATED;
			return true;
		}

        if (!is_dir('/proc/'.$this->pid))
            return null;

        $this->state = self::TERMINATING;

        if (!posix_kill($this->pid, SIGKILL))
            return false;

        if ($wait) {
            pcntl_waitpid($this->pid, $status);
            $this->state = self::TERMINATED;
        } else {
            if (!is_dir('/proc/'.$this->pid))
                $this->state = self::TERMINATED;
        }
        return true;
    }

    /**
     * @return boolean Returns whether worker is running right now.
     */
    public function isRunning()
    {
        return $this->state === self::RUNNING;
    }

    /**
     * @return boolean Returns whether worker is in idle state.
     */
    public function isIdle()
    {
        return $this->state === self::IDLE;
    }

    /**
     * @return boolean Returns whether worker is active right now (running or idle).
     */
    public function isActive()
    {
        return in_array($this->state, [self::RUNNING, self::IDLE], true);
    }

    /**
     * @return array|mixed|null
     */
    public function checkForFinish()
    {
    	if ($this->simulation) {
    		if ($this->reportedPayloadsCounter < count($this->simulationBuffer)) {
				$payload_result = $this->simulationBuffer[$this->reportedPayloadsCounter];
				return [$this->reportedPayloadsCounter++, $payload_result];
			}
			return null;
		}

        if (strlen($msg_size_bytes = socket_read($this->toChild, 4)) === 4) {
            $data = unpack('N', $msg_size_bytes);
            $msg_size = $data[1];

            $msg = socket_read($this->toChild, $msg_size);
            $data = unserialize($msg);
            $this->remainingPayloadsCounter--;

            // mark as idle only if this last payload
            if ($this->remainingPayloadsCounter === 0)
                $this->state = self::IDLE;

            return [$this->reportedPayloadsCounter++, $data];
        }
        return null;
    }

    /**
     * @return null|boolean
     */
    public function checkForTermination()
    {
    	if ($this->simulation) {
    		return null;
		}

        $pid = pcntl_waitpid($this->pid, $status, WNOHANG);
        if ($pid === $this->pid) {
            $this->state = self::TERMINATED;
            socket_close($this->toChild);
            return true;
        }
        return null;
    }

	/**
	 * Sends payload to worker
	 * @param mixed $data
	 * @return int Serial number of payload
	 * @throws \Exception
	 */
    public function sendPayload($data)
    {
    	// if not forked yet
		if (!$this->simulation && $this->pid === null) {
			$this->start();
		}

        $this->remainingPayloadsCounter++;
        $this->state = self::RUNNING;

        if ($this->simulation) {
			$result = $this->sentPayloadsCounter++;
			$this->simulationBuffer[$result] = $this->onPayload($data);
			$this->state = self::IDLE;
			return $result;
		}

		$data = serialize($data);
		// write payload to socket
		socket_write($this->toChild, pack('N', strlen($data)));
		socket_write($this->toChild, $data);

        return $this->sentPayloadsCounter++;
    }

    /**
     * @return integer The process ID of the worker
     */
    public function getPid()
    {
        return $this->pid;
    }

	/**
	 * @return int
	 */
	public function getCurrentPayload()
	{
		return $this->reportedPayloadsCounter;
	}

    ///
    /// Child methods below only
    ///

    /**
     * The first method that is being called when the worker starts.
     * @param resource $socket Socket to communicate with master process and transport payload / result.
     * @param integer $parentPid The process ID of the parent
     */
    protected function onWorkerStart($socket, $parentPid)
    {
        declare(ticks = 1);
        // set signal handler
        pcntl_signal(SIGTERM, [$this, 'onSigTerm']);

        $this->toParent = $socket;
        $this->parentPid = $parentPid;
        socket_set_nonblock($this->toParent);
        $i = 0;
        while (!$this->termAccepted) {
            // echo ($i++).' try'.PHP_EOL;
            // sleep for sometime
            if (strlen($msg_size_bytes = socket_read($this->toParent, 4)) != 4) {
                usleep($this->checkMicroTime);
                continue;
            }
            $data = unpack('N', $msg_size_bytes);
            $msg_size = $data[1];

            $msg = socket_read($this->toParent, $msg_size);
            $data = unserialize($msg);

            // echo '[Payload] Msg('.$msg_size.'): '.$msg.PHP_EOL;

            // launch worker to do it's work
            $result = call_user_func([$this, 'onPayload'], $data);
            $data = serialize($result);

            // write result to socket
            socket_write($this->toParent, pack('N', strlen($data)));
            // echo '[Finish] Msg('.strlen($data).'): '.$data.PHP_EOL;
            socket_write($this->toParent, $data);

            // send SIGUSR1 to parent to indicate that's we've finished
            if ($this->useSignals)
                posix_kill($this->parentPid, SIGUSR1);
        }
        socket_close($this->toParent);
        // notify we ended
        if ($this->useSignals)
            posix_kill($this->parentPid, SIGUSR2);
    }

    /**
     * The main handler and work executor in worker. Accepts all payload and should do all the work to process it.
     * @return boolean
     */
    abstract public function onPayload($payload);

    /**
     * SIGTERM Handler to gracefully stop worker without losing any data.
     */
    public function onSigTerm()
    {
        $this->termAccepted = true;
    }
}


<?php
namespace wapmorgan\Threadable;

use Exception;

class ForkingThread
{
	/**
	 * @return bool
	 */
	public static function supportsForking()
	{
		return function_exists('pcntl_fork');
	}

	/**
	 * Forks current process.
	 * The parent thread will be returned to calling code.
	 * The child thread will execute callback and exit after that.
	 * @param $callback
	 * @param array $params
	 * @return integer The ID of child process
	 * @throws Exception
	 */
	public function forkThread($callback, array $params = [])
	{
		$res = pcntl_fork();
		if ($res < 0)
			throw new Exception('Can\'t fork');

		if ($res === 0) {
			call_user_func_array($callback, $params);
			exit;
		} else {
			return $res;
		}
	}
}


<?php

/*
 * This file is part of the Active Collab Authentication project.
 *
 * (c) A51 doo <info@activecollab.com>. All rights reserved.
 */

namespace ActiveCollab\Authentication\Authorizer;

use ActiveCollab\Authentication\AuthenticatedUser\RepositoryInterface;
use ActiveCollab\Authentication\Authorizer\ExceptionAware\ExceptionHandler\ExceptionHandlerInterface;
use ActiveCollab\Authentication\Authorizer\RequestAware\RequestAware;
use ActiveCollab\Authentication\Authorizer\RequestAware\RequestAwareInterface;
use ActiveCollab\Authentication\Authorizer\RequestProcessor\RequestProcessorInterface;
use ActiveCollab\Authentication\Exception\InvalidCredentialsException;
use ActiveCollab\Authentication\Exception\UserNotFoundException;

/**
 * @package ActiveCollab\Authentication\Authorizer
 */
class SamlAuthorizer extends Authorizer implements RequestAwareInterface
{
    use RequestAware;

    /**
     * @var RepositoryInterface
     */
    private $user_repository;

    /**
     * @param RepositoryInterface       $user_repository
     * @param RequestProcessorInterface $request_processor
     * @param ExceptionHandlerInterface $exception_handler
     */
    public function __construct(RepositoryInterface $user_repository, RequestProcessorInterface $request_processor = null, ExceptionHandlerInterface $exception_handler = null)
    {
        $this->user_repository = $user_repository;
        $this->setRequestProcessor($request_processor);
        $this->setExceptionHandler($exception_handler);
    }

    /**
     * {@inheritdoc}
     */
    public function verifyCredentials(array $payload)
    {
        if (!isset($payload['username'])) {
            throw new InvalidCredentialsException();
        }

        $user = $this->user_repository->findByUsername($payload['username']);

        if (!$user) {
            throw new UserNotFoundException();
        }

        return $user;
    }
}


<?php

/*
 * This file is part of the Active Collab Authentication project.
 *
 * (c) A51 doo <info@activecollab.com>. All rights reserved.
 */

namespace ActiveCollab\Authentication\Saml;

use ActiveCollab\Authentication\Session\SessionInterface;
use DateTime;
use InvalidArgumentException;
use LightSaml\Binding\BindingFactory;
use LightSaml\ClaimTypes;
use LightSaml\Context\Profile\MessageContext;
use LightSaml\Credential\KeyHelper;
use LightSaml\Credential\X509Certificate;
use LightSaml\Helper;
use LightSaml\Model\Assertion\Issuer;
use LightSaml\Model\Context\DeserializationContext;
use LightSaml\Model\Context\SerializationContext;
use LightSaml\Model\Protocol\AuthnRequest;
use LightSaml\Model\Protocol\Response;
use LightSaml\Model\XmlDSig\SignatureWriter;
use LightSaml\SamlConstants;

/**
 * @package ActiveCollab\Authentication\Saml
 */
class SamlUtils
{
    const SESSION_DURATION_TYPE_ATTRIBUTE_NAME = 'session_duration_type';

    /**
     * Get saml authnRequest.
     *
     * @param  string $consumer_service_url
     * @param  string $idp_destination
     * @param  string $issuer
     * @param  string $saml_crt
     * @param  string $saml_key
     * @return string
     */
    public function getAuthnRequest(
        $consumer_service_url,
        $idp_destination,
        $issuer,
        $saml_crt,
        $saml_key
    ) {
        $authn_request = new AuthnRequest();
        $authn_request
            ->setAssertionConsumerServiceURL($consumer_service_url)
            ->setProtocolBinding(SamlConstants::BINDING_SAML2_HTTP_POST)
            ->setID(Helper::generateID())
            ->setIssueInstant(new DateTime())
            ->setDestination($idp_destination)
            ->setIssuer(new Issuer($issuer));

        $certificate = new X509Certificate();
        $certificate->loadPem($saml_crt);
        $private_key = KeyHelper::createPrivateKey($saml_key, '', false);

        $authn_request->setSignature(new SignatureWriter($certificate, $private_key));

        $serialization_context = new SerializationContext();
        $authn_request->serialize($serialization_context->getDocument(), $serialization_context);

        $binding_factory = new BindingFactory();
        $redirect_binding = $binding_factory->create(SamlConstants::BINDING_SAML2_HTTP_REDIRECT);

        $message_context = new MessageContext();
        $message_context->setMessage($authn_request);

        /** @var \Symfony\Component\HttpFoundation\RedirectResponse $http_response */
        $http_response = $redirect_binding->send($message_context);

        return $http_response->getTargetUrl();
    }

    /**
     * Parse saml response.
     *
     * @param  array    $payload
     * @return Response
     */
    public function parseSamlResponse(array $payload)
    {
        $deserialization_context = new DeserializationContext();
        $deserialization_context->getDocument()->loadXML(base64_decode($payload['SAMLResponse']));

        $saml_response = new Response();
        $saml_response->deserialize($deserialization_context->getDocument()->firstChild, $deserialization_context);

        return $saml_response;
    }

    /**
     * @param  Response    $response
     * @return null|string
     */
    public function getEmailAddress(Response $response)
    {
        foreach ($response->getAllAssertions() as $assertion) {
            foreach ($assertion->getAllAttributeStatements() as $statement) {
                $username = $statement->getFirstAttributeByName(ClaimTypes::EMAIL_ADDRESS);

                if ($username) {
                    return $username->getFirstAttributeValue();
                }
            }
        }
    }

    public function getSessionDurationType(Response $response)
    {
        foreach ($response->getAllAssertions() as $assertion) {
            foreach ($assertion->getAllAttributeStatements() as $statement) {
                $session_type = $statement->getFirstAttributeByName(SsoResponse::SESSION_DURATION_TYPE_ATTRIBUTE_NAME);

                if ($session_type && $this->validateSessionType($session_type->getFirstAttributeValue())) {
                    return $session_type->getFirstAttributeValue();
                }
            }
        }

        return SessionInterface::DEFAULT_SESSION_DURATION;
    }

    /**
     * @param  Response $response
     * @return string
     */
    public function getIssuerUrl(Response $response)
    {
        return $response->getIssuer()->getValue();
    }

    private function validateSessionType($session_type)
    {
        if (!in_array($session_type, SessionInterface::SESSION_DURATIONS)) {
            throw new InvalidArgumentException('Invalid session duration value');
        }

        return true;
    }
}


<?php

/**
 * This file is part of plumphp/plum.
 *
 * (c) Florian Eckerstorfer <florian@eckerstorfer.co>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Plum\Plum\Pipe;

use InvalidArgumentException;
use Plum\Plum\Writer\WriterInterface;

/**
 * WriterPipe.
 *
 * @author    Florian Eckerstorfer
 * @copyright 2014-2016 Florian Eckerstorfer
 */
class WriterPipe extends AbstractPipe
{
    /**
     * @param WriterInterface|array $element
     *
     * @return WriterPipe
     */
    public static function createWriter($element)
    {
        if ($element instanceof WriterInterface) {
            $writer = $element;
        } elseif (isset($element['writer']) && $element['writer'] instanceof WriterInterface) {
            $writer = $element['writer'];
        } else {
            throw new InvalidArgumentException('Workflow::addWriter() must be called with either an instance of '.
                                               '"Plum\Plum\Writer\WriterInterface" or with an array that contains '.
                                               '"writer".');
        }

        $pipe         = new self($element);
        $pipe->writer = $writer;

        return $pipe;
    }
}


<?php

/*
 * This file is part of the Active Collab Authentication project.
 *
 * (c) A51 doo <info@activecollab.com>. All rights reserved.
 */

namespace ActiveCollab\Authentication\Authorizer\ExceptionAware;

use ActiveCollab\Authentication\Authorizer\ExceptionAware\DelegatesToHandler\DelegatesToHandlerInterface;

/**
 * @package ActiveCollab\Authentication\Authorizer\ExceptionAware
 */
trait ExceptionAware
{
    /**
     * {@inheritdoc}
     */
    public function handleException(array $credentials, $error_or_exception)
    {
        if ($this instanceof DelegatesToHandlerInterface && $this->getExceptionHandler()) {
            return $this->getExceptionHandler()->handleException($credentials, $error_or_exception);
        }

        return null;
    }
}


<?php

/*
 * This file is part of the Active Collab Authentication project.
 *
 * (c) A51 doo <info@activecollab.com>. All rights reserved.
 */

namespace ActiveCollab\Authentication\Adapter;

use ActiveCollab\Authentication\AuthenticationResult\Transport\Authentication\AuthenticationTransportInterface;
use ActiveCollab\Authentication\AuthenticationResult\Transport\Authorization\AuthorizationTransportInterface;
use ActiveCollab\Authentication\AuthenticationResult\Transport\TransportInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

/**
 * @package ActiveCollab\Authentication\Adapter
 */
abstract class Adapter implements AdapterInterface
{
    /**
     * {@inheritdoc}
     */
    public function applyTo(ServerRequestInterface $request, ResponseInterface $response, TransportInterface $transport)
    {
        if ($transport instanceof AuthenticationTransportInterface || $transport instanceof AuthorizationTransportInterface) {
            $request = $request
                ->withAttribute('authentication_adapter', $this)
                ->withAttribute('authenticated_user', $transport->getAuthenticatedUser())
                ->withAttribute('authenticated_with', $transport->getAuthenticatedWith());
        }

        return [$request, $response];
    }
}


<?php

/*
 * This file is part of the Active Collab Authentication project.
 *
 * (c) A51 doo <info@activecollab.com>. All rights reserved.
 */

namespace ActiveCollab\Authentication\Password\Manager;

use InvalidArgumentException;

/**
 * @package ActiveCollab\Authentication\Password
 */
class PasswordManager implements PasswordManagerInterface
{
    /**
     * @var string
     */
    private $global_salt;

    /**
     * @param string $global_salt
     */
    public function __construct($global_salt = '')
    {
        $this->global_salt = (string) $global_salt;
    }

    /**
     * {@inheritdoc}
     */
    public function verify($password, $hash, $hashed_with)
    {
        switch ($hashed_with) {
            case self::HASHED_WITH_PHP:
                return password_verify($this->global_salt . $password, $hash);
            case self::HASHED_WITH_PBKDF2:
            case self::HASHED_WITH_SHA1:
                return $this->hash($password, $hashed_with) === $hash;
            default:
                throw new InvalidArgumentException("Hashing mechanism '$hashed_with' is not supported");
        }
    }

    /**
     * {@inheritdoc}
     */
    public function hash($password, $hash_with = self::HASHED_WITH_PHP)
    {
        switch ($hash_with) {
            case self::HASHED_WITH_PHP:
                return password_hash($this->global_salt . $password, PASSWORD_DEFAULT);
            case self::HASHED_WITH_PBKDF2:
                return base64_encode($this->pbkdf2($password, $this->global_salt, 1000, 40));
            case self::HASHED_WITH_SHA1:
                return sha1($this->global_salt . $password);
            default:
                throw new InvalidArgumentException("Hashing mechanism '$hash_with' is not supported");
        }
    }

    /**
     * {@inheritdoc}
     */
    public function needsRehash($hash, $hashed_with)
    {
        if ($hashed_with === self::HASHED_WITH_PHP) {
            return password_needs_rehash($hash, PASSWORD_DEFAULT);
        } else {
            return true;
        }
    }

    /**
     * PBKDF2 Implementation (described in RFC 2898).
     *
     * Source: http://www.itnewb.com/tutorial/Encrypting-Passwords-with-PHP-for-Storage-Using-the-RSA-PBKDF2-Standard
     *
     * @param  string $p  password
     * @param  string $s  salt
     * @param  int    $c  iteration count (use 1000 or higher)
     * @param  int    $kl derived key length
     * @param  string $a  hash algorithm
     * @return string derived key
     */
    private function pbkdf2($p, $s, $c, $kl, $a = 'sha256')
    {
        $hl = strlen(hash($a, null, true)); // Hash length
        $kb = ceil($kl / $hl);              // Key blocks to compute
        $dk = '';                           // Derived key

        // Create key
        for ($block = 1; $block <= $kb; ++$block) {

            // Initial hash for this block
            $ib = $b = hash_hmac($a, $s . pack('N', $block), $p, true);

            // Perform block iterations
            for ($i = 1; $i < $c; ++$i) {
                // XOR each iterate

                $ib ^= ($b = hash_hmac($a, $b, $p, true));
            }

            $dk .= $ib; // Append iterated block
        }

        // Return derived key of correct length

        return substr($dk, 0, $kl);
    }
}


<?php
namespace Peridot\WebDriverManager;

use Peridot\WebDriverManager\Binary\BinaryInterface;
use Peridot\WebDriverManager\Binary\BinaryResolver;
use Peridot\WebDriverManager\Binary\BinaryResolverInterface;
use Peridot\WebDriverManager\Binary\ChromeDriver;
use Peridot\WebDriverManager\Binary\DriverInterface;
use Peridot\WebDriverManager\Binary\IEDriver;
use Peridot\WebDriverManager\Binary\SeleniumStandalone;
use Peridot\WebDriverManager\Event\EventEmitterInterface;
use Peridot\WebDriverManager\Event\EventEmitterTrait;
use Peridot\WebDriverManager\Process\SeleniumProcessInterface;
use Peridot\WebDriverManager\Process\SeleniumProcess;
use RuntimeException;

/**
 * The Manager provides an api for controlling Selenium Server. It can be used
 * to keep binaries and drivers up to date, as well as start Selenium Server.
 *
 * @package Peridot\WebDriverManager
 */
class Manager implements EventEmitterInterface
{
    use EventEmitterTrait;

    /**
     * @var array
     */
    protected $binaries;

    /**
     * @var BinaryResolverInterface
     */
    protected $resolver;

    /**
     * @var SeleniumProcessInterface
     */
    protected $process;

    /**
     * @var string
     */
    protected $installPath = '';

    /**
     * @param BinaryResolverInterface $resolver
     * @param SeleniumProcessInterface $process
     */
    public function __construct(BinaryResolverInterface $resolver = null, SeleniumProcessInterface $process = null) {
        $this->resolver = $resolver;
        $this->process = $process;
        $this->binaries = [];

        $resolver = $this->getBinaryResolver();
        $this->addBinary(new SeleniumStandalone($resolver));
        $this->addBinary(new ChromeDriver($resolver));
        $this->addBinary(new IEDriver($resolver));
        $this->setBinaryResolver($resolver);
    }

    /**
     * Add a binary to the collection of managed binaries.
     *
     * @param BinaryInterface $binary
     */
    public function addBinary(BinaryInterface $binary)
    {
        $this->binaries[$binary->getName()] = $binary;
    }

    /**
     * Remove a binary from the collection of managed binaries.
     *
     * @param string $binaryName
     */
    public function removeBinary($binaryName)
    {
        if (isset($this->binaries[$binaryName])) {
            unset($this->binaries[$binaryName]);
        }
    }

    /**
     * Return the BinaryResolver used to resolve binary files.
     *
     * @return BinaryResolver|BinaryResolverInterface
     */
    public function getBinaryResolver()
    {
        if ($this->resolver === null) {
            $this->resolver = new BinaryResolver();
        }

        return $this->resolver;
    }

    /**
     * Set the BinaryResolver used to resolve binary files.
     *
     * @param BinaryResolverInterface $resolver
     */
    public function setBinaryResolver(BinaryResolverInterface $resolver)
    {
        $this->resolver = $resolver;
        $this->inherit(['progress', 'request.start', 'complete'], $resolver);
    }

    /**
     * Return the SeleniumProcessInterface that will execute the
     * selenium server command.
     *
     * @return SeleniumProcess|SeleniumProcessInterface
     */
    public function getSeleniumProcess()
    {
        if ($this->process === null) {
            $this->process = new SeleniumProcess();
        }

        return $this->process;
    }

    /**
     * Return all managed binaries.
     *
     * @param callable $predicate
     * @return array
     */
    public function getBinaries(callable $predicate = null)
    {
        $binaries = $this->binaries;
        if ($predicate !== null) {
            return array_filter($binaries, $predicate);
        }
        return $binaries;
    }

    /**
     * Return all binaries that are considered drivers.
     *
     * @return array
     */
    public function getDrivers()
    {
        return $this->getBinaries(function ($binary) {
            return $binary instanceof DriverInterface;
        });
    }

    /**
     * Pending binaries are binaries that are supported but have not been installed.
     *
     * @return array
     */
    public function getPendingBinaries()
    {
        return $this->getBinaries(function (BinaryInterface $binary) {
            $exists = $binary->exists($this->getInstallPath());
            $supported = $binary->isSupported();
            return $supported && !$exists;
        });
    }

    /**
     * Fetch and save binaries.
     *
     * @return bool
     */
    public function update($binaryName = '')
    {
        if ($binaryName) {
            $this->updateSingle($binaryName);
            return;
        }

        foreach ($this->binaries as $binary) {
            $binary->fetchAndSave($this->getInstallPath());
        }
    }

    /**
     * Update a single binary.
     *
     * @param $binaryName
     * @return void
     */
    public function updateSingle($binaryName)
    {
        if (! array_key_exists($binaryName, $this->binaries)) {
            throw new RuntimeException("Binary named $binaryName does not exist");
        }

        $binary = $this->binaries[$binaryName];
        $binary->fetchAndSave($this->getInstallPath());
    }

    /**
     * Start the Selenium server.
     *
     * @param bool $background
     * @param int $port
     * @param array $args
     * @return SeleniumProcessInterface
     */
    public function start($background = false, $port = 4444, array $args = [])
    {
        $selenium = $this->binaries['selenium'];
        $this->assertStartConditions($selenium);

        $process = $this->getSeleniumProcess();
        $this->registerBinaries($process, $selenium);

        if ($port != 4444) {
            $process->addArg('-port', $port);
        }

        if (!empty($args)) {
            $process->addArgs($args);
        }

        return $process->start($background);
    }

    /**
     * Start Selenium in the foreground.
     *
     * @param int $port
     * @return SeleniumProcessInterface
     */
    public function startInForeground($port = 4444, array $args = [])
    {
        return $this->start(false, $port, $args);
    }

    /**
     * Start Selenium in a background process.
     *
     * @param int $port
     * @return SeleniumProcessInterface
     */
    public function startInBackground($port = 4444, array $args = [])
    {
        return $this->start(true, $port, $args);
    }

    /**
     * Remove all binaries from the install path.
     *
     * @return void
     */
    public function clean()
    {
        $files = glob($this->getInstallPath() . '/*');
        foreach ($files as $file) {
            unlink($file);
        }
    }

    /**
     * Get the installation path of binaries.
     *
     * @return string
     */
    public function getInstallPath()
    {
        if ($this->installPath === '') {
            $this->installPath = realpath(__DIR__ . '/../binaries');
        }

        return $this->installPath;
    }

    /**
     * Set the installation path for binaries.
     *
     * @param string $path
     */
    public function setInstallPath($path)
    {
        $this->installPath = $path;
    }

    /**
     * Assert that the selenium server can start.
     *
     * @param SeleniumStandalone $selenium
     * @throws \RuntimeException
     * @return void
     */
    protected function assertStartConditions(SeleniumStandalone $selenium)
    {
        if (!$selenium->exists($this->getInstallPath())) {
            throw new RuntimeException("Selenium Standalone binary not installed");
        }

        if (!$this->getSeleniumProcess()->isAvailable()) {
            throw new RuntimeException('java is not available');
        }
    }

    /**
     * Register selenium binary and drivers with the process.
     *
     * @param SeleniumProcessInterface $process
     * @param SeleniumStandalone $selenium
     * @return void
     */
    protected function registerBinaries(SeleniumProcessInterface $process, SeleniumStandalone $selenium)
    {
        $drivers = $this->getDrivers();
        foreach ($drivers as $driver) {
            $process->addBinary($driver, $this->getInstallPath());
        }
        $process->addBinary($selenium, $this->getInstallPath());
    }
}


<?php
use Peridot\Scope\Scope;
use Prophecy\Argument;
use Prophecy\Prophet;

class BinaryHelper extends Scope
{
    /**
     * @var Prophet
     */
    private $prophet;

    public function __construct(Prophet $prophet)
    {
        $this->prophet = $prophet;
    }

    /**
     * Create a binary mock.
     *
     * @param $name
     * @param $supported
     * @param $exists
     */
    public function createBinary($name, $supported, $exists)
    {
        $binary = $this->prophet->prophesize('Peridot\WebDriverManager\Binary\BinaryInterface');
        $binary->isSupported()->willReturn($supported);
        $binary->getName()->willReturn($name);
        $binary->exists(Argument::any())->willReturn($exists);
        return $binary;
    }
}


<?php

/*
 * This file is part of the Active Collab Authentication project.
 *
 * (c) A51 doo <info@activecollab.com>. All rights reserved.
 */

namespace ActiveCollab\Authentication\LoginPolicy;

use InvalidArgumentException;

/**
 * @package ActiveCollab\Authentication\LoginPolicy
 */
class LoginPolicy implements LoginPolicyInterface
{
    /**
     * @var string
     */
    private $username_format;

    /**
     * @var bool
     */
    private $remember_extends_session;

    /**
     * @var bool
     */
    private $password_change_enabled;

    /**
     * @var bool
     */
    private $password_recovery_enabled;

    /**
     * @var string
     */
    private $external_login_url;

    /**
     * @var string
     */
    private $external_logout_url;

    /**
     * @var string
     */
    private $external_change_password_url;

    /**
     * @var string
     */
    private $external_update_profile_url;

    /**
     * LoginPolicy constructor.
     *
     * @param string      $username_format
     * @param bool        $remember_extends_session
     * @param bool        $password_change_enabled
     * @param bool        $password_recovery_enabled
     * @param string|null $external_login_url
     * @param string|null $external_logout_url
     * @param string|null $external_change_password_url
     * @param string|null $external_update_profile_url
     */
    public function __construct($username_format = self::USERNAME_FORMAT_TEXT, $remember_extends_session = true, $password_change_enabled = true, $password_recovery_enabled = true, $external_login_url = null, $external_logout_url = null, $external_change_password_url = null, $external_update_profile_url = null)
    {
        $this->setUsernameFormat($username_format);

        $this->setRememberExtendsSession($remember_extends_session);
        $this->setIsPasswordChangeEnabled($password_change_enabled);
        $this->setIsPasswordRecoveryEnabled($password_recovery_enabled);

        $this->setExternalLoginUrl($external_login_url);
        $this->setExternalLogoutUrl($external_logout_url);
        $this->setExternalChangePasswordUrl($external_change_password_url);
        $this->setExternalUpdateProfileUrl($external_update_profile_url);
    }

    /**
     * {@inheritdoc}
     */
    public function getUsernameFormat()
    {
        return $this->username_format;
    }

    /**
     * {@inheritdoc}
     */
    public function &setUsernameFormat($value)
    {
        if (in_array($value, self::VALID_USERNAME_FORMATS)) {
            $this->username_format = $value;
        } else {
            throw new InvalidArgumentException('Username format is not valid');
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function rememberExtendsSession()
    {
        return $this->remember_extends_session;
    }

    /**
     * {@inheritdoc}
     */
    public function &setRememberExtendsSession($value)
    {
        $this->remember_extends_session = (bool) $value;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function isPasswordChangeEnabled()
    {
        return $this->password_change_enabled;
    }

    /**
     * {@inheritdoc}
     */
    public function &setIsPasswordChangeEnabled($value)
    {
        $this->password_change_enabled = (bool) $value;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function isPasswordRecoveryEnabled()
    {
        return $this->password_recovery_enabled;
    }

    /**
     * {@inheritdoc}
     */
    public function &setIsPasswordRecoveryEnabled($value)
    {
        $this->password_recovery_enabled = (bool) $value;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getExternalLoginUrl()
    {
        return $this->external_login_url;
    }

    /**
     * {@inheritdoc}
     */
    public function &setExternalLoginUrl($value)
    {
        $this->external_login_url = $this->getValidExternalUrlValue($value);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getExternalLogoutUrl()
    {
        return $this->external_logout_url;
    }

    /**
     * {@inheritdoc}
     */
    public function &setExternalLogoutUrl($value)
    {
        $this->external_logout_url = $this->getValidExternalUrlValue($value);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getExternalChangePasswordUrl()
    {
        return $this->external_change_password_url;
    }

    /**
     * {@inheritdoc}
     */
    public function &setExternalChangePasswordUrl($value)
    {
        $this->external_change_password_url = $this->getValidExternalUrlValue($value);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getExternalUpdateProfileUrl()
    {
        return $this->external_update_profile_url;
    }

    /**
     * {@inheritdoc}
     */
    public function &setExternalUpdateProfileUrl($value)
    {
        $this->external_update_profile_url = $this->getValidExternalUrlValue($value);

        return $this;
    }

    /**
     * Validate and return acceptable URL value.
     *
     * @param  string|null $url
     * @return string|null
     */
    private function getValidExternalUrlValue($url)
    {
        if ($url === null || (is_string($url) && filter_var($url, FILTER_VALIDATE_URL))) {
            return $url;
        }

        throw new InvalidArgumentException('URL is not valid');
    }

    /**
     * {@inheritdoc}
     */
    public function jsonSerialize()
    {
        return [
            'username_format' => $this->getUsernameFormat(),
            'remember_extends_session' => $this->rememberExtendsSession(),
            'password_change_enabled' => $this->isPasswordChangeEnabled(),
            'password_recovery_enabled' => $this->isPasswordRecoveryEnabled(),
            'external_login_url' => $this->getExternalLoginUrl(),
            'external_logout_url' => $this->getExternalLogoutUrl(),
            'external_change_password_url' => $this->getExternalChangePasswordUrl(),
            'external_update_profile_url' => $this->getExternalUpdateProfileUrl(),
        ];
    }
}


<?php

/**
 * This file is part of plumphp/plum.
 *
 * (c) Florian Eckerstorfer <florian@eckerstorfer.co>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Plum\Plum\Converter;

/**
 * HeaderConverter.
 *
 * @author    Sebastian Göttschkes <sebastian.goettschkes@googlemail.com>
 * @copyright 2014-2016 Florian Eckerstorfer
 */
class HeaderConverter implements ConverterInterface
{
    /** @var string[]|null */
    private $header = null;

    /**
     * {@inheritdoc}
     */
    public function convert($item)
    {
        if ($this->header === null) {
            $this->header = $item;

            return $item;
        }

        $newItem = [];
        foreach ($item as $key => $value) {
            if (isset($this->header[$key])) {
                $key = $this->header[$key];
            }
            $newItem[$key] = $value;
        }

        return $newItem;
    }
}


<?php

/**
 * This file is part of plumphp/plum.
 *
 * (c) Florian Eckerstorfer <florian@eckerstorfer.co>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Plum\Plum\Pipe;

use InvalidArgumentException;
use Plum\Plum\Converter\CallbackConverter;
use Plum\Plum\Converter\ConverterInterface;

/**
 * ConverterPipe.
 *
 * @author    Florian Eckerstorfer
 * @copyright 2014-2016 Florian Eckerstorfer
 */
class ConverterPipe extends AbstractPipe
{
    /**
     * @param ConverterInterface|callable|array $element
     *
     * @return ConverterPipe
     */
    public static function createConverter($element)
    {
        if (is_callable($element)) {
            $converter = new CallbackConverter($element);
        } elseif (self::hasElementCallbackConverter($element)) {
            $converter = new CallbackConverter($element['converter']);
        } elseif ($element instanceof ConverterInterface) {
            $converter = $element;
        } elseif (self::hasElementConverter($element)) {
            $converter = $element['converter'];
        } else {
            throw new InvalidArgumentException('Workflow::addConverter() must be called with either an instance of '.
                                               '"Plum\Plum\Converter\ConverterInterface" or with an array that '.
                                               'contains "converter".');
        }

        $pipe            = new self($element);
        $pipe->converter = $converter;

        return $pipe;
    }

    /**
     * @param array|callable|ConverterInterface $element
     *
     * @return bool
     */
    protected static function hasElementCallbackConverter($element)
    {
        return is_array($element) && isset($element['converter']) && is_callable($element['converter']);
    }

    /**
     * @param array|callable|ConverterInterface $element
     *
     * @return bool
     */
    protected static function hasElementConverter($element)
    {
        return is_array($element)
               && isset($element['converter'])
               && $element['converter'] instanceof ConverterInterface;
    }
}


<?php
namespace Peridot\WebDriverManager\Binary;

use Peridot\WebDriverManager\Versions;

/**
 * ChromeDriver is used to resolve the Selenium Server driver for using the Chrome
 * web browser.
 *
 * @package Peridot\WebDriverManager\Binary
 */
class ChromeDriver extends CompressedBinary implements DriverInterface
{
    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getName()
    {
        return 'chromedriver';
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getFileName()
    {
        $file = "chromedriver_";
        $system = $this->resolver->getSystem();

        if ($system->isMac()) {
            $file .= 'mac64';
        }

        if ($system->isWindows()) {
            $file .= "win32";
        }

        if ($system->isLinux()) {
            $file .= $this->getLinuxFileName();
        }

        return "$file.zip";
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getUrl()
    {
        $version = Versions::CHROMEDRIVER;
        return "http://chromedriver.storage.googleapis.com/$version/{$this->getFileName()}";
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getOutputFileName()
    {
        $version = Versions::CHROMEDRIVER;
        return "chromedriver_$version.zip";
    }

    /**
     * Get the linux filename.
     *
     * @return string
     */
    public function getLinuxFileName()
    {
        $file = "linux32";
        $system = $this->resolver->getSystem();
        if ($system->is64Bit()) {
            $file = 'linux64';
        }
        return $file;
    }

    /**
     * Remove old versions of the binary.
     *
     * @param $directory
     * @return void
     */
    protected function removeOldVersions($directory)
    {
        $paths = glob("$directory/chromedriver*");
        foreach ($paths as $path) {
            unlink($path);
        }
    }

    /**
     * {@inheritdoc}
     *
     * @param string $directory
     * @return string
     */
    protected function getOldFilePattern($directory)
    {
        return $directory . '/' . str_replace(Versions::CHROMEDRIVER, '*', $this->getOutputFileName());
    }

    /**
     * {@inheritdoc}
     *
     * @param string $directory
     * @return string
     */
    public function getDriverPath($directory)
    {
        $file = "$directory/chromedriver";
        $system = $this->resolver->getSystem();

        if ($system->isWindows()) {
            $file .= '.exe';
        }

        return "webdriver.chrome.driver=$file";
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getExtractedName()
    {
        $name = $this->getName();
        $system = $this->resolver->getSystem();

        if ($system->isWindows()) {
            $name .= '.exe';
        }
        return $name;
    }
}


<?php
namespace Peridot\WebDriverManager\Binary;

/**
 * CompressedBinary extends the AbstractBinary to include behavior
 * for handling compressed binaries.
 *
 * @package Peridot\WebDriverManager\Binary
 */
abstract class CompressedBinary extends AbstractBinary
{
    /**
     * Overrides default save behavior to first decompress
     * an archive format before saving it to a directory.
     *
     * @param string $directory
     * @return bool
     */
    public function save($directory)
    {
        if (empty($this->contents)) {
            return false;
        }

        if ($this->exists($directory)) {
            return true;
        }

        $compressedPath = $directory . DIRECTORY_SEPARATOR . $this->getOutputFileName();
        $this->removeOldVersions($directory);
        file_put_contents($compressedPath, $this->contents);
        $extracted = $this->resolver->extract($compressedPath, $directory);

        if ($extracted) {
            chmod("$directory/{$this->getExtractedName()}", 0777);
        }

        return $extracted;
    }

    /**
     * {@inheritdoc}
     *
     * @param $directory
     * @return bool
     */
    public function exists($directory)
    {
        return file_exists("$directory/{$this->getOutputFileName()}");
    }

    /**
     * Return the output filename for the compressed binary.
     *
     * @return string
     */
    abstract public function getOutputFileName();

    /**
     * Get the name of the extracted binary.
     *
     * @return string
     */
    abstract public function getExtractedName();
}


<?php
namespace Peridot\WebDriverManager\Process;

use Peridot\WebDriverManager\Binary\BinaryInterface;
use Peridot\WebDriverManager\Binary\DriverInterface;

/**
 * SeleniumProcess is responsible for controlling Selenium Server processes.
 *
 * @package Peridot\WebDriverManager\Process
 */
class SeleniumProcess implements SeleniumProcessInterface
{
    /**
     * @var array
     */
    protected $args = [];

    /**
     * @var resource
     */
    protected $process = null;

    /**
     * @var array
     */
    protected $pipes = [];

    public function __construct()
    {
    }

    /**
     * {@inheritdoc}
     *
     * @param BinaryInterface $binary
     * @param string $directory
     * @return void
     */
    public function addBinary(BinaryInterface $binary, $directory)
    {
        if (! $binary->exists($directory)) {
            return;
        }

        if ($binary instanceof DriverInterface) {
            $this->addArg('-D' . $binary->getDriverPath($directory));
            return;
        }

        if($this->pathContains($binary->getFileName(), ".jar")){
            $this->addArg('-jar');
        }

        $this->addArg(realpath($directory . '/' . $binary->getFileName()));
    }

    private function pathContains($path, $match)
    {
        return strpos($path, $match) !== false;
    }

    /**
     * {@inheritdoc}
     *
     * @return array
     */
    public function getArgs()
    {
        return $this->args;
    }

    /**
     * {@inheritdoc}
     *
     * @param string $arg
     * @return void
     */
    public function addArg($arg)
    {
        $this->args[] = $arg;
        $rest = array_slice(func_get_args(), 1);
        foreach ($rest as $arg) {
            $this->args[] = $arg;
        }
    }

    /**
     * {@inheritdoc}
     *
     * @param array $args
     * @return void
     */
    public function addArgs(array $args)
    {
        foreach ($args as $arg) {
            $this->args[] = $arg;
        }
    }

    /**
     * {@inheritdoc}
     *
     * @return bool
     */
    public function isAvailable()
    {
        $command = 'java -version';
        $descriptors = $this->getDescriptorSpec();
        $this->process = proc_open($this->formatCommand($command), $descriptors, $this->pipes);
        $status = $this->getStatus(true);
        $available = $status['exitcode'] == 0;
        proc_close($this->process);
        return $available;
    }

    /**
     * {@inheritdoc}
     *
     * @return $this
     */
    public function start($background = false)
    {
        $command = $this->getCommand();
        if (! $background) {
            exec($command);
            return $this;
        }

        $descriptors = $this->getDescriptorSpec();
        $this->process = proc_open($command, $descriptors, $this->pipes);
        return $this;
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getCommand()
    {
        return $this->formatCommand('java ' . implode(' ', $this->args));
    }

    /**
     * {@inheritdoc}
     *
     * @param bool $loop
     * @return array
     */
    public function getStatus($loop = false)
    {
        $status = proc_get_status($this->process);
        while ($loop && $status['running']) {
            usleep(20000);
            $status = proc_get_status($this->process);
        }
        return $status;
    }

    /**
     * {@inheritdoc}
     *
     * @return bool
     */
    public function isRunning()
    {
        $status = $this->getStatus();
        return $status['running'];
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getError()
    {
        return stream_get_contents($this->pipes[2]);
    }

    /**
     * {@inheritdoc}
     *
     * @return int
     */
    public function close()
    {
        proc_terminate($this->process);
        return proc_close($this->process);
    }

    /**
     * Format a shell command according to the running Operating System to avoid zombie processes.
     *
     * @param string $command A shell command.
     * @return string
     */
    private function formatCommand($command)
    {
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            return $command;
        }
        return 'exec ' . $command;
    }

    /**
     * Helper to create an array suitable as a descriptor for process streams.
     *
     * @return array
     */
    private function getDescriptorSpec()
    {
        return [
            0 => ['pipe', 'r'],
            1 => ['pipe', 'w'],
            2 => ['pipe', 'w']
        ];
    }
}


<?php
namespace Peridot\WebDriverManager\Binary\Request;

use Peridot\WebDriverManager\Event\EventEmitterTrait;

/**
 * StandardBinaryRequest uses file_get_contents with a stream context. It is capable of emitting
 * download progress events containing bytes transferred, and bytes total.
 *
 * @package Peridot\WebDriverManager\Binary
 */
class StandardBinaryRequest implements BinaryRequestInterface
{
    use EventEmitterTrait;

    /**
     * @var string
     */
    private $url;

    /**
     * {@inheritdoc}
     *
     * @param $url
     * @return string
     */
    public function request($url)
    {
        if (empty($url)) {
            return '';
        }

        $this->url = $url;
        $context = $this->getContext();
        $contents = file_get_contents($url, false, $context);
        $this->emit('complete');
        return $contents;
    }

    /**
     * Callback used when progress is made requesting.
     *
     * @param $notification_code
     * @param $severity
     * @param $message
     * @param $message_code
     * @param $bytes_transferred
     * @param $bytes_max
     * @return void
     */
    public function onNotification($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max)
    {
        switch($notification_code) {
            case STREAM_NOTIFY_PROGRESS:
                $this->emit('progress', [$bytes_transferred]);
                break;
            case STREAM_NOTIFY_FILE_SIZE_IS:
                $this->emit('request.start', [$this->url, $bytes_max]);
                break;
        }
    }

    /**
     * Create a context for file_get_contents.
     *
     * @return resource
     */
    protected function getContext()
    {
        $context_options = [
            'http' => [
                'method' => 'GET',
                'user_agent' => 'Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0'
            ]
        ];
        $context = stream_context_create($context_options, [
            'notification' => [$this, 'onNotification']
        ]);
        return $context;
    }
}


<?php
namespace Peridot\WebDriverManager\Binary;

use Peridot\WebDriverManager\Versions;

/**
 * IEDriver is used to resolve the Selenium Server driver for using the Internet Explorer
 * web browser.
 *
 * @package Peridot\WebDriverManager\Binary
 */
class IEDriver extends CompressedBinary implements DriverInterface
{
    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getName()
    {
        return 'IEDriver';
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getFileName()
    {
        if ($this->isSupported()) {
            $file = "IEDriverServer_Win32_";

            if ($this->resolver->getSystem()->is64Bit()) {
                $file = 'IEDriverServer_x64_';
            }

            return $file . Versions::IEDRIVER . '.zip';
        }
       return "";
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getUrl()
    {
        $fileName = $this->getFileName();

        if (!$fileName) {
            return '';
        }

        $version = Versions::IEDRIVER;
        $short = substr($version, 0, strrpos($version, '.'));
        return "http://selenium-release.storage.googleapis.com/$short/{$this->getFileName()}";
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getOutputFileName()
    {
        $version = Versions::IEDRIVER;
        return "IEDriver_$version.zip";
    }

    /**
     * {@inheritdoc}
     *
     * @return bool
     */
    public function isSupported()
    {
        $system = $this->resolver->getSystem();
        return $system->isWindows();
    }

    /**
     * Remove old versions of the binary.
     *
     * @param $directory
     * @return void
     */
    protected function removeOldVersions($directory)
    {
        $paths = glob("$directory/IEDriver*");
        foreach ($paths as $path) {
            unlink($path);
        }
    }

    /**
     * {@inheritdoc}
     *
     * @param string $directory
     * @return string
     */
    protected function getOldFilePattern($directory)
    {
        return $directory . '/' . str_replace(Versions::IEDRIVER, '*', $this->getOutputFileName());
    }

    /**
     * {@inheritdoc}
     *
     * @param string $directory
     * @return string
     */
    public function getDriverPath($directory)
    {
        $file = "$directory/{$this->getExtractedName()}";
        return "webdriver.ie.driver=$file";
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getExtractedName()
    {
        return 'IEDriverServer.exe';
    }
}


<?php

/*
 * This file is part of the Active Collab Authentication project.
 *
 * (c) A51 doo <info@activecollab.com>. All rights reserved.
 */

namespace ActiveCollab\Authentication;

use ActiveCollab\Authentication\Adapter\AdapterInterface;
use ActiveCollab\Authentication\AuthenticatedUser\AuthenticatedUserInterface;
use ActiveCollab\Authentication\AuthenticationResult\AuthenticationResultInterface;
use ActiveCollab\Authentication\AuthenticationResult\Transport\Authentication\AuthenticationTransportInterface;
use ActiveCollab\Authentication\AuthenticationResult\Transport\Authorization\AuthorizationTransport;
use ActiveCollab\Authentication\AuthenticationResult\Transport\Transport;
use ActiveCollab\Authentication\AuthenticationResult\Transport\TransportInterface;
use ActiveCollab\Authentication\Authorizer\AuthorizerInterface;
use ActiveCollab\Authentication\Exception\InvalidAuthenticationRequestException;
use Exception;
use InvalidArgumentException;
use LogicException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

/**
 * @package ActiveCollab\Authentication
 */
class Authentication implements AuthenticationInterface
{
    /**
     * @var array
     */
    private $adapters;

    /**
     * Authenticated user instance.
     *
     * @var AuthenticatedUserInterface
     */
    private $authenticated_user;

    /**
     * @var AuthenticationResultInterface
     */
    private $authenticated_with;

    /**
     * @var callable[]
     */
    private $on_user_authenticated = [];

    /**
     * @var callable[]
     */
    private $on_user_authorized = [];

    /**
     * @var callable[]
     */
    private $on_user_authorization_failed = [];

    /**
     * @var callable[]
     */
    private $on_user_set = [];

    /**
     * @var callable[]
     */
    private $on_user_deauthenticated = [];

    /**
     * @param array $adapters
     */
    public function __construct(array $adapters)
    {
        foreach ($adapters as $adapter) {
            if (!($adapter instanceof AdapterInterface)) {
                throw new LogicException('Invalid authentication adapter provided');
            }
        }

        $this->adapters = $adapters;
    }

    /**
     * {@inheritdoc}
     */
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
    {
        $auth_result = $this->authenticatedUsingAdapters($request);

        if ($auth_result instanceof TransportInterface && !$auth_result->isEmpty()) {
            if ($auth_result instanceof AuthenticationTransportInterface) {
                $this->setAuthenticatedUser($auth_result->getAuthenticatedUser());
                $this->setAuthenticatedWith($auth_result->getAuthenticatedWith());

                $this->triggerEvent('user_authenticated', $auth_result->getAuthenticatedUser(), $auth_result->getAuthenticatedWith());
            }

            list($request, $response) = $auth_result->applyTo($request, $response);
        }

        if ($next) {
            $response = $next($request, $response);
        }

        return $response;
    }

    /**
     * {@inheritdoc}
     */
    public function authorize(AuthorizerInterface $authorizer, AdapterInterface $adapter, array $credentials, $payload = null)
    {
        try {
            $user = $authorizer->verifyCredentials($credentials);
            $authenticated_with = $adapter->authenticate($user, $credentials);

            $this->triggerEvent('user_authorized', $user, $authenticated_with);

            return new AuthorizationTransport($adapter, $user, $authenticated_with, $payload);
        } catch (Exception $e) {
            $this->triggerEvent('user_authorization_failed', $credentials, $e);

            throw $e;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function terminate(AdapterInterface $adapter, AuthenticationResultInterface $authenticated_with)
    {
        $termination_result = $adapter->terminate($authenticated_with);

        $this->triggerEvent('user_deauthenticated', $authenticated_with);

        return $termination_result;
    }

    /**
     * {@inheritdoc}
     */
    public function getAdapters()
    {
        return $this->adapters;
    }

    /**
     * @param  ServerRequestInterface  $request
     * @return TransportInterface|null
     * @throws Exception
     */
    private function authenticatedUsingAdapters(ServerRequestInterface $request)
    {
        $last_exception = null;
        $results = [];

        /** @var AdapterInterface $adapter */
        foreach ($this->adapters as $adapter) {
            try {
                $initialization_result = $adapter->initialize($request);

                if ($initialization_result instanceof Transport && !$initialization_result->isEmpty()) {
                    $results[] = $initialization_result;
                }
            } catch (Exception $e) {
                $last_exception = $e;
            }
        }

        if (empty($results)) {
            if ($last_exception) {
                throw $last_exception;
            }

            return null;
        }

        if (count($results) > 1) {
            throw new InvalidAuthenticationRequestException('You can not be authenticated with more than one authentication method');
        }

        return $results[0];
    }

    /**
     * {@inheritdoc}
     */
    public function &getAuthenticatedUser()
    {
        return $this->authenticated_user;
    }

    /**
     * {@inheritdoc}
     */
    public function &setAuthenticatedUser(AuthenticatedUserInterface $user = null)
    {
        $this->authenticated_user = $user;

        $this->triggerEvent('user_set', $user);

        return $this;
    }

    /**
     * @return AuthenticationResultInterface|null
     */
    public function getAuthenticatedWith()
    {
        return $this->authenticated_with;
    }

    /**
     * {@inheritdoc}
     */
    public function &setAuthenticatedWith(AuthenticationResultInterface $value)
    {
        $this->authenticated_with = $value;

        return $this;
    }

    /**
     * Trigger an internal event.
     *
     * @param  string $event_name
     * @param  array  $arguments
     * @return $this
     */
    private function &triggerEvent($event_name, ...$arguments)
    {
        $property_name = "on_{$event_name}";

        /** @var callable $handler */
        foreach ($this->$property_name as $handler) {
            call_user_func_array($handler, $arguments);
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function &onUserAuthenticated(callable $value)
    {
        $this->on_user_authenticated[] = $value;

        return $this;
    }

    public function &onUserAuthorized(callable $value)
    {
        $this->on_user_authorized[] = $value;

        return $this;
    }

    public function &onUserAuthorizationFailed(callable $value)
    {
        $this->on_user_authorization_failed[] = $value;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function &onUserSet(callable $value)
    {
        $this->on_user_set[] = $value;

        return $this;
    }

    public function &onUserDeauthenticated(callable $value)
    {
        $this->on_user_deauthenticated[] = $value;

        return $this;
    }

    /**
     * Kept for backward compatibility reasons. Will be removed.
     *
     * {@inheritdoc}
     */
    public function &setOnAuthenciatedUserChanged(callable $value = null)
    {
        if (empty($value)) {
            throw new InvalidArgumentException('Value needs to be a callable.');
        }

        return $this->onUserSet($value);
    }
}


<?php

/*
 * This file is part of the Active Collab Authentication project.
 *
 * (c) A51 doo <info@activecollab.com>. All rights reserved.
 */

namespace ActiveCollab\Authentication\Adapter;

use ActiveCollab\Authentication\AuthenticatedUser\AuthenticatedUserInterface;
use ActiveCollab\Authentication\AuthenticatedUser\RepositoryInterface as UserRepositoryInterface;
use ActiveCollab\Authentication\AuthenticationResult\AuthenticationResultInterface;
use ActiveCollab\Authentication\AuthenticationResult\Transport\Authentication\AuthenticationTransport;
use ActiveCollab\Authentication\AuthenticationResult\Transport\Deauthentication\DeauthenticationTransport;
use ActiveCollab\Authentication\Exception\InvalidTokenException;
use ActiveCollab\Authentication\Token\RepositoryInterface as TokenRepositoryInterface;
use ActiveCollab\Authentication\Token\TokenInterface;
use InvalidArgumentException;
use Psr\Http\Message\ServerRequestInterface;

/**
 * @package ActiveCollab\Authentication\Adapter
 */
class TokenBearerAdapter extends Adapter
{
    /**
     * @var UserRepositoryInterface
     */
    private $user_repository;

    /**
     * @var TokenRepositoryInterface
     */
    private $token_repository;

    /**
     * @param UserRepositoryInterface  $user_repository
     * @param TokenRepositoryInterface $token_repository
     */
    public function __construct(UserRepositoryInterface $user_repository, TokenRepositoryInterface $token_repository)
    {
        $this->user_repository = $user_repository;
        $this->token_repository = $token_repository;
    }

    /**
     * {@inheritdoc}
     */
    public function initialize(ServerRequestInterface $request)
    {
        if (!$request->hasHeader('Authorization')) {
            return null;
        }

        $authorization = $request->getHeaderLine('Authorization');

        if (empty($authorization) || substr($authorization, 0, 6) !== 'Bearer') {
            return null;
        }

        $token_id = trim(substr($authorization, 7));

        if ($token_id === null || $token_id === '') {
            throw new InvalidTokenException();
        }

        if ($token = $this->token_repository->getById($token_id)) {
            if ($user = $token->getAuthenticatedUser($this->user_repository)) {
                $this->token_repository->recordUsageByToken($token);

                return new AuthenticationTransport($this, $user, $token);
            }
        }

        throw new InvalidTokenException();
    }

    /**
     * {@inheritdoc}
     */
    public function authenticate(AuthenticatedUserInterface $authenticated_user, array $credentials = [])
    {
        return $this->token_repository->issueToken($authenticated_user, $credentials);
    }

    /**
     * {@inheritdoc}
     */
    public function terminate(AuthenticationResultInterface $authenticated_with)
    {
        if (!$authenticated_with instanceof TokenInterface) {
            throw new InvalidArgumentException('Instance is not a token');
        }

        $this->token_repository->terminateToken($authenticated_with);

        return new DeauthenticationTransport($this);
    }
}


<?php

/**
 * This file is part of plumphp/plum.
 *
 * (c) Florian Eckerstorfer <florian@eckerstorfer.co>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Plum\Plum\Pipe;

use InvalidArgumentException;
use Plum\Plum\Filter\CallbackFilter;
use Plum\Plum\Filter\FilterInterface;

/**
 * FilterPipe.
 *
 * @author    Florian Eckerstorfer
 * @copyright 2014-2016 Florian Eckerstorfer
 */
class FilterPipe extends AbstractPipe
{
    /**
     * @param FilterInterface|callable|array $element
     *
     * @return FilterPipe
     */
    public static function createFilter($element)
    {
        if (is_callable($element)) {
            $filter = new CallbackFilter($element);
        } elseif (is_array($element) && isset($element['filter']) && is_callable($element['filter'])) {
            $filter = new CallbackFilter($element['filter']);
            unset($element['filter']);
        } elseif (is_array($element) && isset($element['filter']) && $element['filter'] instanceof FilterInterface) {
            $filter = $element['filter'];
            unset($element['filter']);
        } elseif ($element instanceof FilterInterface) {
            $filter = $element;
        } else {
            throw new InvalidArgumentException('Workflow::addFilter() must be called with either an instance of '.
                                               '"Plum\Plum\Filter\FilterInterface" or an array that contains '.
                                               '"filter".');
        }

        $pipe         = new self($element);
        $pipe->filter = $filter;

        return $pipe;
    }
}


<?php

/*
 * This file is part of the Active Collab Authentication project.
 *
 * (c) A51 doo <info@activecollab.com>. All rights reserved.
 */

namespace ActiveCollab\Authentication\Authorizer;

use ActiveCollab\Authentication\AuthenticatedUser\AuthenticatedUserInterface;
use ActiveCollab\Authentication\AuthenticatedUser\RepositoryInterface;
use ActiveCollab\Authentication\Authorizer\ExceptionAware\ExceptionHandler\ExceptionHandlerInterface;
use ActiveCollab\Authentication\Exception\RuntimeException;
use ActiveCollab\Authentication\Exception\UserNotFoundException;
use Google_Client;

/**
 * @package ActiveCollab\Authentication\Authorizer
 */
class GoogleAuthorizer extends Authorizer
{
    use CredentialFieldsCheckTrait;

    /**
     * @var RepositoryInterface
     */
    private $user_repository;

    /**
     * @var Google_Client
     */
    private $google_client;

    /**
     * @var string
     */
    private $client_id;

    /**
     * @var array
     */
    private $user_profile;

    /**
     * GoogleAuthorizer constructor.
     *
     * @param RepositoryInterface            $user_repository
     * @param Google_Client                  $google_client
     * @param                                $client_id
     * @param ExceptionHandlerInterface|null $exception_handler
     */
    public function __construct(RepositoryInterface $user_repository, Google_Client $google_client, $client_id, ExceptionHandlerInterface $exception_handler = null)
    {
        $this->user_repository = $user_repository;
        $this->google_client = $google_client;
        $this->client_id = $client_id;
        $this->user_profile = [];
        $this->setExceptionHandler($exception_handler);
    }

    /**
     * Credentials should be in array format with keys: token and username.
     * Example: ['token' => '123abc', 'username' => 'john.doe.123@gmail.com'].
     *
     * {@inheritdoc}
     */
    public function verifyCredentials(array $credentials)
    {
        $this->verifyRequiredFields($credentials, ['token', 'username']);

        $token = $credentials['token'];
        $username = $credentials['username'];

        $payload = $this->google_client->verifyIdToken($token)->getAttributes()['payload'];
        $this->verifyGoogleProfile($payload, $username);
        $this->user_profile = $payload;

        $user = $this->user_repository->findByUsername($username);
        $this->verifyUser($user);

        return $user;
    }

    /**
     * @return array
     */
    public function getUserProfile()
    {
        return $this->user_profile;
    }

    /**
     * @param array  $payload
     * @param string $username
     */
    private function verifyGoogleProfile(array $payload, $username)
    {
        if ($this->client_id !== $payload['aud']) {
            throw new RuntimeException('Unrecognized google_client');
        }

        if (!in_array($payload['iss'], $this->getDomains())) {
            throw new RuntimeException('Wrong issuer');
        }

        if ($username !== $payload['email']) {
            throw new RuntimeException('Email is not verified by Google');
        }
    }

    /**
     * @param AuthenticatedUserInterface|null $user
     */
    private function verifyUser(AuthenticatedUserInterface $user = null)
    {
        if (!$user || !$user->canAuthenticate()) {
            throw new UserNotFoundException();
        }
    }

    /**
     * @return array
     */
    private function getDomains()
    {
        return ['accounts.google.com', 'https://accounts.google.com'];
    }
}