Passed
Push — master ( 6361e3...e88489 )
by Dmitry
02:04
created

General::get_nodes_from_request()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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