1 /*
2 * Python bindings module for libmodi (pymodi)
3 *
4 * Copyright (C) 2012-2021, Joachim Metz <joachim.metz@gmail.com>
5 *
6 * Refer to AUTHORS for acknowledgements.
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22 #include <common.h>
23 #include <narrow_string.h>
24 #include <types.h>
25
26 #if defined( HAVE_STDLIB_H ) || defined( HAVE_WINAPI )
27 #include <stdlib.h>
28 #endif
29
30 #include "pymodi.h"
31 #include "pymodi_error.h"
32 #include "pymodi_file_object_io_handle.h"
33 #include "pymodi_handle.h"
34 #include "pymodi_libbfio.h"
35 #include "pymodi_libcerror.h"
36 #include "pymodi_libmodi.h"
37 #include "pymodi_python.h"
38 #include "pymodi_unused.h"
39
40 #if !defined( LIBMODI_HAVE_BFIO )
41
42 LIBMODI_EXTERN \
43 int libmodi_check_file_signature_file_io_handle(
44 libbfio_handle_t *file_io_handle,
45 libmodi_error_t **error );
46
47 #endif /* !defined( LIBMODI_HAVE_BFIO ) */
48
49 /* The pymodi module methods
50 */
51 PyMethodDef pymodi_module_methods[] = {
52 { "get_version",
53 (PyCFunction) pymodi_get_version,
54 METH_NOARGS,
55 "get_version() -> String\n"
56 "\n"
57 "Retrieves the version." },
58
59 { "check_file_signature",
60 (PyCFunction) pymodi_check_file_signature,
61 METH_VARARGS | METH_KEYWORDS,
62 "check_file_signature(filename) -> Boolean\n"
63 "\n"
64 "Checks if a file has a Mac OS disk image file signature." },
65
66 { "check_file_signature_file_object",
67 (PyCFunction) pymodi_check_file_signature_file_object,
68 METH_VARARGS | METH_KEYWORDS,
69 "check_file_signature_file_object(file_object) -> Boolean\n"
70 "\n"
71 "Checks if a file has a Mac OS disk image file signature using a file-like object." },
72
73 { "open",
74 (PyCFunction) pymodi_open_new_handle,
75 METH_VARARGS | METH_KEYWORDS,
76 "open(filename, mode='r') -> Object\n"
77 "\n"
78 "Opens a handle." },
79
80 { "open_file_object",
81 (PyCFunction) pymodi_open_new_handle_with_file_object,
82 METH_VARARGS | METH_KEYWORDS,
83 "open_file_object(file_object, mode='r') -> Object\n"
84 "\n"
85 "Opens a handle using a file-like object." },
86
87 /* Sentinel */
88 { NULL, NULL, 0, NULL }
89 };
90
91 /* Retrieves the pymodi/libmodi version
92 * Returns a Python object if successful or NULL on error
93 */
pymodi_get_version(PyObject * self PYMODI_ATTRIBUTE_UNUSED,PyObject * arguments PYMODI_ATTRIBUTE_UNUSED)94 PyObject *pymodi_get_version(
95 PyObject *self PYMODI_ATTRIBUTE_UNUSED,
96 PyObject *arguments PYMODI_ATTRIBUTE_UNUSED )
97 {
98 const char *errors = NULL;
99 const char *version_string = NULL;
100 size_t version_string_length = 0;
101
102 PYMODI_UNREFERENCED_PARAMETER( self )
103 PYMODI_UNREFERENCED_PARAMETER( arguments )
104
105 Py_BEGIN_ALLOW_THREADS
106
107 version_string = libmodi_get_version();
108
109 Py_END_ALLOW_THREADS
110
111 version_string_length = narrow_string_length(
112 version_string );
113
114 /* Pass the string length to PyUnicode_DecodeUTF8
115 * otherwise it makes the end of string character is part
116 * of the string
117 */
118 return( PyUnicode_DecodeUTF8(
119 version_string,
120 (Py_ssize_t) version_string_length,
121 errors ) );
122 }
123
124 /* Checks if a file has a Mac OS disk image file signature
125 * Returns a Python object if successful or NULL on error
126 */
pymodi_check_file_signature(PyObject * self PYMODI_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)127 PyObject *pymodi_check_file_signature(
128 PyObject *self PYMODI_ATTRIBUTE_UNUSED,
129 PyObject *arguments,
130 PyObject *keywords )
131 {
132 PyObject *string_object = NULL;
133 libcerror_error_t *error = NULL;
134 const char *filename_narrow = NULL;
135 static char *function = "pymodi_check_file_signature";
136 static char *keyword_list[] = { "filename", NULL };
137 int result = 0;
138
139 #if defined( HAVE_WIDE_SYSTEM_CHARACTER )
140 const wchar_t *filename_wide = NULL;
141 #else
142 PyObject *utf8_string_object = NULL;
143 #endif
144
145 PYMODI_UNREFERENCED_PARAMETER( self )
146
147 /* Note that PyArg_ParseTupleAndKeywords with "s" will force Unicode strings to be converted to narrow character string.
148 * On Windows the narrow character strings contains an extended ASCII string with a codepage. Hence we get a conversion
149 * exception. This will also fail if the default encoding is not set correctly. We cannot use "u" here either since that
150 * does not allow us to pass non Unicode string objects and Python (at least 2.7) does not seems to automatically upcast them.
151 */
152 if( PyArg_ParseTupleAndKeywords(
153 arguments,
154 keywords,
155 "O|",
156 keyword_list,
157 &string_object ) == 0 )
158 {
159 return( NULL );
160 }
161 PyErr_Clear();
162
163 result = PyObject_IsInstance(
164 string_object,
165 (PyObject *) &PyUnicode_Type );
166
167 if( result == -1 )
168 {
169 pymodi_error_fetch_and_raise(
170 PyExc_RuntimeError,
171 "%s: unable to determine if string object is of type Unicode.",
172 function );
173
174 return( NULL );
175 }
176 else if( result != 0 )
177 {
178 PyErr_Clear();
179
180 #if defined( HAVE_WIDE_SYSTEM_CHARACTER )
181 filename_wide = (wchar_t *) PyUnicode_AsUnicode(
182 string_object );
183 Py_BEGIN_ALLOW_THREADS
184
185 result = libmodi_check_file_signature_wide(
186 filename_wide,
187 &error );
188
189 Py_END_ALLOW_THREADS
190 #else
191 utf8_string_object = PyUnicode_AsUTF8String(
192 string_object );
193
194 if( utf8_string_object == NULL )
195 {
196 pymodi_error_fetch_and_raise(
197 PyExc_RuntimeError,
198 "%s: unable to convert Unicode string to UTF-8.",
199 function );
200
201 return( NULL );
202 }
203 #if PY_MAJOR_VERSION >= 3
204 filename_narrow = PyBytes_AsString(
205 utf8_string_object );
206 #else
207 filename_narrow = PyString_AsString(
208 utf8_string_object );
209 #endif
210 Py_BEGIN_ALLOW_THREADS
211
212 result = libmodi_check_file_signature(
213 filename_narrow,
214 &error );
215
216 Py_END_ALLOW_THREADS
217
218 Py_DecRef(
219 utf8_string_object );
220
221 #endif /* defined( HAVE_WIDE_SYSTEM_CHARACTER ) */
222
223 if( result == -1 )
224 {
225 pymodi_error_raise(
226 error,
227 PyExc_IOError,
228 "%s: unable to check file signature.",
229 function );
230
231 libcerror_error_free(
232 &error );
233
234 return( NULL );
235 }
236 if( result != 0 )
237 {
238 Py_IncRef(
239 (PyObject *) Py_True );
240
241 return( Py_True );
242 }
243 Py_IncRef(
244 (PyObject *) Py_False );
245
246 return( Py_False );
247 }
248 PyErr_Clear();
249
250 #if PY_MAJOR_VERSION >= 3
251 result = PyObject_IsInstance(
252 string_object,
253 (PyObject *) &PyBytes_Type );
254 #else
255 result = PyObject_IsInstance(
256 string_object,
257 (PyObject *) &PyString_Type );
258 #endif
259 if( result == -1 )
260 {
261 pymodi_error_fetch_and_raise(
262 PyExc_RuntimeError,
263 "%s: unable to determine if string object is of type string.",
264 function );
265
266 return( NULL );
267 }
268 else if( result != 0 )
269 {
270 PyErr_Clear();
271
272 #if PY_MAJOR_VERSION >= 3
273 filename_narrow = PyBytes_AsString(
274 string_object );
275 #else
276 filename_narrow = PyString_AsString(
277 string_object );
278 #endif
279 Py_BEGIN_ALLOW_THREADS
280
281 result = libmodi_check_file_signature(
282 filename_narrow,
283 &error );
284
285 Py_END_ALLOW_THREADS
286
287 if( result == -1 )
288 {
289 pymodi_error_raise(
290 error,
291 PyExc_IOError,
292 "%s: unable to check file signature.",
293 function );
294
295 libcerror_error_free(
296 &error );
297
298 return( NULL );
299 }
300 if( result != 0 )
301 {
302 Py_IncRef(
303 (PyObject *) Py_True );
304
305 return( Py_True );
306 }
307 Py_IncRef(
308 (PyObject *) Py_False );
309
310 return( Py_False );
311 }
312 PyErr_Format(
313 PyExc_TypeError,
314 "%s: unsupported string object type.",
315 function );
316
317 return( NULL );
318 }
319
320 /* Checks if a file has a Mac OS disk image file signature using a file-like object
321 * Returns a Python object if successful or NULL on error
322 */
pymodi_check_file_signature_file_object(PyObject * self PYMODI_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)323 PyObject *pymodi_check_file_signature_file_object(
324 PyObject *self PYMODI_ATTRIBUTE_UNUSED,
325 PyObject *arguments,
326 PyObject *keywords )
327 {
328 PyObject *file_object = NULL;
329 libbfio_handle_t *file_io_handle = NULL;
330 libcerror_error_t *error = NULL;
331 static char *function = "pymodi_check_file_signature_file_object";
332 static char *keyword_list[] = { "file_object", NULL };
333 int result = 0;
334
335 PYMODI_UNREFERENCED_PARAMETER( self )
336
337 if( PyArg_ParseTupleAndKeywords(
338 arguments,
339 keywords,
340 "O|",
341 keyword_list,
342 &file_object ) == 0 )
343 {
344 return( NULL );
345 }
346 if( pymodi_file_object_initialize(
347 &file_io_handle,
348 file_object,
349 &error ) != 1 )
350 {
351 pymodi_error_raise(
352 error,
353 PyExc_MemoryError,
354 "%s: unable to initialize file IO handle.",
355 function );
356
357 libcerror_error_free(
358 &error );
359
360 goto on_error;
361 }
362 Py_BEGIN_ALLOW_THREADS
363
364 result = libmodi_check_file_signature_file_io_handle(
365 file_io_handle,
366 &error );
367
368 Py_END_ALLOW_THREADS
369
370 if( result == -1 )
371 {
372 pymodi_error_raise(
373 error,
374 PyExc_IOError,
375 "%s: unable to check file signature.",
376 function );
377
378 libcerror_error_free(
379 &error );
380
381 goto on_error;
382 }
383 if( libbfio_handle_free(
384 &file_io_handle,
385 &error ) != 1 )
386 {
387 pymodi_error_raise(
388 error,
389 PyExc_MemoryError,
390 "%s: unable to free file IO handle.",
391 function );
392
393 libcerror_error_free(
394 &error );
395
396 goto on_error;
397 }
398 if( result != 0 )
399 {
400 Py_IncRef(
401 (PyObject *) Py_True );
402
403 return( Py_True );
404 }
405 Py_IncRef(
406 (PyObject *) Py_False );
407
408 return( Py_False );
409
410 on_error:
411 if( file_io_handle != NULL )
412 {
413 libbfio_handle_free(
414 &file_io_handle,
415 NULL );
416 }
417 return( NULL );
418 }
419
420 /* Creates a new handle object and opens it
421 * Returns a Python object if successful or NULL on error
422 */
pymodi_open_new_handle(PyObject * self PYMODI_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)423 PyObject *pymodi_open_new_handle(
424 PyObject *self PYMODI_ATTRIBUTE_UNUSED,
425 PyObject *arguments,
426 PyObject *keywords )
427 {
428 pymodi_handle_t *pymodi_handle = NULL;
429 static char *function = "pymodi_open_new_handle";
430
431 PYMODI_UNREFERENCED_PARAMETER( self )
432
433 /* PyObject_New does not invoke tp_init
434 */
435 pymodi_handle = PyObject_New(
436 struct pymodi_handle,
437 &pymodi_handle_type_object );
438
439 if( pymodi_handle == NULL )
440 {
441 PyErr_Format(
442 PyExc_MemoryError,
443 "%s: unable to create handle.",
444 function );
445
446 goto on_error;
447 }
448 if( pymodi_handle_init(
449 pymodi_handle ) != 0 )
450 {
451 goto on_error;
452 }
453 if( pymodi_handle_open(
454 pymodi_handle,
455 arguments,
456 keywords ) == NULL )
457 {
458 goto on_error;
459 }
460 return( (PyObject *) pymodi_handle );
461
462 on_error:
463 if( pymodi_handle != NULL )
464 {
465 Py_DecRef(
466 (PyObject *) pymodi_handle );
467 }
468 return( NULL );
469 }
470
471 /* Creates a new handle object and opens it using a file-like object
472 * Returns a Python object if successful or NULL on error
473 */
pymodi_open_new_handle_with_file_object(PyObject * self PYMODI_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)474 PyObject *pymodi_open_new_handle_with_file_object(
475 PyObject *self PYMODI_ATTRIBUTE_UNUSED,
476 PyObject *arguments,
477 PyObject *keywords )
478 {
479 pymodi_handle_t *pymodi_handle = NULL;
480 static char *function = "pymodi_open_new_handle_with_file_object";
481
482 PYMODI_UNREFERENCED_PARAMETER( self )
483
484 /* PyObject_New does not invoke tp_init
485 */
486 pymodi_handle = PyObject_New(
487 struct pymodi_handle,
488 &pymodi_handle_type_object );
489
490 if( pymodi_handle == NULL )
491 {
492 PyErr_Format(
493 PyExc_MemoryError,
494 "%s: unable to create handle.",
495 function );
496
497 goto on_error;
498 }
499 if( pymodi_handle_init(
500 pymodi_handle ) != 0 )
501 {
502 goto on_error;
503 }
504 if( pymodi_handle_open_file_object(
505 pymodi_handle,
506 arguments,
507 keywords ) == NULL )
508 {
509 goto on_error;
510 }
511 return( (PyObject *) pymodi_handle );
512
513 on_error:
514 if( pymodi_handle != NULL )
515 {
516 Py_DecRef(
517 (PyObject *) pymodi_handle );
518 }
519 return( NULL );
520 }
521
522 #if PY_MAJOR_VERSION >= 3
523
524 /* The pymodi module definition
525 */
526 PyModuleDef pymodi_module_definition = {
527 PyModuleDef_HEAD_INIT,
528
529 /* m_name */
530 "pymodi",
531 /* m_doc */
532 "Python libmodi module (pymodi).",
533 /* m_size */
534 -1,
535 /* m_methods */
536 pymodi_module_methods,
537 /* m_reload */
538 NULL,
539 /* m_traverse */
540 NULL,
541 /* m_clear */
542 NULL,
543 /* m_free */
544 NULL,
545 };
546
547 #endif /* PY_MAJOR_VERSION >= 3 */
548
549 /* Initializes the pymodi module
550 */
551 #if PY_MAJOR_VERSION >= 3
PyInit_pymodi(void)552 PyMODINIT_FUNC PyInit_pymodi(
553 void )
554 #else
555 PyMODINIT_FUNC initpymodi(
556 void )
557 #endif
558 {
559 PyObject *module = NULL;
560 PyGILState_STATE gil_state = 0;
561
562 #if defined( HAVE_DEBUG_OUTPUT )
563 libmodi_notify_set_stream(
564 stderr,
565 NULL );
566 libmodi_notify_set_verbose(
567 1 );
568 #endif
569
570 /* Create the module
571 * This function must be called before grabbing the GIL
572 * otherwise the module will segfault on a version mismatch
573 */
574 #if PY_MAJOR_VERSION >= 3
575 module = PyModule_Create(
576 &pymodi_module_definition );
577 #else
578 module = Py_InitModule3(
579 "pymodi",
580 pymodi_module_methods,
581 "Python libmodi module (pymodi)." );
582 #endif
583 if( module == NULL )
584 {
585 #if PY_MAJOR_VERSION >= 3
586 return( NULL );
587 #else
588 return;
589 #endif
590 }
591 #if PY_VERSION_HEX < 0x03070000
592 PyEval_InitThreads();
593 #endif
594 gil_state = PyGILState_Ensure();
595
596 /* Setup the handle type object
597 */
598 pymodi_handle_type_object.tp_new = PyType_GenericNew;
599
600 if( PyType_Ready(
601 &pymodi_handle_type_object ) < 0 )
602 {
603 goto on_error;
604 }
605 Py_IncRef(
606 (PyObject *) &pymodi_handle_type_object );
607
608 PyModule_AddObject(
609 module,
610 "handle",
611 (PyObject *) &pymodi_handle_type_object );
612
613 PyGILState_Release(
614 gil_state );
615
616 #if PY_MAJOR_VERSION >= 3
617 return( module );
618 #else
619 return;
620 #endif
621
622 on_error:
623 PyGILState_Release(
624 gil_state );
625
626 #if PY_MAJOR_VERSION >= 3
627 return( NULL );
628 #else
629 return;
630 #endif
631 }
632
633