Completed
Branch master (5f7358)
by Дроботун
04:24
created

vtapi3.vtapi3   F

Complexity

Total Complexity 150

Size/Duplication

Total Lines 1245
Duplicated Lines 31.81 %

Importance

Changes 0
Metric Value
eloc 550
dl 396
loc 1245
rs 2
c 0
b 0
f 0
wmc 150

40 Methods

Rating   Name   Duplication   Size   Complexity  
A VirusTotalAPIUrls.get_comments() 0 32 4
A VirusTotalAPIUrls.put_votes() 0 35 5
A VirusTotalAPIFiles.get_relationship() 0 31 4
A VirusTotalAPIUrls.get_votes() 0 32 4
A VirusTotalAPIIPAddresses.get_report() 27 27 4
A VirusTotalAPIFiles.put_comments() 0 29 4
A VirusTotalAPIIPAddresses.get_comments() 30 30 4
B VirusTotalAPIFiles.upload() 0 39 8
A VirusTotalAPI.__init__() 0 15 1
A VirusTotalAPIFiles.put_votes() 0 33 5
A VirusTotalAPIUrls.get_relationship() 0 34 4
A VirusTotalAPIUrls.put_comments() 0 31 4
A VirusTotalAPIFiles.get_comments() 0 30 4
A VirusTotalAPIUrls.get_network_location() 0 29 4
A VirusTotalAPI.get_last_result() 0 7 1
A VirusTotalAPIUrls.analyse() 0 29 4
A VirusTotalAPIIPAddresses.get_relationship() 31 31 4
A VirusTotalAPIDomains.get_relationship() 31 31 4
A VirusTotalAPIFiles.get_votes() 0 30 4
A VirusTotalAPI.get_last_http_error() 0 7 1
A VirusTotalAPIFiles.get_report() 0 27 4
A VirusTotalAPI.get_version_api() 0 7 1
A VirusTotalAPIDomains.get_votes() 30 30 4
A VirusTotalAPIIPAddresses.put_comments() 29 29 4
B VirusTotalAPIFiles.get_file_id() 0 33 7
A VirusTotalAPIDomains.get_report() 27 27 4
A VirusTotalAPIIPAddresses.get_votes() 30 30 4
A VirusTotalAPIUrls.get_url_id_sha256() 0 11 1
A VirusTotalAPIAnalyses.get_report() 0 27 4
A VirusTotalAPIUrls.get_report() 0 29 4
A VirusTotalAPIFiles.get_upload_url() 0 24 4
A VirusTotalAPIUrls.get_url_id_base64() 0 11 1
A VirusTotalAPIIPAddresses.put_votes() 33 33 5
A VirusTotalAPIDomains.put_votes() 33 33 5
A VirusTotalAPIError.__init__() 0 5 1
A VirusTotalAPIFiles.analyse() 0 27 4
A VirusTotalAPIFiles.get_behaviours() 0 28 4
A VirusTotalAPIDomains.get_comments() 30 30 4
A VirusTotalAPIDomains.put_comments() 29 29 4
A VirusTotalAPIUrls.upload() 0 28 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like vtapi3.vtapi3 often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""The module describes classes that implement methods for accessing service API functions
2
   www.virustotai.com.
3
4
   More information: https://developers.virustotal.com/v3.0/reference#getting-started
5
6
   Author: Evgeny Drobotun (c) 2019
