Passed
Push — master ( 76cacd...120832 )
by Dmitry
01:58
created

General::check_presence_request()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 5
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
        $this->check_presence_request();
111
112
        // process <Request> group
113
        $r = $this->get_nodes_from_request('Request');
114
115
        if (count($r) < 1)
116
        {
117
            throw new Exception\Structure('The xml-query does not contain any element Request!', -52);
118
        }
119
120
        foreach ($r[0]->childNodes as $child)
121
        {
122
            $this->check_and_parse_request_node($child, 'DateTime');
123
            $this->check_and_parse_request_node($child, 'Sign');
124
125
            if (in_array($child->nodeName, $this->operations))
126
            {
127
                if ( ! isset($this->Operation))
128
                {
129
                    $this->Operation = $child->nodeName;
130
                }
131
                else
132
                {
133
                    throw new Exception\Structure('There is more than one Operation type element in the xml-query!', -53);
134
                }
135
            }
136
        }
137
138
        if ( ! isset($this->Operation))
139
        {
140
            throw new Exception\Structure('There is no Operation type element in the xml request!', -55);
141
        }
142
143
        // process <Operation> group
144
        $r = $this->get_nodes_from_request($this->Operation);
145
146
        foreach ($r[0]->childNodes as $child)
147
        {
148
            $this->check_and_parse_request_node($child, 'ServiceId');
149
        }
150
    }
151
152
    /**
153
     *      Check if presence request
154
     *
155
     *      @throws Exception\Structure
156
     */
157
    protected function check_presence_request()
158
    {
159
        if (empty($this->raw_request))
160
        {
161
            throw new Exception\Structure('An empty xml request', -50);
162
        }
163
    }
164
165
    /**
166
     *      Get group of nodes from XML-request
167
     *
168
     *      @param string $name
169
     *      @return array
170
     */
171
    protected function get_nodes_from_request($name)
172
    {
173
        libxml_use_internal_errors(true);
174
        $doc = new \DOMDocument();
175
        if ( ! $doc->loadXML($this->raw_request))
176
        {
177
            foreach(libxml_get_errors() as $e){
178
                Log::instance()->error($e->message);
179
            }
180
            throw new Exception\Structure('The wrong XML is received', -51);
181
        }
182
183
        return $this->getNodes($doc, $name);
184
    }
185
186
    /**
187
     *      Check if present node of request and then parse it
188
     *
189
     *      @param \DOMNode $n
190
     *      @param string $name
191
     */
192
    protected function check_and_parse_request_node($n, $name)
193
    {
194
        if ($n->nodeName == $name)
195
        {
196
            $this->parse_request_node($n, $name);
197
        }
198
    }
199
200
    /**
201
     *      Parse node of request
202
     *
203
     *      @param \DOMNode $n
204
     *      @param string $name
205
     *
206
     *      @throws Exception\Structure
207
     */
208
    protected function parse_request_node($n, $name)
209
    {
210
        if ( ! isset($this->$name))
211
        {
212
            $this->$name = $n->nodeValue;
213
        }
214
        else
215
        {
216
            throw new Exception\Structure('There is more than one '.$name.' element in the xml-query!', -56);
217
        }
218
    }
219
220
    /**
221
     *      "Rough" validation of the received xml request
222
     *
223
     *      @param array $options
224
     *      @throws Exception\Data
225
     *      @throws Exception\Structure
226
     */
227
    public function validate_request($options)
228
    {
229
        $this->validate_element('DateTime');
230
        $this->validate_element('Sign');
231
        $this->validate_element('ServiceId');
232
233
        // compare received value ServiceId with option ServiceId
234
        if (intval($options['ServiceId']) != intval($this->ServiceId))
235
        {
236
            throw new Exception\Data('This request is not for our ServiceId!', -58);
237
        }
238
    }
239
240
    /**
241
     *      Validation of xml-element
242
     *
243
     *      @param string $name
244
     */
245
    public function validate_element($name)
246
    {
247
        if ( ! isset($this->$name))
248
        {
249
            throw new Exception\Structure('There is no '.$name.' element in the xml request!', -57);
250
        }
251
    }
252
253
    /**
254
     *      Verify signature of request
255
     *
256
     *      @param array $options
257
     */
258
    public function verify_sign($options)
259
    {
260
        if (isset($options['UseSign']) && ($options['UseSign'] === true))
261
        {
262
            $this->check_verify_sign_result(
263
                $result = (new OpenSSL())->verify(
264
                    str_replace($this->Sign, '', $this->raw_request),
265
                    pack("H*", $this->Sign),
266
                    (new OpenSSL())->get_pub_key($this->get_pub_key($options))
267
                )
268
            );
269
        }
270
    }
271
272
    /**
273
     *      load file with easysoft public key
274
     *
275
     *      @param array $options
276
     *      @throws Exception\Runtime
277
     *      @return string
278
     */
279
    protected function get_pub_key($options)
280
    {
281
        if ( ! isset($options['EasySoftPKey']))
282
        {
283
            throw new Exception\Runtime('The parameter EasySoftPKey is not set!', -94);
284
        }
285
286
        return (new Key())->get($options['EasySoftPKey'], 'public');
287
    }
288
289
    /**
290
     *      check result of openssl verify signature
291
     *
292
     *      @param integer $result
293
     *      @throws Exception\Sign
294
     */
295
    protected function check_verify_sign_result($result)
296
    {
297
        if ($result == -1)
298
        {
299
            throw new Exception\Sign('Error verify signature of request!', -96);
300
        }
301
        elseif ($result == 0)
302
        {
303
            throw new Exception\Sign('Signature of request is incorrect!', -95);
304
        }
305
    }
306
307
    /**
308
     *      Selects nodes by name
309
     *
310
     *      @param \DOMDocument $dom
311
     *      @param string $name
312
     *      @param array $ret
313
     *
314
     *      @return array nodes with the name
315
     */
316
    protected function getNodes($dom, $name, $ret=array())
317
    {
318
        foreach($dom->childNodes as $child)
319
        {
320
            if ($child->nodeName == $name)
321
            {
322
                array_push($ret, $child);
323
            }
324
            else
325
            {
326
                if (count($child->childNodes) > 0)
327
                {
328
                    $ret = $this->getNodes($child, $name, $ret);
329
                }
330
            }
331
        }
332
333
        return $ret;
334
    }
335
336
}
337