Passed
Push — master ( 5bfe47...314d48 )
by Dmitry
01:58
created

General::validate_request()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 19
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 5
eloc 8
c 2
b 0
f 1
nc 1
nop 1
dl 0
loc 19
rs 8.8571
1
<?php
2
3
/**
4
 *      General class for all request types
5
 *
6
 *      @package php_EasyPay
7
 *      @version 1.1
8
 *      @author Dmitry Shovchko <[email protected]>
9
 *
10
 */
11
12
namespace EasyPay\Provider31\Request;
13
14
use EasyPay\Log as Log;
15
use EasyPay\Exception;
16
use EasyPay\Key as Key;
17
18
class General
19
{
20
        /**
21
         *      @var string raw request
22
         */
23
        protected $raw_request;
24
25
        /**
26
         *      @var string 'DateTime' node
27
         */
28
        protected $DateTime;
29
30
        /**
31
         *      @var string 'Sign' node
32
         */
33
        protected $Sign;
34
35
        /**
36
         *      @var string 'Operation' type
37
         */
38
        protected $Operation;
39
40
        /**
41
         *      @var string 'ServiceId' node
42
         */
43
        protected $ServiceId;
44
45
        /**
46
         *      @var array list of possible operations
47
         */
48
        protected $operations = array('Check','Payment','Confirm','Cancel');
49
50
        /**
51
         *      General constructor
52
         *
53
         *      @param string $raw Raw request data
54
         */
55
        public function __construct($raw)
56
        {
57
                $this->raw_request = strval($raw);
58
59
                $this->parse_request_data();
60
        }
61
62
        /**
63
         *      Get DateTime
64
         *
65
         *      @return string
66
         */
67
        public function DateTime()
68
        {
69
                return $this->DateTime;
70
        }
71
72
        /**
73
         *      Get Sign
74
         *
75
         *      @return string
76
         */
77
        public function Sign()
78
        {
79
                return $this->Sign;
80
        }
81
82
        /**
83
         *      Get Operation type
84
         *
85
         *      @return string
86
         */
87
        public function Operation()
88
        {
89
                return $this->Operation;
90
        }
91
92
        /**
93
         *      Get ServiceId
94
         *
95
         *      @return string
96
         */
97
        public function ServiceId()
98
        {
99
                return $this->ServiceId;
100
        }
101
102
        /**
103
         *      Parse xml-request, which was previously "extracted" from the body of the http request
104
         *
105
         *      @throws Exception\Structure
106
         */
107
        protected function parse_request_data()
108
        {
109
                if (empty($this->raw_request))
110
                {
111
                        throw new Exception\Structure('An empty xml request', -50);
112
                }
113
114
                libxml_use_internal_errors(true);
115
                $doc = new \DOMDocument();
116
                if ( ! $doc->loadXML($this->raw_request))
117
                {
118
                        foreach(libxml_get_errors() as $e){
119
                                Log::instance()->error($e->message);
120
                        }
121
                        throw new Exception\Structure('The wrong XML is received', -51);
122
                }
123
124
                // process <Request> group
125
                $r = $this->getNodes($doc, 'Request');
126
127
                if (count($r) < 1)
128
                {
129
                        throw new Exception\Structure('The xml-query does not contain any element Request!', -52);
130
                }
131
132
                foreach ($r[0]->childNodes as $child)
133
                {
134
                        if ($child->nodeName == 'DateTime')
135
                        {
136
                                $this->parse_request_node($child, 'DateTime');
137
                        }
138
                        elseif ($child->nodeName == 'Sign')
139
                        {
140
                                $this->parse_request_node($child, 'Sign');
141
                        }
142
                        elseif (in_array($child->nodeName, $this->operations))
143
                        {
144
                                if ( ! isset($this->Operation))
145
                                {
146
                                        $this->Operation = $child->nodeName;
147
                                }
148
                                else
149
                                {
150
                                        throw new Exception\Structure('There is more than one Operation type element in the xml-query!', -53);
151
                                }
152
                        }
153
                }
154
155
                if ( ! isset($this->Operation))
156
                {
157
                        throw new Exception\Structure('There is no Operation type element in the xml request!', -55);
158
                }
159
160
                // process <Operation> group
161
                $r = $this->getNodes($doc, $this->Operation);
162
163
                foreach ($r[0]->childNodes as $child)
164
                {
165
                        if ($child->nodeName == 'ServiceId')
166
                        {
167
                                $this->parse_request_node($child, 'ServiceId');
168
                        }
169
                }
170
        }
171
172
        /**
173
         *      Parse node of request
174
         *
175
         *      @param \DOMNode $n
176
         *      @param string $name
177
         *
178
         *      @throws Exception\Structure
179
         */
180
        protected function parse_request_node($n, $name)
181
        {
182
                if ( ! isset($this->$name))
183
                {
184
                        $this->$name = $n->nodeValue;
185
                }
186
                else
187
                {
188
                        throw new Exception\Structure('There is more than one '.$name.' element in the xml-query!', -56);
189
                }
190
        }
191
192
        /**
193
         *      "Rough" validation of the received xml request
194
         *
195
         *      @param array $options
196
         *      @throws Exception\Data
197
         *      @throws Exception\Structure
198
         */
199
        public function validate_request($options)
200
        {
201
                if ( ! isset($this->DateTime))
202
                {
203
                        throw new Exception\Structure('There is no DateTime element in the xml request!', -57);
204
                }
205
                if ( ! isset($this->Sign))
206
                {
207
                        throw new Exception\Structure('There is no Sign element in the xml request!', -57);
208
                }
209
                if ( ! isset($this->ServiceId))
210
                {
211
                        throw new Exception\Structure('There is no ServiceId element in the xml request!', -57);
212
                }
213
214
                // compare received value ServiceId with option ServiceId
215
                if (intval($options['ServiceId']) != intval($this->ServiceId))
216
                {
217
                        throw new Exception\Data('This request is not for our ServiceId!', -58);
218
                }
219
        }
220
221
        /**
222
         *      Verify signature of request
223
         *
224
         *      @param array $options
225
         *      @throws Exception\Runtime
226
         *      @throws Exception\Sign
227
         */
228
        public function verify_sign($options)
229
        {
230
                if (!isset($options['UseSign']) || ($options['UseSign'] === false))
231
                {
232
                        return null;
233
                }
234
                if ( ! isset($options['EasySoftPKey']))
235
                {
236
                        throw new Exception\Runtime('The parameter EasySoftPKey is not set!', -94);
237
                }
238
                $pkeyid = (new Key())->get($options['EasySoftPKey'], 'public');
239
240
                $pub_key = openssl_pkey_get_public($pkeyid);
241
                if ($pub_key === FALSE)
242
                {
243
                        throw new Exception\Runtime('Can not extract the public key from certificate!', -97);
244
                }
245
                $bin_sign = pack("H*", $this->Sign);
246
                $xml = str_replace($this->Sign, '', $this->raw_request);
247
                $check = openssl_verify($xml, $bin_sign, $pub_key);
248
                if ($check == -1)
249
                {
250
                        throw new Exception\Sign('Error verify signature of request!', -96);
251
                }
252
                elseif ($check == 0)
253
                {
254
                        throw new Exception\Sign('Signature of request is incorrect!', -95);
255
                }
256
        }
257
258
        /**
259
         *      Selects nodes by name
260
         *
261
         *      @param \DOMDocument $dom
262
         *      @param string $name
263
         *      @param array $ret
264
         *
265
         *      @return array nodes with the name
266
         */
267
        protected function getNodes($dom, $name, $ret=array())
268
        {
269
                foreach($dom->childNodes as $child)
270
                {
271
                        if ($child->nodeName == $name)
272
                        {
273
                                array_push($ret, $child);
274
                        }
275
                        else
276
                        {
277
                                if (count($child->childNodes) > 0)
278
                                {
279
                                        $ret = $this->getNodes($child, $name, $ret);
280
                                }
281
                        }
282
                }
283
284
                return $ret;
285
        }
286
287
}
288