7
   License: MIT (https://github.com/drobotun/virustotalapi3/blob/master/LICENSE)
8
9
   Requirements:
10
      $ pip install requests
11
12
   Example usage:
13
14
   from vtapi3 import VirusTotalAPIFiles, VirusTotalAPIError
15
      ...
16
   vt_files = VirusTotalAPIFiles(<Insert API key string here>)
17
   try:
18
       result = vt_files.upload(<Insert faile name here>)
19
   except VirusTotalAPIError as err:
20
       print(err, err.err_code)
21
   else:
22
       if vt_files.get_last_http_error() == vt_files.HTTP_OK:
23
           result = json.loads(result)
24
           result = json.dumps(result, sort_keys=False, indent=4)
25
           print(result)
26
       else:
27
           print('HTTP Error [' + str(vt_files.get_last_http_error()) +']')
28
      ...
29
"""
30
import base64
31
import hashlib
32
import errno
33
import requests
34
35
36
class VirusTotalAPI:
37
    """A base class for subclasses that implement methods for working with files, URLs, domain
38
       names, and IP addresses.
39
40
       Attributes:
41
          base_url: The base URL for sending requests (str).
42
          headers: Request header containing API key (dict).
43
          timeout: Server response timeout. A tuple that includes a timeout value for 'connect' and
44
             a timeout value for 'read'. If specify a single timeout value, it will be applied to
45
             both timeout 'connect' and timeout 'read'.
46
          proxies: The Protocol and the URL of the proxy server (dict).
47
          _version_api: VirusTotal API version (str).
48
          _last_http_error: HTTP status code of last operation (int).
49
          _last_result: Result of the last execution of a subclass method of this class.
50
51
       Constants: HTTP error codes constants.
52
53
       Methods:
54
         get_version_api(): Return the API version values.
55
         get_last_http_error(): Return the HTTP status code of last operation.
56
         get_last_result(): Return the result of executing methods of subclasses of this class.
57
    """
58
59
    HTTP_OK = requests.codes['ok']
60
    HTTP_BAD_REQUEST_ERROR = requests.codes['bad_request']
61
    HTTP_AUTHENTICATION_REQUIRED_ERROR = requests.codes['unauthorized']
62
    HTTP_FORBIDDEN_ERROR = requests.codes['forbidden']
63
    HTTP_NOT_FOUND_ERROR = requests.codes['not_found']
64
    HTTP_ALREADY_EXISTS_ERROR = requests.codes['conflict']
65
    HTTP_QUOTA_EXCEEDED_ERROR = requests.codes['too_many_requests']
66
    HTTP_TRANSIENT_ERROR = requests.codes['service_unavailable']
67
68
    def __init__(self, api_key=None, timeout=None, proxies=None):
69
        """Inits VirusTotalAPI.
70
71
           Args:
72
              api_key: your API key to access the functions of the service VirusTotal (str).
73
              timeout: Server response timeout (int). Optional.
74
              proxies: The Protocol and the URL of the proxy server (dict). Optional.
75
        """
76
        self.base_url = 'https://www.virustotal.com/api/v3'
77
        self.headers = {'x-apikey' : api_key}
78
        self.timeout = timeout
79
        self.proxies = proxies
80
        self._version_api = 'version 3'
81
        self._last_http_error = None
82
        self._last_result = None
83
84
    def get_version_api(self):
85
        """Return the API version values.
86
87
           Return:
88
              String containing API version ('version 3').
89
        """
90
        return self._version_api
91
92
    def get_last_http_error(self):
93
        """Return the HTTP status code of last operation.
94
95
           Return:
96
              HTTP status code of last operation.
97
        """
98
        return self._last_http_error
99
100
    def get_last_result(self):
101
        """Return the result of executing methods of subclasses of this class.
102
103
           Return:
104
              Result of the last execution of a subclass method of this class.
105
        """
106
        return self._last_result
107
108
109
class VirusTotalAPIFiles(VirusTotalAPI):
110
    """The analysis new files and retrieving information about any file from the VirusTotal database
111
       methods are defined in the class.
112
113
       Methods:
114
          get_file_id(): Get SHA256, SHA1 or MD5 file identifier.
115
          upload(): Upload and analyse a file.
116
          get_upload_url(): Get a URL for uploading files larger than 32MB.
117
          get_report(): Retrieve information about a file.
118
          reanalyse(): Reanalyse a file already in VirusTotal.
119
          get_comments(): Retrieve comments for a file.
120
          put_comments(): Add a comment to a file.
121
          get_votes(): Retrieve votes for a file.
122
          put_votes(): Add a votes to a file.
123
          get_relationship(): Retrieve objects related to a file.
124
          get_behaviours(): Get the PCAP for the sandbox.
125
    """
126
127
    @staticmethod
128
    def get_file_id(file_path, hash_alg='sha256'):
129
        """Get SHA256, SHA1 or MD5 file identifier.
130
131
           Args:
132
              file_path: Path to the file to be scanned (str).
133
              hash: Necessary identifier ('sha256', 'sha1' or 'md5'). The default value is
134
                 'sha256'.
135
136
           Return:
137
              The SHA256, SHA1 or MD5 identifier of the file.
138
139
           Exception
140
              VirusTotalAPIError(File not found): In case the file is not found.
141
              VirusTotalAPIError(Permission error): In case do not have access rights to the file.
142
              VirusTotalAPIError(IO Error): If an IO error occurs during file operations.
143
        """
144
        buffer_size = 65536
145
        hasher = hashlib.new(hash_alg)
146
        try:
147
            with open(file_path, 'rb') as file:
148
                buffer = file.read(buffer_size)
149
                while len(buffer) > 0:
150
                    hasher.update(buffer)
151
                    buffer = file.read(buffer_size)
152
        except FileNotFoundError:
153
            raise VirusTotalAPIError('File not found', errno.ENOENT)
154
        except PermissionError:
155
            raise VirusTotalAPIError('Permission error', errno.EPERM)
156
        except OSError:
157
            raise VirusTotalAPIError('IO Error', errno.EIO)
158
        else:
159
            return hasher.hexdigest()
160
161
    def upload(self, file_path):
162
        """Upload and analyse a file.
163
164
        Args:
165
           file_path: Path to the file to be scanned (str).
166
167
        Return:
168
           The response from the server as a byte sequence.
169
170
        Exception
171
           VirusTotalAPIError(Connection error): In case of server connection errors.
172
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
173
           VirusTotalAPIError(File not found): In case the file you want to upload to the server is
174
              not found.
175
           VirusTotalAPIError(Permission error): In case do not have access rights to the file.
176
           VirusTotalAPIError(IO Error): If an IO error occurs during file operations.
177
        """
178
        self._last_http_error = None
179
        self._last_result = None
180
        api_url = self.base_url + '/files'
181
        try:
182
            with open(file_path, 'rb') as file:
183
                files = {'file': (file_path, file)}
184
                response = requests.post(api_url, headers=self.headers, files=files,
185
                                         timeout=self.timeout, proxies=self.proxies)
186
        except FileNotFoundError:
187
            raise VirusTotalAPIError('File not found', errno.ENOENT)
188
        except PermissionError:
189
            raise VirusTotalAPIError('Permission error', errno.EPERM)
190
        except requests.exceptions.Timeout:
191
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
192
        except requests.exceptions.ConnectionError:
193
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
194
        except OSError:
195
            raise VirusTotalAPIError('IO Error', errno.EIO)
196
        else:
197
            self._last_http_error = response.status_code
198
            self._last_result = response.content
199
            return response.content
200
201
    def get_upload_url(self):
202
        """Get a URL for uploading files larger than 32MB.
203
204
        Return:
205
           The response from the server as a byte sequence.
206
207
        Exception
208
           VirusTotalAPIError(Connection error): In case of server connection errors.
209
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
210
        """
211
        self._last_http_error = None
212
        self._last_result = None
213
        api_url = self.base_url + '/files/upload_url'
214
        try:
215
            response = requests.get(api_url, headers=self.headers,
216
                                    timeout=self.timeout, proxies=self.proxies)
217
        except requests.exceptions.Timeout:
218
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
219
        except requests.exceptions.ConnectionError:
220
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
221
        else:
222
            self._last_http_error = response.status_code
223
            self._last_result = response.content
224
            return response.content
225
226
    def get_report(self, file_id):
227
        """Retrieve information about a file.
228
229
        Args:
230
           file_id: SHA-256, SHA-1 or MD5 identifying the file (str).
231
232
       Return:
233
           The response from the server as a byte sequence.
234
235
        Exception
236
           VirusTotalAPIError(Connection error): In case of server connection errors.
237
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
238
        """
239
        self._last_http_error = None
240
        self._last_result = None
241
        api_url = self.base_url + '/files/' + file_id
242
        try:
243
            response = requests.get(api_url, headers=self.headers,
244
                                    timeout=self.timeout, proxies=self.proxies)
245
        except requests.exceptions.Timeout:
246
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
247
        except requests.exceptions.ConnectionError:
248
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
249
        else:
250
            self._last_http_error = response.status_code
251
            self._last_result = response.content
252
            return response.content
253
254
    def analyse(self, file_id):
255
        """Reanalyse a file already in VirusTotal.
256
257
        Args:
258
           file_id: SHA-256, SHA-1 or MD5 identifying the file (str).
259
260
        Return:
261
           The response from the server as a byte sequence.
262
263
        Exception
264
           VirusTotalAPIError(Connection error): In case of server connection errors.
265
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
266
        """
267
        self._last_http_error = None
268
        self._last_result = None
269
        api_url = self.base_url + '/files/' + file_id + '/analyse'
270
        try:
271
            response = requests.post(api_url, headers=self.headers,
272
                                     timeout=self.timeout, proxies=self.proxies)
273
        except requests.exceptions.Timeout:
274
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
275
        except requests.exceptions.ConnectionError:
276
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
277
        else:
278
            self._last_http_error = response.status_code
279
            self._last_result = response.content
280
            return response.content
281
282
    def get_comments(self, file_id, limit=10, cursor='""'):
283
        """Retrieve comments for a file.
284
285
        Args:
286
           file_id: SHA-256, SHA-1 or MD5 identifying the file (str).
287
           limit: Maximum number of comments to retrieve (int). The default value is 10.
288
           cursor: Continuation cursor (str). The default value is ''.
289
290
        Return:
291
           The response from the server as a byte sequence.
292
293
        Exception
294
           VirusTotalAPIError(Connection error): In case of server connection errors.
295
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
296
        """
297
        self._last_http_error = None
298
        self._last_result = None
299
        query_string = {'limit': str(limit), 'cursor': cursor}
300
        api_url = self.base_url + '/files/' + file_id + '/comments'
301
        try:
302
            response = requests.get(api_url, headers=self.headers, params=query_string,
303
                                    timeout=self.timeout, proxies=self.proxies)
304
        except requests.exceptions.Timeout:
305
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
306
        except requests.exceptions.ConnectionError:
307
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
308
        else:
309
            self._last_http_error = response.status_code
310
            self._last_result = response.content
311
            return response.content
312
313
    def put_comments(self, file_id, text):
314
        """Add a comment to a file.
315
316
        Args:
317
           file_id: SHA-256, SHA-1 or MD5 identifying the file (str).
318
           text: Text of the comment (str).
319
320
        Return:
321
           The response from the server as a byte sequence.
322
323
        Exception
324
           VirusTotalAPIError(Connection error): In case of server connection errors.
325
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
326
        """
327
        self._last_http_error = None
328
        self._last_result = None
329
        comments = {"data": {'type': 'comment', 'attributes': {'text': text}}}
330
        api_url = self.base_url + '/files/' + file_id + '/comments'
331
        try:
332
            response = requests.post(api_url, headers=self.headers, json=comments,
333
                                     timeout=self.timeout, proxies=self.proxies)
334
        except requests.exceptions.Timeout:
335
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
336
        except requests.exceptions.ConnectionError:
337
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
338
        else:
339
            self._last_http_error = response.status_code
340
            self._last_result = response.content
341
            return response.content
342
343
    def get_votes(self, file_id, limit=10, cursor='""'):
344
        """Retrieve votes for a file.
345
346
        Args:
347
           file_id: SHA-256, SHA-1 or MD5 identifying the file (str).
348
           limit: Maximum number of comments to retrieve (int). The default value is 10.
349
           cursor: Continuation cursor (str). The default value is ''.
350
351
        Return:
352
           The response from the server as a byte sequence.
353
354
        Exception
355
           VirusTotalAPIError(Connection error): In case of server connection errors.
356
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
357
        """
358
        self._last_http_error = None
359
        self._last_result = None
360
        query_string = {'limit': str(limit), 'cursor': cursor}
361
        api_url = self.base_url + '/files/' + file_id + '/votes'
362
        try:
363
            response = requests.get(api_url, headers=self.headers, params=query_string,
364
                                    timeout=self.timeout, proxies=self.proxies)
365
        except requests.exceptions.Timeout:
366
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
367
        except requests.exceptions.ConnectionError:
368
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
369
        else:
370
            self._last_http_error = response.status_code
371
            self._last_result = response.content
372
            return response.content
373
374
    def put_votes(self, file_id, malicious=False):
375
        """Add a votes to a file.
376
377
        Args:
378
           file_id: SHA-256, SHA-1 or MD5 identifying the file (str).
379
           malicious: Determines a malicious (True) or harmless (False) file (bool).
380
381
        Return:
382
           The response from the server as a byte sequence.
383
384
        Exception
385
           VirusTotalAPIError(Connection error): In case of server connection errors.
386
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
387
        """
388
        self._last_http_error = None
389
        self._last_result = None
390
        if malicious:
391
            verdict = 'malicious'
392
        else:
393
            verdict = 'harmless'
394
        votes = {'data': {'type': 'vote', 'attributes': {'verdict': verdict}}}
395
        api_url = self.base_url + '/files/' + file_id + '/votes'
396
        try:
397
            response = requests.post(api_url, headers=self.headers, json=votes,
398
                                     timeout=self.timeout, proxies=self.proxies)
399
        except requests.exceptions.Timeout:
400
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
401
        except requests.exceptions.ConnectionError:
402
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
403
        else:
404
            self._last_http_error = response.status_code
405
            self._last_result = response.content
406
            return response.content
407
408
    def get_relationship(self, file_id, relationship='/behaviours', limit=10, cursor='""'):
409
        """Retrieve objects related to a file.
410
411
        Args:
412
           file_id: SHA-256, SHA-1 or MD5 identifying the file (str).
413
           relationship: Relationship name (str). The default value is "/behaviours".
414
           limit: Maximum number of comments to retrieve (int). The default value is 10.
415
           cursor: Continuation cursor (str). The default value is ''.
416
417
        Return:
418
           The response from the server as a byte sequence.
419
420
        Exception
421
           VirusTotalAPIError(Connection error): In case of server connection errors.
422
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
423
        """
424
        self._last_http_error = None
425
        self._last_result = None
426
        query_string = {'limit': str(limit), 'cursor': cursor}
427
        api_url = self.base_url + '/files/' + file_id + relationship
428
        try:
429
            response = requests.get(api_url, headers=self.headers, params=query_string,
430
                                    timeout=self.timeout, proxies=self.proxies)
431
        except requests.exceptions.Timeout:
432
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
433
        except requests.exceptions.ConnectionError:
434
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
435
        else:
436
            self._last_http_error = response.status_code
437
            self._last_result = response.content
438
            return response.content
439
440
    def get_behaviours(self, sandbox_id):
441
        """Get the PCAP for the sandbox.
442
443
        Args:
444
           sandbox_id: Identifier obtained using the 'get_relationship' method with
445
              the value of the 'relationship' argument equal to 'behaviours' (str).
446
447
        Return:
448
           The response from the server as a byte sequence.
449
450
        Exception
451
           VirusTotalAPIError(Connection error): In case of server connection errors.
452
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
453
        """
454
        self._last_http_error = None
455
        self._last_result = None
456
        api_url = self.base_url + '/file_behaviours/' + sandbox_id + '/pcap'
457
        try:
458
            response = requests.get(api_url, headers=self.headers,
459
                                    timeout=self.timeout, proxies=self.proxies)
460
        except requests.exceptions.Timeout:
461
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
462
        except requests.exceptions.ConnectionError:
463
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
464
        else:
465
            self._last_http_error = response.status_code
466
            self._last_result = response.content
467
            return response.content
468
469
470
class VirusTotalAPIUrls(VirusTotalAPI):
471
    """The analysis new URLs and retrieving information about any URLs from the VirusTotal database
472
       methods are defined in the class.
473
474
       Methods:
475
          get_url_id_base64(): Get base64 encoded URL identifier.
476
          get_url_id_sha256(): Get the URL identifier as a SHA256 hash.
477
          upload(): Upload URL for analysis.
478
          get_report(): Retrieve information about an URL.
479
          analyse(): Analyse an URL.
480
          get_comments(): Retrieve comments for an URL.
481
          put_comments(): Add a comment to a URL.
482
          get_votes(): Retrieve votes for an URL.
483
          put_votes(): Add a votes to a URL.
484
          get_network_location(): Get the domain or IP address for a URL.
485
    """
486
487
    @staticmethod
488
    def get_url_id_base64(url):
489
        """Get base64 encoded URL identifier.
490
491
        Args:
492
           url: The URL for which you want to get the identifier (str).
493
494
        Return:
495
           The identifier of the url, base64 encoded (str).
496
        """
497
        return base64.urlsafe_b64encode(url.encode('utf-8')).decode('utf-8').rstrip('=')
498
499
    @staticmethod
500
    def get_url_id_sha256(url):
501
        """Get the URL identifier as a SHA256 hash.
502
503
        Args:
504
           url: The URL for which you want to get the identifier (str).
505
506
        Return:
507
           The identifier of the url, SHA256 encoded (str).
508
        """
509
        return hashlib.sha256(url.encode()).hexdigest()
510
511
    def upload(self, url):
512
        """Upload URL for analysis.
513
514
        Args:
515
           url: URL to be analyzed (str).
516
517
        Return:
518
           The response from the server as a byte sequence.
519
520
        Exception
521
           VirusTotalAPIError(Connection error): In case of server connection errors.
522
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
523
        """
524
        self._last_http_error = None
525
        self._last_result = None
526
        data = {'url': url}
527
        api_url = self.base_url + '/urls'
528
        try:
529
            response = requests.post(api_url, headers=self.headers, data=data,
530
                                     timeout=self.timeout, proxies=self.proxies)
531
        except requests.exceptions.Timeout:
532
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
533
        except requests.exceptions.ConnectionError:
534
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
535
        else:
536
            self._last_http_error = response.status_code
537
            self._last_result = response.content
538
            return response.content
539
540
    def get_report(self, url_id):
541
        """Retrieve information about an URL.
542
543
        Args:
544
           url_id: URL identifier (str). This identifier can adopt two forms: the SHA-256 of the
545
              canonized URL (method 'get_url_id_sha256()'), the string resulting from encoding the
546
              URL in base64 without the "=" padding (method 'get_url_id_base64()').
547
548
        Return:
549
           The response from the server as a byte sequence.
550
551
        Exception
552
           VirusTotalAPIError(Connection error): In case of server connection errors.
553
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
554
        """
555
        self._last_http_error = None
556
        self._last_result = None
557
        api_url = self.base_url + '/urls/' + url_id
558
        try:
559
            response = requests.get(api_url, headers=self.headers,
560
                                    timeout=self.timeout, proxies=self.proxies)
561
        except requests.exceptions.Timeout:
562
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
563
        except requests.exceptions.ConnectionError:
564
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
565
        else:
566
            self._last_http_error = response.status_code
567
            self._last_result = response.content
568
            return response.content
569
570
    def analyse(self, url_id):
571
        """Analyse an URL.
572
573
        Args:
574
           url_id: URL identifier (str). This identifier can adopt two forms: the SHA-256 of the
575
              canonized URL (method 'get_url_id_sha256()'), the string resulting from encoding the
576
              URL in base64 without the "=" padding (method 'get_url_id_base64()').
577
578
        Return:
579
           The response from the server as a byte sequence.
580
581
        Exception
582
           VirusTotalAPIError(Connection error): In case of server connection errors.
583
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
584
        """
585
        self._last_http_error = None
586
        self._last_result = None
587
        api_url = self.base_url + '/urls/' + url_id + '/analyse'
588
        try:
589
            response = requests.post(api_url, headers=self.headers,
590
                                     timeout=self.timeout, proxies=self.proxies)
591
        except requests.exceptions.Timeout:
592
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
593
        except requests.exceptions.ConnectionError:
594
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
595
        else:
596
            self._last_http_error = response.status_code
597
            self._last_result = response.content
598
            return response.content
599
600
    def get_comments(self, url_id, limit=10, cursor='""'):
601
        """Retrieve comments for an URL.
602
603
        Args:
604
           url_id: URL identifier (str). This identifier can adopt two forms: the SHA-256 of the
605
              canonized URL (method 'get_url_id_sha256()'), the string resulting from encoding the
606
              URL in base64 without the "=" padding (method 'get_url_id_base64()').
607
           limit: Maximum number of comments to retrieve (int). The default value is 10.
608
           cursor: Continuation cursor (str). The default value is ''.
609
610
        Return:
611
           The response from the server as a byte sequence.
612
613
        Exception
614
           VirusTotalAPIError(Connection error): In case of server connection errors.
615
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
616
        """
617
        self._last_http_error = None
618
        self._last_result = None
619
        query_string = {'limit': str(limit), 'cursor': cursor}
620
        api_url = self.base_url + '/urls/' + url_id + '/comments'
621
        try:
622
            response = requests.get(api_url, headers=self.headers, params=query_string,
623
                                    timeout=self.timeout, proxies=self.proxies)
624
        except requests.exceptions.Timeout:
625
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
626
        except requests.exceptions.ConnectionError:
627
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
628
        else:
629
            self._last_http_error = response.status_code
630
            self._last_result = response.content
631
            return response.content
632
633
    def put_comments(self, url_id, text):
634
        """Add a comment to a URL.
635
636
        Args:
637
           url_id: URL identifier (str). This identifier can adopt two forms: the SHA-256 of the
638
              canonized URL (method 'get_url_id_sha256()'), the string resulting from encoding the
639
              URL in base64 without the "=" padding (method 'get_url_id_base64()').
640
           text: Text of the comment (str).
641
642
        Return:
643
           The response from the server as a byte sequence.
644
645
        Exception
646
           VirusTotalAPIError(Connection error): In case of server connection errors.
647
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
648
        """
649
        self._last_http_error = None
650
        self._last_result = None
651
        comments = {"data": {'type': 'comment', 'attributes': {'text': text}}}
652
        api_url = self.base_url + '/urls/' + url_id + '/comments'
653
        try:
654
            response = requests.post(api_url, headers=self.headers, json=comments,
655
                                     timeout=self.timeout, proxies=self.proxies)
656
        except requests.exceptions.Timeout:
657
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
658
        except requests.exceptions.ConnectionError:
659
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
660
        else:
661
            self._last_http_error = response.status_code
662
            self._last_result = response.content
663
            return response.content
664
665
    def get_votes(self, url_id, limit=10, cursor='""'):
666
        """Retrieve votes for a URL.
667
668
        Args:
669
           url_id: URL identifier (str). This identifier can adopt two forms: the SHA-256 of the
670
              canonized URL (method 'get_url_id_sha256()'), the string resulting from encoding the
671
              URL in base64 without the "=" padding (method 'get_url_id_base64()').
672
           limit: Maximum number of comments to retrieve (int). The default value is 10.
673
           cursor: Continuation cursor (str). The default value is ''.
674
675
        Return:
676
           The response from the server as a byte sequence.
677
678
        Exception
679
           VirusTotalAPIError(Connection error): In case of server connection errors.
680
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
681
        """
682
        self._last_http_error = None
683
        self._last_result = None
684
        query_string = {'limit': str(limit), 'cursor': cursor}
685
        api_url = self.base_url + '/urls/' + url_id + '/votes'
686
        try:
687
            response = requests.get(api_url, headers=self.headers, params=query_string,
688
                                    timeout=self.timeout, proxies=self.proxies)
689
        except requests.exceptions.Timeout:
690
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
691
        except requests.exceptions.ConnectionError:
692
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
693
        else:
694
            self._last_http_error = response.status_code
695
            self._last_result = response.content
696
            return response.content
697
698
    def put_votes(self, url_id, malicious=False):
699
        """Add a vote for a URL.
700
701
        Args:
702
           url_id: URL identifier (str). This identifier can adopt two forms: the SHA-256 of the
703
              canonized URL (method 'get_url_id_sha256()'), the string resulting from encoding the
704
              URL in base64 without the "=" padding (method 'get_url_id_base64()').
705
           malicious: Determines a malicious (True) or harmless (False) URL (bool).
706
707
        Return:
708
           The response from the server as a byte sequence.
709
710
        Exception
711
           VirusTotalAPIError(Connection error): In case of server connection errors.
712
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
713
        """
714
        self._last_http_error = None
715
        self._last_result = None
716
        if malicious:
717
            verdict = 'malicious'
718
        else:
719
            verdict = 'harmless'
720
        votes = {'data': {'type': 'vote', 'attributes': {'verdict': verdict}}}
721
        api_url = self.base_url + '/urls/' + url_id + '/votes'
722
        try:
723
            response = requests.post(api_url, headers=self.headers, json=votes,
724
                                     timeout=self.timeout, proxies=self.proxies)
725
        except requests.exceptions.Timeout:
726
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
727
        except requests.exceptions.ConnectionError:
728
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
729
        else:
730
            self._last_http_error = response.status_code
731
            self._last_result = response.content
732
            return response.content
733
734
    def get_network_location(self, url_id):
735
        """Get the domain or IP address for a URL.
736
737
        Args:
738
           url_id: URL identifier (str). This identifier can adopt two forms: the SHA-256 of the
739
              canonized URL (method 'get_url_id_sha256()'), the string resulting from encoding the
740
              URL in base64 without the "=" padding (method 'get_url_id_base64()').
741
742
        Return:
743
           The response from the server as a byte sequence.
744
745
        Exception
746
           VirusTotalAPIError(Connection error): In case of server connection errors.
747
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
748
        """
749
        self._last_http_error = None
750
        self._last_result = None
751
        api_url = self.base_url + '/urls/' + url_id + '/network_location'
752
        try:
753
            response = requests.get(api_url, headers=self.headers,
754
                                    timeout=self.timeout, proxies=self.proxies)
755
        except requests.exceptions.Timeout:
756
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
757
        except requests.exceptions.ConnectionError:
758
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
759
        else:
760
            self._last_http_error = response.status_code
761
            self._last_result = response.content
762
            return response.content
763
764
    def get_relationship(self, url_id, relationship='/last_serving_ip_address',
765
                         limit=10, cursor='""'):
766
        """Retrieve objects related to an URL
767
768
        Args:
769
           url_id: URL identifier (str). This identifier can adopt two forms: the SHA-256 of the
770
              canonized URL (method 'get_url_id_sha256()'), the string resulting from encoding the
771
              URL in base64 without the "=" padding (method 'get_url_id_base64()').
772
           relationship: Relationship name (str). The default value is '/last_serving_ip_address'.
773
           limit: Maximum number of comments to retrieve (int). The default value is 10.
774
           cursor: Continuation cursor (str). The default value is ''.
775
776
        Return:
777
           The response from the server as a byte sequence.
778
779
        Exception
780
           VirusTotalAPIError(Connection error): In case of server connection errors.
781
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
782
        """
783
        self._last_http_error = None
784
        self._last_result = None
785
        query_string = {'limit': str(limit), 'cursor': cursor}
786
        api_url = self.base_url + '/urls/' + url_id + relationship
787
        try:
788
            response = requests.get(api_url, headers=self.headers, params=query_string,
789
                                    timeout=self.timeout, proxies=self.proxies)
790
        except requests.exceptions.Timeout:
791
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
792
        except requests.exceptions.ConnectionError:
793
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
794
        else:
795
            self._last_http_error = response.status_code
796
            self._last_result = response.content
797
            return response.content
798
799
800 View Code Duplication
class VirusTotalAPIDomains(VirusTotalAPI):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
801
    """The retrieving information about any domain from the VirusTotal database methods are defined
802
       in the class.
803
804
       Methods:
805
          get_report(): Retrieve information about an Internet domain.
806
          get_comments(): Retrieve comments for an Internet domain.
807
          put_comments(): Add a comment to an Internet domain.
808
          get_relationship(): Retrieve objects related to an Internet domain.
809
          get_votes(): Retrieve votes for a hostname or domain.
810
          put_votes(): Add a vote for a hostname or domain.
811
    """
812
813
    def get_report(self, domain):
814
        """Retrieve information about an Internet domain.
815
816
        Args:
817
           domain: Domain name (str).
818
819
        Return:
820
           The response from the server as a byte sequence.
821
822
        Exception
823
           VirusTotalAPIError(Connection error): In case of server connection errors.
824
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
825
        """
826
        self._last_http_error = None
827
        self._last_result = None
828
        api_url = self.base_url + '/domains/' + domain
829
        try:
830
            response = requests.get(api_url, headers=self.headers,
831
                                    timeout=self.timeout, proxies=self.proxies)
832
        except requests.exceptions.Timeout:
833
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
834
        except requests.exceptions.ConnectionError:
835
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
836
        else:
837
            self._last_http_error = response.status_code
838
            self._last_result = response.content
839
            return response.content
840
841
    def get_comments(self, domain, limit=10, cursor='""'):
842
        """Retrieve comments for an Internet domain.
843
844
        Args:
845
           domain: Domain name (str).
846
           limit: Maximum number of comments to retrieve (int). The default value is 10.
847
           cursor: Continuation cursor (str). The default value is ''.
848
849
        Return:
850
           The response from the server as a byte sequence.
851
852
        Exception
853
           VirusTotalAPIError(Connection error): In case of server connection errors.
854
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
855
        """
856
        self._last_http_error = None
857
        self._last_result = None
858
        query_string = {'limit': str(limit), 'cursor': cursor}
859
        api_url = self.base_url + '/domains/' + domain + '/comments'
860
        try:
861
            response = requests.get(api_url, headers=self.headers, params=query_string,
862
                                    timeout=self.timeout, proxies=self.proxies)
863
        except requests.exceptions.Timeout:
864
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
865
        except requests.exceptions.ConnectionError:
866
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
867
        else:
868
            self._last_http_error = response.status_code
869
            self._last_result = response.content
870
            return response.content
871
872
    def put_comments(self, domain, text):
873
        """Add a comment to an Internet domain.
874
875
        Args:
876
           domain: Domain name (str).
877
           text: Text of the comment (str).
878
879
        Return:
880
           The response from the server as a byte sequence.
881
882
        Exception
883
           VirusTotalAPIError(Connection error): In case of server connection errors.
884
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
885
        """
886
        self._last_http_error = None
887
        self._last_result = None
888
        comments = {"data": {'type': 'comment', 'attributes': {'text': text}}}
889
        api_url = self.base_url + '/domains/' + domain + '/comments'
890
        try:
891
            response = requests.post(api_url, headers=self.headers, json=comments,
892
                                     timeout=self.timeout, proxies=self.proxies)
893
        except requests.exceptions.Timeout:
894
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
895
        except requests.exceptions.ConnectionError:
896
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
897
        else:
898
            self._last_http_error = response.status_code
899
            self._last_result = response.content
900
            return response.content
901
902
    def get_relationship(self, domain, relationship='/resolutions', limit=10, cursor='""'):
903
        """Retrieve objects related to an Internet domain.
904
905
        Args:
906
           domain: Domain name (str).
907
           relationship: Relationship name (str). The default value is '/resolutions'.
908
           limit: Maximum number of comments to retrieve (int). The default value is 10.
909
           cursor: Continuation cursor (str). The default value is ''.
910
911
        Return:
912
           The response from the server as a byte sequence.
913
914
        Exception
915
           VirusTotalAPIError(Connection error): In case of server connection errors.
916
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
917
        """
918
        self._last_http_error = None
919
        self._last_result = None
920
        query_string = {'limit': str(limit), 'cursor': cursor}
921
        api_url = self.base_url + '/domains/' + domain + relationship
922
        try:
923
            response = requests.get(api_url, headers=self.headers, params=query_string,
924
                                    timeout=self.timeout, proxies=self.proxies)
925
        except requests.exceptions.Timeout:
926
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
927
        except requests.exceptions.ConnectionError:
928
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
929
        else:
930
            self._last_http_error = response.status_code
931
            self._last_result = response.content
932
            return response.content
933
934
    def get_votes(self, domain, limit=10, cursor='""'):
935
        """Retrieve votes for a hostname or domain.
936
937
        Args:
938
           domain: Domain name (str).
939
           limit: Maximum number of comments to retrieve (int). The default value is 10.
940
           cursor: Continuation cursor (str). The default value is ''.
941
942
        Return:
943
           The response from the server as a byte sequence.
944
945
        Exception
946
           VirusTotalAPIError(Connection error): In case of server connection errors.
947
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
948
        """
949
        self._last_http_error = None
950
        self._last_result = None
951
        query_string = {'limit': str(limit), 'cursor': cursor}
952
        api_url = self.base_url + '/domains/' + domain + '/votes'
953
        try:
954
            response = requests.get(api_url, headers=self.headers, params=query_string,
955
                                    timeout=self.timeout, proxies=self.proxies)
956
        except requests.exceptions.Timeout:
957
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
958
        except requests.exceptions.ConnectionError:
959
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
960
        else:
961
            self._last_http_error = response.status_code
962
            self._last_result = response.content
963
            return response.content
964
965
    def put_votes(self, domain, malicious=False):
966
        """Add a vote for a hostname or domain.
967
968
        Args:
969
           domain: Domain name (str).
970
           malicious: Determines a malicious (True) or harmless (False) domain (bool).
971
972
        Return:
973
           The response from the server as a byte sequence.
974
975
        Exception
976
           VirusTotalAPIError(Connection error): In case of server connection errors.
977
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
978
        """
979
        self._last_http_error = None
980
        self._last_result = None
981
        if malicious:
982
            verdict = 'malicious'
983
        else:
984
            verdict = 'harmless'
985
        votes = {'data': {'type': 'vote', 'attributes': {'verdict': verdict}}}
986
        api_url = self.base_url + '/domains/' + domain + '/votes'
987
        try:
988
            response = requests.post(api_url, headers=self.headers, json=votes,
989
                                     timeout=self.timeout, proxies=self.proxies)
990
        except requests.exceptions.Timeout:
991
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
992
        except requests.exceptions.ConnectionError:
993
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
994
        else:
995
            self._last_http_error = response.status_code
996
            self._last_result = response.content
997
            return response.content
998
999
1000 View Code Duplication
class VirusTotalAPIIPAddresses(VirusTotalAPI):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1001
    """The retrieving information about any IP addresses from the VirusTotal database methods are
1002
       defined in the class.
1003
1004
       Methods:
1005
          get_report(): Retrieve information about an IP address.
1006
          get_comments(): Retrieve comments for an IP address.
1007
          put_comments(): Add a comment to an IP address.
1008
          get_relationship(): Retrieve objects related to an IP address.
1009
          get_votes(): Retrieve votes for an IP address.
1010
          put_votes(): Add a vote for an IP address.
1011
    """
1012
1013
    def get_report(self, ip_address):
1014
        """Retrieve information about an IP address.
1015
1016
        Args:
1017
           ip_address: IP address (str).
1018
1019
        Return:
1020
           The response from the server as a byte sequence.
1021
1022
        Exception
1023
           VirusTotalAPIError(Connection error): In case of server connection errors.
1024
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
1025
        """
1026
        self._last_http_error = None
1027
        self._last_result = None
1028
        api_url = self.base_url + '/ip_addresses/' + ip_address
1029
        try:
1030
            response = requests.get(api_url, headers=self.headers,
1031
                                    timeout=self.timeout, proxies=self.proxies)
1032
        except requests.exceptions.Timeout:
1033
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
1034
        except requests.exceptions.ConnectionError:
1035
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
1036
        else:
1037
            self._last_http_error = response.status_code
1038
            self._last_result = response.content
1039
            return response.content
1040
1041
    def get_comments(self, ip_address, limit=10, cursor='""'):
1042
        """Retrieve comments for an IP address.
1043
1044
        Args:
1045
           ip_address: IP address (str).
1046
           limit: Maximum number of comments to retrieve (int). The default value is 10.
1047
           cursor: Continuation cursor (str). The default value is ''.
1048
1049
        Return:
1050
           The response from the server as a byte sequence.
1051
1052
        Exception
1053
           VirusTotalAPIError(Connection error): In case of server connection errors.
1054
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
1055
        """
1056
        self._last_http_error = None
1057
        self._last_result = None
1058
        query_string = {'limit': str(limit), 'cursor': cursor}
1059
        api_url = self.base_url + '/ip_addresses/' + ip_address + '/comments'
1060
        try:
1061
            response = requests.get(api_url, headers=self.headers, params=query_string,
1062
                                    timeout=self.timeout, proxies=self.proxies)
1063
        except requests.exceptions.Timeout:
1064
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
1065
        except requests.exceptions.ConnectionError:
1066
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
1067
        else:
1068
            self._last_http_error = response.status_code
1069
            self._last_result = response.content
1070
            return response.content
1071
1072
    def put_comments(self, ip_address, text):
1073
        """Add a comment to an IP address.
1074
1075
        Args:
1076
           ip_address: IP address (str).
1077
           text: Text of the comment (str).
1078
1079
        Return:
1080
           The response from the server as a byte sequence.
1081
1082
        Exception
1083
           VirusTotalAPIError(Connection error): In case of server connection errors.
1084
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
1085
        """
1086
        self._last_http_error = None
1087
        self._last_result = None
1088
        comments = {"data": {'type': 'comment', 'attributes': {'text': text}}}
1089
        api_url = self.base_url + '/ip_addresses/' + ip_address + '/comments'
1090
        try:
1091
            response = requests.post(api_url, headers=self.headers, json=comments,
1092
                                     timeout=self.timeout, proxies=self.proxies)
1093
        except requests.exceptions.Timeout:
1094
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
1095
        except requests.exceptions.ConnectionError:
1096
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
1097
        else:
1098
            self._last_http_error = response.status_code
1099
            self._last_result = response.content
1100
            return response.content
1101
1102
    def get_relationship(self, ip_address, relationship='/resolutions', limit=10, cursor='""'):
1103
        """Retrieve objects related to an IP address.
1104
1105
        Args:
1106
           ip_address: IP address (str).
1107
           relationship: Relationship name (str). The default value is '/resolutions'.
1108
           limit: Maximum number of comments to retrieve (int). The default value is 10.
1109
           cursor: Continuation cursor (str). The default value is ''.
1110
1111
        Return:
1112
           The response from the server as a byte sequence.
1113
1114
        Exception
1115
           VirusTotalAPIError(Connection error): In case of server connection errors.
1116
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
1117
        """
1118
        self._last_http_error = None
1119
        self._last_result = None
1120
        query_string = {'limit': str(limit), 'cursor': cursor}
1121
        api_url = self.base_url + '/ip_addresses/' + ip_address + relationship
1122
        try:
1123
            response = requests.get(api_url, headers=self.headers, params=query_string,
1124
                                    timeout=self.timeout, proxies=self.proxies)
1125
        except requests.exceptions.Timeout:
1126
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
1127
        except requests.exceptions.ConnectionError:
1128
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
1129
        else:
1130
            self._last_http_error = response.status_code
1131
            self._last_result = response.content
1132
            return response.content
1133
1134
    def get_votes(self, ip_address, limit=10, cursor='""'):
1135
        """Retrieve votes for an IP address.
1136
1137
        Args:
1138
           domain: Domain name (str).
1139
           limit: Maximum number of comments to retrieve (int). The default value is 10.
1140
           cursor: Continuation cursor (str). The default value is ''.
1141
1142
        Return:
1143
           The response from the server as a byte sequence.
1144
1145
        Exception
1146
           VirusTotalAPIError(Connection error): In case of server connection errors.
1147
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
1148
        """
1149
        self._last_http_error = None
1150
        self._last_result = None
1151
        query_string = {'limit': str(limit), 'cursor': cursor}
1152
        api_url = self.base_url + '/ip_addresses/' + ip_address + '/votes'
1153
        try:
1154
            response = requests.get(api_url, headers=self.headers, params=query_string,
1155
                                    timeout=self.timeout, proxies=self.proxies)
1156
        except requests.exceptions.Timeout:
1157
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
1158
        except requests.exceptions.ConnectionError:
1159
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
1160
        else:
1161
            self._last_http_error = response.status_code
1162
            self._last_result = response.content
1163
            return response.content
1164
1165
    def put_votes(self, ip_address, malicious=False):
1166
        """Add a vote for an IP address.
1167
1168
        Args:
1169
           domain: IP address (str).
1170
           malicious: Determines a malicious (True) or harmless (False) domain (bool).
1171
1172
        Return:
1173
           The response from the server as a byte sequence.
1174
1175
        Exception
1176
           VirusTotalAPIError(Connection error): In case of server connection errors.
1177
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
1178
        """
1179
        self._last_http_error = None
1180
        self._last_result = None
1181
        if malicious:
1182
            verdict = 'malicious'
1183
        else:
1184
            verdict = 'harmless'
1185
        votes = {'data': {'type': 'vote', 'attributes': {'verdict': verdict}}}
1186
        api_url = self.base_url + '/ip_addresses/' + ip_address + '/votes'
1187
        try:
1188
            response = requests.post(api_url, headers=self.headers, json=votes,
1189
                                     timeout=self.timeout, proxies=self.proxies)
1190
        except requests.exceptions.Timeout:
1191
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
1192
        except requests.exceptions.ConnectionError:
1193
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
1194
        else:
1195
            self._last_http_error = response.status_code
1196
            self._last_result = response.content
1197
            return response.content
1198
1199
1200
class VirusTotalAPIAnalyses(VirusTotalAPI):
1201
    """The retrieving information about analysis of the file or URL method are defined in the class.
1202
1203
       Methods:
1204
          get_report(): Retrieve information about a file or URL analysis.
1205
    """
1206
1207
    def get_report(self, object_id):
1208
        """Retrieve information about a file or URL analysis.
1209
1210
        Args:
1211
           object_id: Analysis identifier (str).
1212
1213
        Return:
1214
           The response from the server as a byte sequence.
1215
1216
        Exception
1217
           VirusTotalAPIError(Connection error): In case of server connection errors.
1218
           VirusTotalAPIError(Timeout error): If the response timeout from the server is exceeded.
1219
        """
1220
        self._last_http_error = None
1221
        self._last_result = None
1222
        api_url = self.base_url + '/analyses/' + object_id
1223
        try:
1224
            response = requests.get(api_url, headers=self.headers,
1225
                                    timeout=self.timeout, proxies=self.proxies)
1226
        except requests.exceptions.Timeout:
1227
            raise VirusTotalAPIError('Timeout error', errno.ETIMEDOUT)
1228
        except requests.exceptions.ConnectionError:
1229
            raise VirusTotalAPIError('Connection error', errno.ECONNABORTED)
1230
        else:
1231
            self._last_http_error = response.status_code
1232
            self._last_result = response.content
1233
            return response.content
1234
1235
1236
class VirusTotalAPIError(Exception):
1237
    """A class that implements exceptions that may occur when module class methods are used.
1238
    """
1239
1240
    def __init__(self, message, err_code):
1241
        """Inits VirusTotalAPIError.
1242
        """
1243
        super().__init__(message)
1244
        self.err_code = err_code
1245