1 /*
2 * Python bindings module for libolecf (pyolecf)
3 *
4 * Copyright (C) 2008-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 "pyolecf.h"
31 #include "pyolecf_error.h"
32 #include "pyolecf_file.h"
33 #include "pyolecf_file_object_io_handle.h"
34 #include "pyolecf_item.h"
35 #include "pyolecf_item_types.h"
36 #include "pyolecf_items.h"
37 #include "pyolecf_libbfio.h"
38 #include "pyolecf_libcerror.h"
39 #include "pyolecf_libolecf.h"
40 #include "pyolecf_property_section.h"
41 #include "pyolecf_property_sections.h"
42 #include "pyolecf_property_set.h"
43 #include "pyolecf_property_set_stream.h"
44 #include "pyolecf_property_value.h"
45 #include "pyolecf_property_values.h"
46 #include "pyolecf_python.h"
47 #include "pyolecf_stream.h"
48 #include "pyolecf_unused.h"
49 #include "pyolecf_value_types.h"
50
51 #if !defined( LIBOLECF_HAVE_BFIO )
52
53 LIBOLECF_EXTERN \
54 int libolecf_check_file_signature_file_io_handle(
55 libbfio_handle_t *file_io_handle,
56 libolecf_error_t **error );
57
58 #endif /* !defined( LIBOLECF_HAVE_BFIO ) */
59
60 /* The pyolecf module methods
61 */
62 PyMethodDef pyolecf_module_methods[] = {
63 { "get_version",
64 (PyCFunction) pyolecf_get_version,
65 METH_NOARGS,
66 "get_version() -> String\n"
67 "\n"
68 "Retrieves the version." },
69
70 { "check_file_signature",
71 (PyCFunction) pyolecf_check_file_signature,
72 METH_VARARGS | METH_KEYWORDS,
73 "check_file_signature(filename) -> Boolean\n"
74 "\n"
75 "Checks if a file has an Object Linking and Embedding (OLE) Compound File (CF) signature." },
76
77 { "check_file_signature_file_object",
78 (PyCFunction) pyolecf_check_file_signature_file_object,
79 METH_VARARGS | METH_KEYWORDS,
80 "check_file_signature_file_object(file_object) -> Boolean\n"
81 "\n"
82 "Checks if a file has an Object Linking and Embedding (OLE) Compound File (CF) signature using a file-like object." },
83
84 { "open",
85 (PyCFunction) pyolecf_open_new_file,
86 METH_VARARGS | METH_KEYWORDS,
87 "open(filename, mode='r') -> Object\n"
88 "\n"
89 "Opens a file." },
90
91 { "open_file_object",
92 (PyCFunction) pyolecf_open_new_file_with_file_object,
93 METH_VARARGS | METH_KEYWORDS,
94 "open_file_object(file_object, mode='r') -> Object\n"
95 "\n"
96 "Opens a file using a file-like object." },
97
98 /* Sentinel */
99 { NULL, NULL, 0, NULL }
100 };
101
102 /* Retrieves the pyolecf/libolecf version
103 * Returns a Python object if successful or NULL on error
104 */
pyolecf_get_version(PyObject * self PYOLECF_ATTRIBUTE_UNUSED,PyObject * arguments PYOLECF_ATTRIBUTE_UNUSED)105 PyObject *pyolecf_get_version(
106 PyObject *self PYOLECF_ATTRIBUTE_UNUSED,
107 PyObject *arguments PYOLECF_ATTRIBUTE_UNUSED )
108 {
109 const char *errors = NULL;
110 const char *version_string = NULL;
111 size_t version_string_length = 0;
112
113 PYOLECF_UNREFERENCED_PARAMETER( self )
114 PYOLECF_UNREFERENCED_PARAMETER( arguments )
115
116 Py_BEGIN_ALLOW_THREADS
117
118 version_string = libolecf_get_version();
119
120 Py_END_ALLOW_THREADS
121
122 version_string_length = narrow_string_length(
123 version_string );
124
125 /* Pass the string length to PyUnicode_DecodeUTF8
126 * otherwise it makes the end of string character is part
127 * of the string
128 */
129 return( PyUnicode_DecodeUTF8(
130 version_string,
131 (Py_ssize_t) version_string_length,
132 errors ) );
133 }
134
135 /* Checks if a file has an Object Linking and Embedding (OLE) Compound File (CF) signature
136 * Returns a Python object if successful or NULL on error
137 */
pyolecf_check_file_signature(PyObject * self PYOLECF_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)138 PyObject *pyolecf_check_file_signature(
139 PyObject *self PYOLECF_ATTRIBUTE_UNUSED,
140 PyObject *arguments,
141 PyObject *keywords )
142 {
143 PyObject *string_object = NULL;
144 libcerror_error_t *error = NULL;
145 const char *filename_narrow = NULL;
146 static char *function = "pyolecf_check_file_signature";
147 static char *keyword_list[] = { "filename", NULL };
148 int result = 0;
149
150 #if defined( HAVE_WIDE_SYSTEM_CHARACTER )
151 const wchar_t *filename_wide = NULL;
152 #else
153 PyObject *utf8_string_object = NULL;
154 #endif
155
156 PYOLECF_UNREFERENCED_PARAMETER( self )
157
158 /* Note that PyArg_ParseTupleAndKeywords with "s" will force Unicode strings to be converted to narrow character string.
159 * On Windows the narrow character strings contains an extended ASCII string with a codepage. Hence we get a conversion
160 * exception. This will also fail if the default encoding is not set correctly. We cannot use "u" here either since that
161 * does not allow us to pass non Unicode string objects and Python (at least 2.7) does not seems to automatically upcast them.
162 */
163 if( PyArg_ParseTupleAndKeywords(
164 arguments,
165 keywords,
166 "O|",
167 keyword_list,
168 &string_object ) == 0 )
169 {
170 return( NULL );
171 }
172 PyErr_Clear();
173
174 result = PyObject_IsInstance(
175 string_object,
176 (PyObject *) &PyUnicode_Type );
177
178 if( result == -1 )
179 {
180 pyolecf_error_fetch_and_raise(
181 PyExc_RuntimeError,
182 "%s: unable to determine if string object is of type Unicode.",
183 function );
184
185 return( NULL );
186 }
187 else if( result != 0 )
188 {
189 PyErr_Clear();
190
191 #if defined( HAVE_WIDE_SYSTEM_CHARACTER )
192 filename_wide = (wchar_t *) PyUnicode_AsUnicode(
193 string_object );
194 Py_BEGIN_ALLOW_THREADS
195
196 result = libolecf_check_file_signature_wide(
197 filename_wide,
198 &error );
199
200 Py_END_ALLOW_THREADS
201 #else
202 utf8_string_object = PyUnicode_AsUTF8String(
203 string_object );
204
205 if( utf8_string_object == NULL )
206 {
207 pyolecf_error_fetch_and_raise(
208 PyExc_RuntimeError,
209 "%s: unable to convert Unicode string to UTF-8.",
210 function );
211
212 return( NULL );
213 }
214 #if PY_MAJOR_VERSION >= 3
215 filename_narrow = PyBytes_AsString(
216 utf8_string_object );
217 #else
218 filename_narrow = PyString_AsString(
219 utf8_string_object );
220 #endif
221 Py_BEGIN_ALLOW_THREADS
222
223 result = libolecf_check_file_signature(
224 filename_narrow,
225 &error );
226
227 Py_END_ALLOW_THREADS
228
229 Py_DecRef(
230 utf8_string_object );
231
232 #endif /* defined( HAVE_WIDE_SYSTEM_CHARACTER ) */
233
234 if( result == -1 )
235 {
236 pyolecf_error_raise(
237 error,
238 PyExc_IOError,
239 "%s: unable to check file signature.",
240 function );
241
242 libcerror_error_free(
243 &error );
244
245 return( NULL );
246 }
247 if( result != 0 )
248 {
249 Py_IncRef(
250 (PyObject *) Py_True );
251
252 return( Py_True );
253 }
254 Py_IncRef(
255 (PyObject *) Py_False );
256
257 return( Py_False );
258 }
259 PyErr_Clear();
260
261 #if PY_MAJOR_VERSION >= 3
262 result = PyObject_IsInstance(
263 string_object,
264 (PyObject *) &PyBytes_Type );
265 #else
266 result = PyObject_IsInstance(
267 string_object,
268 (PyObject *) &PyString_Type );
269 #endif
270 if( result == -1 )
271 {
272 pyolecf_error_fetch_and_raise(
273 PyExc_RuntimeError,
274 "%s: unable to determine if string object is of type string.",
275 function );
276
277 return( NULL );
278 }
279 else if( result != 0 )
280 {
281 PyErr_Clear();
282
283 #if PY_MAJOR_VERSION >= 3
284 filename_narrow = PyBytes_AsString(
285 string_object );
286 #else
287 filename_narrow = PyString_AsString(
288 string_object );
289 #endif
290 Py_BEGIN_ALLOW_THREADS
291
292 result = libolecf_check_file_signature(
293 filename_narrow,
294 &error );
295
296 Py_END_ALLOW_THREADS
297
298 if( result == -1 )
299 {
300 pyolecf_error_raise(
301 error,
302 PyExc_IOError,
303 "%s: unable to check file signature.",
304 function );
305
306 libcerror_error_free(
307 &error );
308
309 return( NULL );
310 }
311 if( result != 0 )
312 {
313 Py_IncRef(
314 (PyObject *) Py_True );
315
316 return( Py_True );
317 }
318 Py_IncRef(
319 (PyObject *) Py_False );
320
321 return( Py_False );
322 }
323 PyErr_Format(
324 PyExc_TypeError,
325 "%s: unsupported string object type.",
326 function );
327
328 return( NULL );
329 }
330
331 /* Checks if a file has an Object Linking and Embedding (OLE) Compound File (CF) signature using a file-like object
332 * Returns a Python object if successful or NULL on error
333 */
pyolecf_check_file_signature_file_object(PyObject * self PYOLECF_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)334 PyObject *pyolecf_check_file_signature_file_object(
335 PyObject *self PYOLECF_ATTRIBUTE_UNUSED,
336 PyObject *arguments,
337 PyObject *keywords )
338 {
339 PyObject *file_object = NULL;
340 libbfio_handle_t *file_io_handle = NULL;
341 libcerror_error_t *error = NULL;
342 static char *function = "pyolecf_check_file_signature_file_object";
343 static char *keyword_list[] = { "file_object", NULL };
344 int result = 0;
345
346 PYOLECF_UNREFERENCED_PARAMETER( self )
347
348 if( PyArg_ParseTupleAndKeywords(
349 arguments,
350 keywords,
351 "O|",
352 keyword_list,
353 &file_object ) == 0 )
354 {
355 return( NULL );
356 }
357 if( pyolecf_file_object_initialize(
358 &file_io_handle,
359 file_object,
360 &error ) != 1 )
361 {
362 pyolecf_error_raise(
363 error,
364 PyExc_MemoryError,
365 "%s: unable to initialize file IO handle.",
366 function );
367
368 libcerror_error_free(
369 &error );
370
371 goto on_error;
372 }
373 Py_BEGIN_ALLOW_THREADS
374
375 result = libolecf_check_file_signature_file_io_handle(
376 file_io_handle,
377 &error );
378
379 Py_END_ALLOW_THREADS
380
381 if( result == -1 )
382 {
383 pyolecf_error_raise(
384 error,
385 PyExc_IOError,
386 "%s: unable to check file signature.",
387 function );
388
389 libcerror_error_free(
390 &error );
391
392 goto on_error;
393 }
394 if( libbfio_handle_free(
395 &file_io_handle,
396 &error ) != 1 )
397 {
398 pyolecf_error_raise(
399 error,
400 PyExc_MemoryError,
401 "%s: unable to free file IO handle.",
402 function );
403
404 libcerror_error_free(
405 &error );
406
407 goto on_error;
408 }
409 if( result != 0 )
410 {
411 Py_IncRef(
412 (PyObject *) Py_True );
413
414 return( Py_True );
415 }
416 Py_IncRef(
417 (PyObject *) Py_False );
418
419 return( Py_False );
420
421 on_error:
422 if( file_io_handle != NULL )
423 {
424 libbfio_handle_free(
425 &file_io_handle,
426 NULL );
427 }
428 return( NULL );
429 }
430
431 /* Creates a new file object and opens it
432 * Returns a Python object if successful or NULL on error
433 */
pyolecf_open_new_file(PyObject * self PYOLECF_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)434 PyObject *pyolecf_open_new_file(
435 PyObject *self PYOLECF_ATTRIBUTE_UNUSED,
436 PyObject *arguments,
437 PyObject *keywords )
438 {
439 pyolecf_file_t *pyolecf_file = NULL;
440 static char *function = "pyolecf_open_new_file";
441
442 PYOLECF_UNREFERENCED_PARAMETER( self )
443
444 /* PyObject_New does not invoke tp_init
445 */
446 pyolecf_file = PyObject_New(
447 struct pyolecf_file,
448 &pyolecf_file_type_object );
449
450 if( pyolecf_file == NULL )
451 {
452 PyErr_Format(
453 PyExc_MemoryError,
454 "%s: unable to create file.",
455 function );
456
457 goto on_error;
458 }
459 if( pyolecf_file_init(
460 pyolecf_file ) != 0 )
461 {
462 goto on_error;
463 }
464 if( pyolecf_file_open(
465 pyolecf_file,
466 arguments,
467 keywords ) == NULL )
468 {
469 goto on_error;
470 }
471 return( (PyObject *) pyolecf_file );
472
473 on_error:
474 if( pyolecf_file != NULL )
475 {
476 Py_DecRef(
477 (PyObject *) pyolecf_file );
478 }
479 return( NULL );
480 }
481
482 /* Creates a new file object and opens it using a file-like object
483 * Returns a Python object if successful or NULL on error
484 */
pyolecf_open_new_file_with_file_object(PyObject * self PYOLECF_ATTRIBUTE_UNUSED,PyObject * arguments,PyObject * keywords)485 PyObject *pyolecf_open_new_file_with_file_object(
486 PyObject *self PYOLECF_ATTRIBUTE_UNUSED,
487 PyObject *arguments,
488 PyObject *keywords )
489 {
490 pyolecf_file_t *pyolecf_file = NULL;
491 static char *function = "pyolecf_open_new_file_with_file_object";
492
493 PYOLECF_UNREFERENCED_PARAMETER( self )
494
495 /* PyObject_New does not invoke tp_init
496 */
497 pyolecf_file = PyObject_New(
498 struct pyolecf_file,
499 &pyolecf_file_type_object );
500
501 if( pyolecf_file == NULL )
502 {
503 PyErr_Format(
504 PyExc_MemoryError,
505 "%s: unable to create file.",
506 function );
507
508 goto on_error;
509 }
510 if( pyolecf_file_init(
511 pyolecf_file ) != 0 )
512 {
513 goto on_error;
514 }
515 if( pyolecf_file_open_file_object(
516 pyolecf_file,
517 arguments,
518 keywords ) == NULL )
519 {
520 goto on_error;
521 }
522 return( (PyObject *) pyolecf_file );
523
524 on_error:
525 if( pyolecf_file != NULL )
526 {
527 Py_DecRef(
528 (PyObject *) pyolecf_file );
529 }
530 return( NULL );
531 }
532
533 #if PY_MAJOR_VERSION >= 3
534
535 /* The pyolecf module definition
536 */
537 PyModuleDef pyolecf_module_definition = {
538 PyModuleDef_HEAD_INIT,
539
540 /* m_name */
541 "pyolecf",
542 /* m_doc */
543 "Python libolecf module (pyolecf).",
544 /* m_size */
545 -1,
546 /* m_methods */
547 pyolecf_module_methods,
548 /* m_reload */
549 NULL,
550 /* m_traverse */
551 NULL,
552 /* m_clear */
553 NULL,
554 /* m_free */
555 NULL,
556 };
557
558 #endif /* PY_MAJOR_VERSION >= 3 */
559
560 /* Initializes the pyolecf module
561 */
562 #if PY_MAJOR_VERSION >= 3
PyInit_pyolecf(void)563 PyMODINIT_FUNC PyInit_pyolecf(
564 void )
565 #else
566 PyMODINIT_FUNC initpyolecf(
567 void )
568 #endif
569 {
570 PyObject *module = NULL;
571 PyGILState_STATE gil_state = 0;
572
573 #if defined( HAVE_DEBUG_OUTPUT )
574 libolecf_notify_set_stream(
575 stderr,
576 NULL );
577 libolecf_notify_set_verbose(
578 1 );
579 #endif
580
581 /* Create the module
582 * This function must be called before grabbing the GIL
583 * otherwise the module will segfault on a version mismatch
584 */
585 #if PY_MAJOR_VERSION >= 3
586 module = PyModule_Create(
587 &pyolecf_module_definition );
588 #else
589 module = Py_InitModule3(
590 "pyolecf",
591 pyolecf_module_methods,
592 "Python libolecf module (pyolecf)." );
593 #endif
594 if( module == NULL )
595 {
596 #if PY_MAJOR_VERSION >= 3
597 return( NULL );
598 #else
599 return;
600 #endif
601 }
602 #if PY_VERSION_HEX < 0x03070000
603 PyEval_InitThreads();
604 #endif
605 gil_state = PyGILState_Ensure();
606
607 /* Setup the file type object
608 */
609 pyolecf_file_type_object.tp_new = PyType_GenericNew;
610
611 if( PyType_Ready(
612 &pyolecf_file_type_object ) < 0 )
613 {
614 goto on_error;
615 }
616 Py_IncRef(
617 (PyObject *) &pyolecf_file_type_object );
618
619 PyModule_AddObject(
620 module,
621 "file",
622 (PyObject *) &pyolecf_file_type_object );
623
624 /* Setup the item type object
625 */
626 pyolecf_item_type_object.tp_new = PyType_GenericNew;
627
628 if( PyType_Ready(
629 &pyolecf_item_type_object ) < 0 )
630 {
631 goto on_error;
632 }
633 Py_IncRef(
634 (PyObject *) &pyolecf_item_type_object );
635
636 PyModule_AddObject(
637 module,
638 "item",
639 (PyObject *) &pyolecf_item_type_object );
640
641 /* Setup the item_types type object
642 */
643 pyolecf_item_types_type_object.tp_new = PyType_GenericNew;
644
645 if( pyolecf_item_types_init_type(
646 &pyolecf_item_types_type_object ) != 1 )
647 {
648 goto on_error;
649 }
650 if( PyType_Ready(
651 &pyolecf_item_types_type_object ) < 0 )
652 {
653 goto on_error;
654 }
655 Py_IncRef(
656 (PyObject *) &pyolecf_item_types_type_object );
657
658 PyModule_AddObject(
659 module,
660 "item_types",
661 (PyObject *) &pyolecf_item_types_type_object );
662
663 /* Setup the items type object
664 */
665 pyolecf_items_type_object.tp_new = PyType_GenericNew;
666
667 if( PyType_Ready(
668 &pyolecf_items_type_object ) < 0 )
669 {
670 goto on_error;
671 }
672 Py_IncRef(
673 (PyObject *) &pyolecf_items_type_object );
674
675 PyModule_AddObject(
676 module,
677 "items",
678 (PyObject *) &pyolecf_items_type_object );
679
680 /* Setup the property_section type object
681 */
682 pyolecf_property_section_type_object.tp_new = PyType_GenericNew;
683
684 if( PyType_Ready(
685 &pyolecf_property_section_type_object ) < 0 )
686 {
687 goto on_error;
688 }
689 Py_IncRef(
690 (PyObject *) &pyolecf_property_section_type_object );
691
692 PyModule_AddObject(
693 module,
694 "property_section",
695 (PyObject *) &pyolecf_property_section_type_object );
696
697 /* Setup the property_sections type object
698 */
699 pyolecf_property_sections_type_object.tp_new = PyType_GenericNew;
700
701 if( PyType_Ready(
702 &pyolecf_property_sections_type_object ) < 0 )
703 {
704 goto on_error;
705 }
706 Py_IncRef(
707 (PyObject *) &pyolecf_property_sections_type_object );
708
709 PyModule_AddObject(
710 module,
711 "property_sections",
712 (PyObject *) &pyolecf_property_sections_type_object );
713
714 /* Setup the property_set type object
715 */
716 pyolecf_property_set_type_object.tp_new = PyType_GenericNew;
717
718 if( PyType_Ready(
719 &pyolecf_property_set_type_object ) < 0 )
720 {
721 goto on_error;
722 }
723 Py_IncRef(
724 (PyObject *) &pyolecf_property_set_type_object );
725
726 PyModule_AddObject(
727 module,
728 "property_set",
729 (PyObject *) &pyolecf_property_set_type_object );
730
731 /* Setup the property_set_stream type object
732 */
733 pyolecf_property_set_stream_type_object.tp_new = PyType_GenericNew;
734
735 if( PyType_Ready(
736 &pyolecf_property_set_stream_type_object ) < 0 )
737 {
738 goto on_error;
739 }
740 Py_IncRef(
741 (PyObject *) &pyolecf_property_set_stream_type_object );
742
743 PyModule_AddObject(
744 module,
745 "property_set_stream",
746 (PyObject *) &pyolecf_property_set_stream_type_object );
747
748 /* Setup the stream type object
749 */
750 pyolecf_stream_type_object.tp_new = PyType_GenericNew;
751
752 if( PyType_Ready(
753 &pyolecf_stream_type_object ) < 0 )
754 {
755 goto on_error;
756 }
757 Py_IncRef(
758 (PyObject *) &pyolecf_stream_type_object );
759
760 PyModule_AddObject(
761 module,
762 "stream",
763 (PyObject *) &pyolecf_stream_type_object );
764
765 /* Setup the property_value type object
766 */
767 pyolecf_property_value_type_object.tp_new = PyType_GenericNew;
768
769 if( PyType_Ready(
770 &pyolecf_property_value_type_object ) < 0 )
771 {
772 goto on_error;
773 }
774 Py_IncRef(
775 (PyObject *) &pyolecf_property_value_type_object );
776
777 PyModule_AddObject(
778 module,
779 "property_value",
780 (PyObject *) &pyolecf_property_value_type_object );
781
782 /* Setup the property_values type object
783 */
784 pyolecf_property_values_type_object.tp_new = PyType_GenericNew;
785
786 if( PyType_Ready(
787 &pyolecf_property_values_type_object ) < 0 )
788 {
789 goto on_error;
790 }
791 Py_IncRef(
792 (PyObject *) &pyolecf_property_values_type_object );
793
794 PyModule_AddObject(
795 module,
796 "property_values",
797 (PyObject *) &pyolecf_property_values_type_object );
798
799 /* Setup the value_types type object
800 */
801 pyolecf_value_types_type_object.tp_new = PyType_GenericNew;
802
803 if( pyolecf_value_types_init_type(
804 &pyolecf_value_types_type_object ) != 1 )
805 {
806 goto on_error;
807 }
808 if( PyType_Ready(
809 &pyolecf_value_types_type_object ) < 0 )
810 {
811 goto on_error;
812 }
813 Py_IncRef(
814 (PyObject *) &pyolecf_value_types_type_object );
815
816 PyModule_AddObject(
817 module,
818 "value_types",
819 (PyObject *) &pyolecf_value_types_type_object );
820
821 PyGILState_Release(
822 gil_state );
823
824 #if PY_MAJOR_VERSION >= 3
825 return( module );
826 #else
827 return;
828 #endif
829
830 on_error:
831 PyGILState_Release(
832 gil_state );
833
834 #if PY_MAJOR_VERSION >= 3
835 return( NULL );
836 #else
837 return;
838 #endif
839 }
840
841