Subversion Repositories php-qbpwcf

Rev

Rev 3 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
namespace Ratchet\Server;
3
use Ratchet\MessageComponentInterface;
4
use Ratchet\ConnectionInterface;
5
 
6
/**
7
 * An app to go on a server stack to pass a policy file to a Flash socket
8
 * Useful if you're using Flash as a WebSocket polyfill on IE
9
 * Be sure to run your server instance on port 843
10
 * By default this lets accepts everything, make sure you tighten the rules up for production
11
 * @final
12
 * @link http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html
13
 * @link http://learn.adobe.com/wiki/download/attachments/64389123/CrossDomain_PolicyFile_Specification.pdf?version=1
14
 * @link view-source:http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd
15
 */
16
class FlashPolicy implements MessageComponentInterface {
17
 
18
    /**
19
     * Contains the root policy node
20
     * @var string
21
     */
22
    protected $_policy = '<?xml version="1.0"?><!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"><cross-domain-policy></cross-domain-policy>';
23
 
24
    /**
25
     * Stores an array of allowed domains and their ports
26
     * @var array
27
     */
28
    protected $_access = array();
29
 
30
    /**
31
     * @var string
32
     */
33
    protected $_siteControl = '';
34
 
35
    /**
36
     * @var string
37
     */
38
    protected $_cache = '';
39
 
40
    /**
41
     * @var string
42
     */
43
    protected $_cacheValid = false;
44
 
45
    /**
46
     * Add a domain to an allowed access list.
47
     *
48
     * @param string $domain Specifies a requesting domain to be granted access. Both named domains and IP
49
     * addresses are acceptable values. Subdomains are considered different domains. A wildcard (*) can
50
     * be used to match all domains when used alone, or multiple domains (subdomains) when used as a
51
     * prefix for an explicit, second-level domain name separated with a dot (.)
52
     * @param string $ports A comma-separated list of ports or range of ports that a socket connection
53
     * is allowed to connect to. A range of ports is specified through a dash (-) between two port numbers.
54
     * Ranges can be used with individual ports when separated with a comma. A single wildcard (*) can
55
     * be used to allow all ports.
56
     * @param bool $secure
57
     * @throws \UnexpectedValueException
58
     * @return FlashPolicy
59
     */
60
    public function addAllowedAccess($domain, $ports = '*', $secure = false) {
61
        if (!$this->validateDomain($domain)) {
62
           throw new \UnexpectedValueException('Invalid domain');
63
        }
64
 
65
        if (!$this->validatePorts($ports)) {
66
           throw new \UnexpectedValueException('Invalid Port');
67
        }
68
 
69
        $this->_access[]   = array($domain, $ports, (boolean)$secure);
70
        $this->_cacheValid = false;
71
 
72
        return $this;
73
    }
74
 
75
    /**
76
     * Removes all domains from the allowed access list.
77
     * 
78
     * @return \Ratchet\Server\FlashPolicy
79
     */
80
    public function clearAllowedAccess() {
81
        $this->_access      = array();
82
        $this->_cacheValid = false;
83
 
84
        return $this;
85
    }
86
 
87
    /**
88
     * site-control defines the meta-policy for the current domain. A meta-policy specifies acceptable
89
     * domain policy files other than the master policy file located in the target domain's root and named
90
     * crossdomain.xml.
91
     *
92
     * @param string $permittedCrossDomainPolicies
93
     * @throws \UnexpectedValueException
94
     * @return FlashPolicy
95
     */
96
    public function setSiteControl($permittedCrossDomainPolicies = 'all') {
97
        if (!$this->validateSiteControl($permittedCrossDomainPolicies)) {
98
            throw new \UnexpectedValueException('Invalid site control set');
99
        }
100
 
101
        $this->_siteControl = $permittedCrossDomainPolicies;
102
        $this->_cacheValid  = false;
103
 
104
        return $this;
105
    }
106
 
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function onOpen(ConnectionInterface $conn) {
111
    }
112
 
113
    /**
114
     * {@inheritdoc}
115
     */
116
    public function onMessage(ConnectionInterface $from, $msg) {
117
        if (!$this->_cacheValid) {
118
            $this->_cache      = $this->renderPolicy()->asXML();
119
            $this->_cacheValid = true;
120
        }
121
 
122
        $from->send($this->_cache . "\0");
123
        $from->close();
124
    }
125
 
126
    /**
127
     * {@inheritdoc}
128
     */
129
    public function onClose(ConnectionInterface $conn) {
130
    }
131
 
132
    /**
133
     * {@inheritdoc}
134
     */
135
    public function onError(ConnectionInterface $conn, \Exception $e) {
136
        $conn->close();
137
    }
138
 
139
    /**
140
     * Builds the crossdomain file based on the template policy
141
     *
142
     * @throws \UnexpectedValueException
143
     * @return \SimpleXMLElement
144
     */
145
    public function renderPolicy() {
146
        $policy = new \SimpleXMLElement($this->_policy);
147
 
148
        $siteControl = $policy->addChild('site-control');
149
 
150
        if ($this->_siteControl == '') {
151
            $this->setSiteControl();
152
        }
153
 
154
        $siteControl->addAttribute('permitted-cross-domain-policies', $this->_siteControl);
155
 
156
        if (empty($this->_access)) {
157
            throw new \UnexpectedValueException('You must add a domain through addAllowedAccess()');
158
        }
159
 
160
        foreach ($this->_access as $access) {
161
            $tmp = $policy->addChild('allow-access-from');
162
            $tmp->addAttribute('domain', $access[0]);
163
            $tmp->addAttribute('to-ports', $access[1]);
164
            $tmp->addAttribute('secure', ($access[2] === true) ? 'true' : 'false');
165
        }
166
 
167
        return $policy;
168
    }
169
 
170
    /**
171
     * Make sure the proper site control was passed
172
     *
173
     * @param string $permittedCrossDomainPolicies
174
     * @return bool
175
     */
176
    public function validateSiteControl($permittedCrossDomainPolicies) {
177
        //'by-content-type' and 'by-ftp-filename' are not available for sockets
178
        return (bool)in_array($permittedCrossDomainPolicies, array('none', 'master-only', 'all'));
179
    }
180
 
181
    /**
182
     * Validate for proper domains (wildcards allowed)
183
     *
184
     * @param string $domain
185
     * @return bool
186
     */
187
    public function validateDomain($domain) {
188
        return (bool)preg_match("/^((http(s)?:\/\/)?([a-z0-9-_]+\.|\*\.)*([a-z0-9-_\.]+)|\*)$/i", $domain);
189
    }
190
 
191
    /**
192
     * Make sure valid ports were passed
193
     *
194
     * @param string $port
195
     * @return bool
196
     */
197
    public function validatePorts($port) {
198
        return (bool)preg_match('/^(\*|(\d+[,-]?)*\d+)$/', $port);
199
    }
200
}