diff --git a/msvscpp/pypff/pypff.vcproj b/msvscpp/pypff/pypff.vcproj index 8f29ad60..afa5a636 100644 --- a/msvscpp/pypff/pypff.vcproj +++ b/msvscpp/pypff/pypff.vcproj @@ -233,6 +233,18 @@ RelativePath="..\..\pypff\pypff_message.c" > + + + + + + @@ -253,6 +265,10 @@ RelativePath="..\..\pypff\pypff_record_sets.c" > + + + + + + + + @@ -363,6 +391,10 @@ RelativePath="..\..\pypff\pypff_record_sets.h" > + + diff --git a/pypff/Makefile.am b/pypff/Makefile.am index 330f0a39..3be45099 100644 --- a/pypff/Makefile.am +++ b/pypff/Makefile.am @@ -35,12 +35,16 @@ pypff_la_SOURCES = \ pypff_libclocale.h \ pypff_libpff.h \ pypff_message.c pypff_message.h \ + pypff_message_store.c pypff_message_store.h \ + pypff_multi_value.c pypff_multi_value.h \ + pypff_name_to_id_map_entry.c pypff_name_to_id_map_entry.h \ pypff_python.h \ pypff_recipients.c pypff_recipients.h \ pypff_record_entry.c pypff_record_entry.h \ pypff_record_entries.c pypff_record_entries.h \ pypff_record_set.c pypff_record_set.h \ pypff_record_sets.c pypff_record_sets.h \ + pypff_task.c pypff_task.h \ pypff_unused.h pypff_la_LIBADD = \ diff --git a/pypff/pypff.c b/pypff/pypff.c index 51623620..418708c8 100644 --- a/pypff/pypff.c +++ b/pypff/pypff.c @@ -39,12 +39,16 @@ #include "pypff_libcerror.h" #include "pypff_libpff.h" #include "pypff_message.h" +#include "pypff_message_store.h" +#include "pypff_multi_value.h" +#include "pypff_name_to_id_map_entry.h" #include "pypff_python.h" #include "pypff_recipients.h" #include "pypff_record_entries.h" #include "pypff_record_entry.h" #include "pypff_record_set.h" #include "pypff_record_sets.h" +#include "pypff_task.h" #include "pypff_unused.h" #if !defined( LIBPFF_HAVE_BFIO ) @@ -681,6 +685,23 @@ PyMODINIT_FUNC initpypff( "item", (PyObject *) &pypff_item_type_object ); + /* Setup the message_store type object + */ + pypff_message_store_type_object.tp_new = PyType_GenericNew; + + if( PyType_Ready( + &pypff_message_store_type_object ) < 0 ) + { + goto on_error; + } + Py_IncRef( + (PyObject *) &pypff_message_store_type_object ); + + PyModule_AddObject( + module, + "message_store", + (PyObject *) &pypff_message_store_type_object ); + /* Setup the items type object */ pypff_items_type_object.tp_new = PyType_GenericNew; @@ -715,6 +736,23 @@ PyMODINIT_FUNC initpypff( "message", (PyObject *) &pypff_message_type_object ); + /* Setup the task type object + */ + pypff_task_type_object.tp_new = PyType_GenericNew; + + if( PyType_Ready( + &pypff_task_type_object ) < 0 ) + { + goto on_error; + } + Py_IncRef( + (PyObject *) &pypff_task_type_object ); + + PyModule_AddObject( + module, + "task", + (PyObject *) &pypff_task_type_object ); + /* Setup the recipients type object */ pypff_recipients_type_object.tp_new = PyType_GenericNew; @@ -732,6 +770,40 @@ PyMODINIT_FUNC initpypff( "recipients", (PyObject *) &pypff_recipients_type_object ); + /* Setup the multi_value type object + */ + pypff_multi_value_type_object.tp_new = PyType_GenericNew; + + if( PyType_Ready( + &pypff_multi_value_type_object ) < 0 ) + { + goto on_error; + } + Py_IncRef( + (PyObject *) &pypff_multi_value_type_object ); + + PyModule_AddObject( + module, + "multi_value", + (PyObject *) &pypff_multi_value_type_object ); + + /* Setup the name_to_id_map_entry type object + */ + pypff_name_to_id_map_entry_type_object.tp_new = PyType_GenericNew; + + if( PyType_Ready( + &pypff_name_to_id_map_entry_type_object ) < 0 ) + { + goto on_error; + } + Py_IncRef( + (PyObject *) &pypff_name_to_id_map_entry_type_object ); + + PyModule_AddObject( + module, + "name_to_id_map_entry", + (PyObject *) &pypff_name_to_id_map_entry_type_object ); + /* Setup the record_entries type object */ pypff_record_entries_type_object.tp_new = PyType_GenericNew; diff --git a/pypff/pypff_file.c b/pypff/pypff_file.c index ef988338..789e1673 100644 --- a/pypff/pypff_file.c +++ b/pypff/pypff_file.c @@ -41,7 +41,10 @@ #include "pypff_libclocale.h" #include "pypff_libpff.h" #include "pypff_message.h" +#include "pypff_message_store.h" #include "pypff_python.h" +#include "pypff_recipients.h" +#include "pypff_task.h" #include "pypff_unused.h" #if !defined( LIBPFF_HAVE_BFIO ) @@ -1458,19 +1461,23 @@ PyTypeObject *pypff_file_get_item_type_object( case LIBPFF_ITEM_TYPE_RSS_FEED: case LIBPFF_ITEM_TYPE_SHARING: case LIBPFF_ITEM_TYPE_SMS: - case LIBPFF_ITEM_TYPE_TASK: - case LIBPFF_ITEM_TYPE_TASK_REQUEST: case LIBPFF_ITEM_TYPE_VOICEMAIL: return( &pypff_message_type_object ); + case LIBPFF_ITEM_TYPE_TASK: + case LIBPFF_ITEM_TYPE_TASK_REQUEST: + return( &pypff_task_type_object ); + case LIBPFF_ITEM_TYPE_ATTACHMENT: return( &pypff_attachment_type_object ); case LIBPFF_ITEM_TYPE_FOLDER: return( &pypff_folder_type_object ); - case LIBPFF_ITEM_TYPE_ATTACHMENTS: case LIBPFF_ITEM_TYPE_RECIPIENTS: + return( &pypff_recipients_type_object ); + + case LIBPFF_ITEM_TYPE_ATTACHMENTS: case LIBPFF_ITEM_TYPE_SUB_ASSOCIATED_CONTENTS: case LIBPFF_ITEM_TYPE_SUB_FOLDERS: case LIBPFF_ITEM_TYPE_SUB_MESSAGES: @@ -1562,7 +1569,6 @@ PyObject *pypff_file_get_message_store( PyObject *arguments PYPFF_ATTRIBUTE_UNUSED ) { PyObject *message_store_object = NULL; - PyTypeObject *type_object = NULL; libcerror_error_t *error = NULL; libpff_item_t *message_store = NULL; static char *function = "pypff_file_get_message_store"; @@ -1608,20 +1614,8 @@ PyObject *pypff_file_get_message_store( return( Py_None ); } - type_object = pypff_file_get_item_type_object( - message_store ); - - if( type_object == NULL ) - { - PyErr_Format( - PyExc_IOError, - "%s: unable to retrieve message store type object.", - function ); - - goto on_error; - } message_store_object = pypff_item_new( - type_object, + &pypff_message_store_type_object, message_store, (PyObject *) pypff_file ); diff --git a/pypff/pypff_item.c b/pypff/pypff_item.c index 7819159f..03769b46 100644 --- a/pypff/pypff_item.c +++ b/pypff/pypff_item.c @@ -26,6 +26,7 @@ #include #endif +#include "pypff_attachment.h" #include "pypff_error.h" #include "pypff_folder.h" #include "pypff_item.h" @@ -34,8 +35,10 @@ #include "pypff_libpff.h" #include "pypff_message.h" #include "pypff_python.h" +#include "pypff_recipients.h" #include "pypff_record_set.h" #include "pypff_record_sets.h" +#include "pypff_task.h" #include "pypff_unused.h" PyMethodDef pypff_item_object_methods[] = { @@ -887,17 +890,23 @@ PyTypeObject *pypff_item_get_item_type_object( case LIBPFF_ITEM_TYPE_RSS_FEED: case LIBPFF_ITEM_TYPE_SHARING: case LIBPFF_ITEM_TYPE_SMS: - case LIBPFF_ITEM_TYPE_TASK: - case LIBPFF_ITEM_TYPE_TASK_REQUEST: case LIBPFF_ITEM_TYPE_VOICEMAIL: return( &pypff_message_type_object ); + case LIBPFF_ITEM_TYPE_TASK: + case LIBPFF_ITEM_TYPE_TASK_REQUEST: + return( &pypff_task_type_object ); + + case LIBPFF_ITEM_TYPE_ATTACHMENT: + return( &pypff_attachment_type_object ); + case LIBPFF_ITEM_TYPE_FOLDER: return( &pypff_folder_type_object ); - case LIBPFF_ITEM_TYPE_ATTACHMENT: - case LIBPFF_ITEM_TYPE_ATTACHMENTS: case LIBPFF_ITEM_TYPE_RECIPIENTS: + return( &pypff_recipients_type_object ); + + case LIBPFF_ITEM_TYPE_ATTACHMENTS: case LIBPFF_ITEM_TYPE_SUB_ASSOCIATED_CONTENTS: case LIBPFF_ITEM_TYPE_SUB_FOLDERS: case LIBPFF_ITEM_TYPE_SUB_MESSAGES: diff --git a/pypff/pypff_message_store.c b/pypff/pypff_message_store.c new file mode 100644 index 00000000..8bb99739 --- /dev/null +++ b/pypff/pypff_message_store.c @@ -0,0 +1,134 @@ +/* + * Python object wrapper of a libpff message store item + * + * Copyright (C) 2008-2026, Joachim Metz + * + * Refer to AUTHORS for acknowledgements. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "pypff_item.h" +#include "pypff_message_store.h" +#include "pypff_python.h" + +PyMethodDef pypff_message_store_object_methods[] = { + + /* Sentinel */ + { NULL, NULL, 0, NULL } +}; + +PyGetSetDef pypff_message_store_object_get_set_definitions[] = { + + /* Sentinel */ + { NULL, NULL, NULL, NULL, NULL } +}; + +PyTypeObject pypff_message_store_type_object = { + PyVarObject_HEAD_INIT( NULL, 0 ) + + /* tp_name */ + "pypff.message_store", + /* tp_basicsize */ + sizeof( pypff_item_t ), + /* tp_itemsize */ + 0, + /* tp_dealloc */ + 0, + /* tp_print */ + 0, + /* tp_getattr */ + 0, + /* tp_setattr */ + 0, + /* tp_compare */ + 0, + /* tp_repr */ + 0, + /* tp_as_number */ + 0, + /* tp_as_sequence */ + 0, + /* tp_as_mapping */ + 0, + /* tp_hash */ + 0, + /* tp_call */ + 0, + /* tp_str */ + 0, + /* tp_getattro */ + 0, + /* tp_setattro */ + 0, + /* tp_as_buffer */ + 0, + /* tp_flags */ + Py_TPFLAGS_DEFAULT, + /* tp_doc */ + "pypff message store object (wraps libpff_item_t)", + /* tp_traverse */ + 0, + /* tp_clear */ + 0, + /* tp_richcompare */ + 0, + /* tp_weaklistoffset */ + 0, + /* tp_iter */ + 0, + /* tp_iternext */ + 0, + /* tp_methods */ + pypff_message_store_object_methods, + /* tp_members */ + 0, + /* tp_getset */ + pypff_message_store_object_get_set_definitions, + /* tp_base */ + &pypff_item_type_object, + /* tp_dict */ + 0, + /* tp_descr_get */ + 0, + /* tp_descr_set */ + 0, + /* tp_dictoffset */ + 0, + /* tp_init */ + 0, + /* tp_alloc */ + 0, + /* tp_new */ + 0, + /* tp_free */ + 0, + /* tp_is_gc */ + 0, + /* tp_bases */ + NULL, + /* tp_mro */ + NULL, + /* tp_cache */ + NULL, + /* tp_subclasses */ + NULL, + /* tp_weaklist */ + NULL, + /* tp_del */ + 0 +}; diff --git a/pypff/pypff_message_store.h b/pypff/pypff_message_store.h new file mode 100644 index 00000000..bed195a2 --- /dev/null +++ b/pypff/pypff_message_store.h @@ -0,0 +1,42 @@ +/* + * Python object wrapper of a libpff message store item + * + * Copyright (C) 2008-2026, Joachim Metz + * + * Refer to AUTHORS for acknowledgements. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#if !defined( _PYPFF_MESSAGE_STORE_H ) +#define _PYPFF_MESSAGE_STORE_H + +#include +#include + +#include "pypff_item.h" +#include "pypff_python.h" + +#if defined( __cplusplus ) +extern "C" { +#endif + +extern PyMethodDef pypff_message_store_object_methods[]; +extern PyTypeObject pypff_message_store_type_object; + +#if defined( __cplusplus ) +} +#endif + +#endif /* !defined( _PYPFF_MESSAGE_STORE_H ) */ diff --git a/pypff/pypff_multi_value.c b/pypff/pypff_multi_value.c new file mode 100644 index 00000000..89738606 --- /dev/null +++ b/pypff/pypff_multi_value.c @@ -0,0 +1,1091 @@ +/* + * Python object wrapper of libpff_multi_value_t + * + * Copyright (C) 2008-2026, Joachim Metz + * + * Refer to AUTHORS for acknowledgements. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include +#include + +#if defined( HAVE_STDLIB_H ) || defined( HAVE_WINAPI ) +#include +#endif + +#include "pypff_datetime.h" +#include "pypff_error.h" +#include "pypff_integer.h" +#include "pypff_libcerror.h" +#include "pypff_libpff.h" +#include "pypff_multi_value.h" +#include "pypff_python.h" +#include "pypff_unused.h" + +PyMethodDef pypff_multi_value_object_methods[] = { + + { "get_number_of_values", + (PyCFunction) pypff_multi_value_get_number_of_values, + METH_NOARGS, + "get_number_of_values() -> Integer\n" + "\n" + "Retrieves the number of values." }, + + { "get_value_type", + (PyCFunction) pypff_multi_value_get_value_type, + METH_VARARGS | METH_KEYWORDS, + "get_value_type(value_index) -> Integer\n" + "\n" + "Retrieves the type of the value specified by the index." }, + + { "get_value", + (PyCFunction) pypff_multi_value_get_value, + METH_VARARGS | METH_KEYWORDS, + "get_value(value_index) -> Bytes or None\n" + "\n" + "Retrieves the value specified by the index as bytes." }, + + { "get_value_as_integer", + (PyCFunction) pypff_multi_value_get_value_as_integer, + METH_VARARGS | METH_KEYWORDS, + "get_value_as_integer(value_index) -> Integer\n" + "\n" + "Retrieves the value specified by the index as an integer." }, + + { "get_value_as_datetime", + (PyCFunction) pypff_multi_value_get_value_as_datetime, + METH_VARARGS | METH_KEYWORDS, + "get_value_as_datetime(value_index) -> Datetime\n" + "\n" + "Retrieves the value specified by the index as a datetime object." }, + + { "get_value_as_string", + (PyCFunction) pypff_multi_value_get_value_as_string, + METH_VARARGS | METH_KEYWORDS, + "get_value_as_string(value_index) -> Unicode string or None\n" + "\n" + "Retrieves the value specified by the index as a string." }, + + { "get_value_as_binary_data", + (PyCFunction) pypff_multi_value_get_value_as_binary_data, + METH_VARARGS | METH_KEYWORDS, + "get_value_as_binary_data(value_index) -> Bytes or None\n" + "\n" + "Retrieves the value specified by the index as binary data." }, + + { "get_value_as_guid", + (PyCFunction) pypff_multi_value_get_value_as_guid, + METH_VARARGS | METH_KEYWORDS, + "get_value_as_guid(value_index) -> Bytes\n" + "\n" + "Retrieves the value specified by the index as a GUID." }, + + /* Sentinel */ + { NULL, NULL, 0, NULL } +}; + +PyGetSetDef pypff_multi_value_object_get_set_definitions[] = { + + { "number_of_values", + (getter) pypff_multi_value_get_number_of_values, + (setter) 0, + "The number of values.", + NULL }, + + /* Sentinel */ + { NULL, NULL, NULL, NULL, NULL } +}; + +PyTypeObject pypff_multi_value_type_object = { + PyVarObject_HEAD_INIT( NULL, 0 ) + + /* tp_name */ + "pypff.multi_value", + /* tp_basicsize */ + sizeof( pypff_multi_value_t ), + /* tp_itemsize */ + 0, + /* tp_dealloc */ + (destructor) pypff_multi_value_free, + /* tp_print */ + 0, + /* tp_getattr */ + 0, + /* tp_setattr */ + 0, + /* tp_compare */ + 0, + /* tp_repr */ + 0, + /* tp_as_number */ + 0, + /* tp_as_sequence */ + 0, + /* tp_as_mapping */ + 0, + /* tp_hash */ + 0, + /* tp_call */ + 0, + /* tp_str */ + 0, + /* tp_getattro */ + 0, + /* tp_setattro */ + 0, + /* tp_as_buffer */ + 0, + /* tp_flags */ + Py_TPFLAGS_DEFAULT, + /* tp_doc */ + "pypff multi value object (wraps libpff_multi_value_t)", + /* tp_traverse */ + 0, + /* tp_clear */ + 0, + /* tp_richcompare */ + 0, + /* tp_weaklistoffset */ + 0, + /* tp_iter */ + 0, + /* tp_iternext */ + 0, + /* tp_methods */ + pypff_multi_value_object_methods, + /* tp_members */ + 0, + /* tp_getset */ + pypff_multi_value_object_get_set_definitions, + /* tp_base */ + 0, + /* tp_dict */ + 0, + /* tp_descr_get */ + 0, + /* tp_descr_set */ + 0, + /* tp_dictoffset */ + 0, + /* tp_init */ + (initproc) pypff_multi_value_init, + /* tp_alloc */ + 0, + /* tp_new */ + 0, + /* tp_free */ + 0, + /* tp_is_gc */ + 0, + /* tp_bases */ + NULL, + /* tp_mro */ + NULL, + /* tp_cache */ + NULL, + /* tp_subclasses */ + NULL, + /* tp_weaklist */ + NULL, + /* tp_del */ + 0 +}; + +/* Creates a new multi value object + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_multi_value_new( + libpff_multi_value_t *multi_value, + PyObject *parent_object ) +{ + pypff_multi_value_t *pypff_multi_value = NULL; + static char *function = "pypff_multi_value_new"; + + if( multi_value == NULL ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid multi value.", + function ); + + return( NULL ); + } + pypff_multi_value = PyObject_New( + struct pypff_multi_value, + &pypff_multi_value_type_object ); + + if( pypff_multi_value == NULL ) + { + PyErr_Format( + PyExc_MemoryError, + "%s: unable to initialize multi value.", + function ); + + return( NULL ); + } + pypff_multi_value->multi_value = multi_value; + pypff_multi_value->parent_object = parent_object; + + if( parent_object != NULL ) + { + Py_IncRef( + parent_object ); + } + return( (PyObject *) pypff_multi_value ); +} + +/* Initializes a multi value object + * Returns 0 if successful or -1 on error + */ +int pypff_multi_value_init( + pypff_multi_value_t *pypff_multi_value ) +{ + static char *function = "pypff_multi_value_init"; + + if( pypff_multi_value == NULL ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid multi value.", + function ); + + return( -1 ); + } + pypff_multi_value->multi_value = NULL; + pypff_multi_value->parent_object = NULL; + + return( 0 ); +} + +/* Frees a multi value object + */ +void pypff_multi_value_free( + pypff_multi_value_t *pypff_multi_value ) +{ + struct _typeobject *ob_type = NULL; + libcerror_error_t *error = NULL; + static char *function = "pypff_multi_value_free"; + int result = 0; + + if( pypff_multi_value == NULL ) + { + return; + } + ob_type = Py_TYPE( + pypff_multi_value ); + + if( pypff_multi_value->multi_value != NULL ) + { + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_free( + &( pypff_multi_value->multi_value ), + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to free libpff multi value.", + function ); + + libcerror_error_free( + &error ); + } + } + if( pypff_multi_value->parent_object != NULL ) + { + Py_DecRef( + pypff_multi_value->parent_object ); + } + if( ( ob_type != NULL ) + && ( ob_type->tp_free != NULL ) ) + { + ob_type->tp_free( + (PyObject *) pypff_multi_value ); + } +} + +/* Retrieves the number of values + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_multi_value_get_number_of_values( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments PYPFF_ATTRIBUTE_UNUSED ) +{ + PyObject *integer_object = NULL; + libcerror_error_t *error = NULL; + static char *function = "pypff_multi_value_get_number_of_values"; + int number_of_values = 0; + int result = 0; + + PYPFF_UNREFERENCED_PARAMETER( arguments ) + + if( ( pypff_multi_value == NULL ) + || ( pypff_multi_value->multi_value == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid multi value.", + function ); + + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_number_of_values( + pypff_multi_value->multi_value, + &number_of_values, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve number of values.", + function ); + + libcerror_error_free( + &error ); + + return( NULL ); + } +#if PY_MAJOR_VERSION >= 3 + integer_object = PyLong_FromLong( + (long) number_of_values ); +#else + integer_object = PyInt_FromLong( + (long) number_of_values ); +#endif + return( integer_object ); +} + +/* Retrieves the type of a specific value + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_multi_value_get_value_type( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ) +{ + PyObject *integer_object = NULL; + libcerror_error_t *error = NULL; + uint8_t *value_data = NULL; + static char *function = "pypff_multi_value_get_value_type"; + static char *keyword_list[] = { "value_index", NULL }; + size_t value_data_size = 0; + uint32_t value_type = 0; + int value_index = 0; + int result = 0; + + if( ( pypff_multi_value == NULL ) + || ( pypff_multi_value->multi_value == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid multi value.", + function ); + + return( NULL ); + } + if( PyArg_ParseTupleAndKeywords( + arguments, + keywords, + "i", + keyword_list, + &value_index ) == 0 ) + { + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value( + pypff_multi_value->multi_value, + value_index, + &value_type, + &value_data, + &value_data_size, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve value: %d type.", + function, + value_index ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + integer_object = pypff_integer_unsigned_new_from_64bit( + (uint64_t) value_type ); + + return( integer_object ); +} + +/* Retrieves a specific value as bytes + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_multi_value_get_value( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ) +{ + PyObject *bytes_object = NULL; + libcerror_error_t *error = NULL; + uint8_t *value_data = NULL; + static char *function = "pypff_multi_value_get_value"; + static char *keyword_list[] = { "value_index", NULL }; + size_t value_data_size = 0; + uint32_t value_type = 0; + int value_index = 0; + int result = 0; + + if( ( pypff_multi_value == NULL ) + || ( pypff_multi_value->multi_value == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid multi value.", + function ); + + return( NULL ); + } + if( PyArg_ParseTupleAndKeywords( + arguments, + keywords, + "i", + keyword_list, + &value_index ) == 0 ) + { + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value( + pypff_multi_value->multi_value, + value_index, + &value_type, + &value_data, + &value_data_size, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve value: %d.", + function, + value_index ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + if( ( value_data == NULL ) + || ( value_data_size == 0 ) ) + { + Py_IncRef( + Py_None ); + + return( Py_None ); + } + bytes_object = PyBytes_FromStringAndSize( + (char *) value_data, + (Py_ssize_t) value_data_size ); + + return( bytes_object ); +} + +/* Retrieves a specific value as an integer + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_multi_value_get_value_as_integer( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ) +{ + PyObject *integer_object = NULL; + libcerror_error_t *error = NULL; + uint8_t *value_data = NULL; + static char *function = "pypff_multi_value_get_value_as_integer"; + static char *keyword_list[] = { "value_index", NULL }; + size_t value_data_size = 0; + uint64_t value_64bit = 0; + uint32_t value_32bit = 0; + uint32_t value_type = 0; + int value_index = 0; + int result = 0; + + if( ( pypff_multi_value == NULL ) + || ( pypff_multi_value->multi_value == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid multi value.", + function ); + + return( NULL ); + } + if( PyArg_ParseTupleAndKeywords( + arguments, + keywords, + "i", + keyword_list, + &value_index ) == 0 ) + { + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value( + pypff_multi_value->multi_value, + value_index, + &value_type, + &value_data, + &value_data_size, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve value: %d type.", + function, + value_index ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + switch( value_type ) + { + case LIBPFF_VALUE_TYPE_INTEGER_16BIT_SIGNED: + case LIBPFF_VALUE_TYPE_INTEGER_32BIT_SIGNED: + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value_32bit( + pypff_multi_value->multi_value, + value_index, + &value_32bit, + &error ); + + Py_END_ALLOW_THREADS + +#if PY_MAJOR_VERSION >= 3 + if( value_type == LIBPFF_VALUE_TYPE_INTEGER_16BIT_SIGNED ) + { + integer_object = PyLong_FromLong( + (long) ( (int16_t) value_32bit ) ); + } + else + { + integer_object = PyLong_FromLong( + (long) ( (int32_t) value_32bit ) ); + } +#else + if( value_type == LIBPFF_VALUE_TYPE_INTEGER_16BIT_SIGNED ) + { + integer_object = PyInt_FromLong( + (long) ( (int16_t) value_32bit ) ); + } + else + { + integer_object = PyInt_FromLong( + (long) ( (int32_t) value_32bit ) ); + } +#endif + break; + + case LIBPFF_VALUE_TYPE_INTEGER_64BIT_SIGNED: + case LIBPFF_VALUE_TYPE_FILETIME: + case LIBPFF_VALUE_TYPE_FLOATINGTIME: + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value_64bit( + pypff_multi_value->multi_value, + value_index, + &value_64bit, + &error ); + + Py_END_ALLOW_THREADS + + if( value_type == LIBPFF_VALUE_TYPE_INTEGER_64BIT_SIGNED ) + { + integer_object = pypff_integer_signed_new_from_64bit( + (int64_t) value_64bit ); + } + else + { + integer_object = pypff_integer_unsigned_new_from_64bit( + value_64bit ); + } + break; + + default: + PyErr_Format( + PyExc_IOError, + "%s: value is not an integer type.", + function ); + + return( NULL ); + } + if( result != 1 ) + { + Py_XDECREF( + integer_object ); + + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve value: %d as an integer.", + function, + value_index ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + return( integer_object ); +} + +/* Retrieves a specific value as a datetime object + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_multi_value_get_value_as_datetime( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ) +{ + PyObject *datetime_object = NULL; + libcerror_error_t *error = NULL; + uint8_t *value_data = NULL; + static char *function = "pypff_multi_value_get_value_as_datetime"; + static char *keyword_list[] = { "value_index", NULL }; + size_t value_data_size = 0; + uint64_t filetime = 0; + uint32_t value_type = 0; + int value_index = 0; + int result = 0; + + if( ( pypff_multi_value == NULL ) + || ( pypff_multi_value->multi_value == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid multi value.", + function ); + + return( NULL ); + } + if( PyArg_ParseTupleAndKeywords( + arguments, + keywords, + "i", + keyword_list, + &value_index ) == 0 ) + { + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value( + pypff_multi_value->multi_value, + value_index, + &value_type, + &value_data, + &value_data_size, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve value: %d type.", + function, + value_index ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + if( value_type != LIBPFF_VALUE_TYPE_FILETIME ) + { + PyErr_Format( + PyExc_IOError, + "%s: value is not a datetime type.", + function ); + + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value_filetime( + pypff_multi_value->multi_value, + value_index, + &filetime, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve value: %d as a datetime.", + function, + value_index ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + datetime_object = pypff_datetime_new_from_filetime( + filetime ); + + return( datetime_object ); +} + +/* Retrieves a specific value as a string + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_multi_value_get_value_as_string( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ) +{ + PyObject *string_object = NULL; + libcerror_error_t *error = NULL; + char *utf8_string = NULL; + static char *function = "pypff_multi_value_get_value_as_string"; + static char *keyword_list[] = { "value_index", NULL }; + size_t utf8_string_size = 0; + int value_index = 0; + int result = 0; + + if( ( pypff_multi_value == NULL ) + || ( pypff_multi_value->multi_value == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid multi value.", + function ); + + return( NULL ); + } + if( PyArg_ParseTupleAndKeywords( + arguments, + keywords, + "i", + keyword_list, + &value_index ) == 0 ) + { + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value_utf8_string_size( + pypff_multi_value->multi_value, + value_index, + &utf8_string_size, + &error ); + + Py_END_ALLOW_THREADS + + if( result == -1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to determine size of value: %d as UTF-8 string.", + function, + value_index ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + if( ( result == 0 ) + || ( utf8_string_size == 0 ) ) + { + Py_IncRef( + Py_None ); + + return( Py_None ); + } + utf8_string = (char *) PyMem_Malloc( + sizeof( char ) * utf8_string_size ); + + if( utf8_string == NULL ) + { + PyErr_Format( + PyExc_MemoryError, + "%s: unable to create UTF-8 string.", + function ); + + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value_utf8_string( + pypff_multi_value->multi_value, + value_index, + (uint8_t *) utf8_string, + utf8_string_size, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve value: %d as UTF-8 string.", + function, + value_index ); + + libcerror_error_free( + &error ); + + PyMem_Free( + utf8_string ); + + return( NULL ); + } + string_object = PyUnicode_DecodeUTF8( + utf8_string, + (Py_ssize_t) utf8_string_size - 1, + NULL ); + + PyMem_Free( + utf8_string ); + + return( string_object ); +} + +/* Retrieves a specific value as binary data + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_multi_value_get_value_as_binary_data( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ) +{ + PyObject *bytes_object = NULL; + libcerror_error_t *error = NULL; + uint8_t *binary_data = NULL; + static char *function = "pypff_multi_value_get_value_as_binary_data"; + static char *keyword_list[] = { "value_index", NULL }; + size_t binary_data_size = 0; + int value_index = 0; + int result = 0; + + if( ( pypff_multi_value == NULL ) + || ( pypff_multi_value->multi_value == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid multi value.", + function ); + + return( NULL ); + } + if( PyArg_ParseTupleAndKeywords( + arguments, + keywords, + "i", + keyword_list, + &value_index ) == 0 ) + { + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value_binary_data_size( + pypff_multi_value->multi_value, + value_index, + &binary_data_size, + &error ); + + Py_END_ALLOW_THREADS + + if( result == -1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to determine size of value: %d as binary data.", + function, + value_index ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + if( ( result == 0 ) + || ( binary_data_size == 0 ) ) + { + Py_IncRef( + Py_None ); + + return( Py_None ); + } + binary_data = (uint8_t *) PyMem_Malloc( + sizeof( uint8_t ) * binary_data_size ); + + if( binary_data == NULL ) + { + PyErr_Format( + PyExc_MemoryError, + "%s: unable to create binary data.", + function ); + + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value_binary_data( + pypff_multi_value->multi_value, + value_index, + binary_data, + binary_data_size, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve value: %d as binary data.", + function, + value_index ); + + libcerror_error_free( + &error ); + + PyMem_Free( + binary_data ); + + return( NULL ); + } + bytes_object = PyBytes_FromStringAndSize( + (char *) binary_data, + (Py_ssize_t) binary_data_size ); + + PyMem_Free( + binary_data ); + + return( bytes_object ); +} + +/* Retrieves a specific value as a GUID + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_multi_value_get_value_as_guid( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ) +{ + PyObject *bytes_object = NULL; + libcerror_error_t *error = NULL; + static char *function = "pypff_multi_value_get_value_as_guid"; + static char *keyword_list[] = { "value_index", NULL }; + uint8_t guid[ 16 ]; + int value_index = 0; + int result = 0; + + if( ( pypff_multi_value == NULL ) + || ( pypff_multi_value->multi_value == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid multi value.", + function ); + + return( NULL ); + } + if( PyArg_ParseTupleAndKeywords( + arguments, + keywords, + "i", + keyword_list, + &value_index ) == 0 ) + { + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_multi_value_get_value_guid( + pypff_multi_value->multi_value, + value_index, + guid, + 16, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve value: %d as a GUID.", + function, + value_index ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + bytes_object = PyBytes_FromStringAndSize( + (char *) guid, + 16 ); + + return( bytes_object ); +} diff --git a/pypff/pypff_multi_value.h b/pypff/pypff_multi_value.h new file mode 100644 index 00000000..06943143 --- /dev/null +++ b/pypff/pypff_multi_value.h @@ -0,0 +1,108 @@ +/* + * Python object wrapper of libpff_multi_value_t + * + * Copyright (C) 2008-2026, Joachim Metz + * + * Refer to AUTHORS for acknowledgements. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#if !defined( _PYPFF_MULTI_VALUE_H ) +#define _PYPFF_MULTI_VALUE_H + +#include +#include + +#include "pypff_libpff.h" +#include "pypff_python.h" + +#if defined( __cplusplus ) +extern "C" { +#endif + +typedef struct pypff_multi_value pypff_multi_value_t; + +struct pypff_multi_value +{ + /* Python object initialization + */ + PyObject_HEAD + + /* The libpff multi value + */ + libpff_multi_value_t *multi_value; + + /* The parent object + */ + PyObject *parent_object; +}; + +extern PyMethodDef pypff_multi_value_object_methods[]; +extern PyTypeObject pypff_multi_value_type_object; + +PyObject *pypff_multi_value_new( + libpff_multi_value_t *multi_value, + PyObject *parent_object ); + +int pypff_multi_value_init( + pypff_multi_value_t *pypff_multi_value ); + +void pypff_multi_value_free( + pypff_multi_value_t *pypff_multi_value ); + +PyObject *pypff_multi_value_get_number_of_values( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments ); + +PyObject *pypff_multi_value_get_value_type( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ); + +PyObject *pypff_multi_value_get_value( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ); + +PyObject *pypff_multi_value_get_value_as_integer( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ); + +PyObject *pypff_multi_value_get_value_as_datetime( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ); + +PyObject *pypff_multi_value_get_value_as_string( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ); + +PyObject *pypff_multi_value_get_value_as_binary_data( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ); + +PyObject *pypff_multi_value_get_value_as_guid( + pypff_multi_value_t *pypff_multi_value, + PyObject *arguments, + PyObject *keywords ); + +#if defined( __cplusplus ) +} +#endif + +#endif /* !defined( _PYPFF_MULTI_VALUE_H ) */ diff --git a/pypff/pypff_name_to_id_map_entry.c b/pypff/pypff_name_to_id_map_entry.c new file mode 100644 index 00000000..0bd3be83 --- /dev/null +++ b/pypff/pypff_name_to_id_map_entry.c @@ -0,0 +1,616 @@ +/* + * Python object wrapper of libpff_name_to_id_map_entry_t + * + * Copyright (C) 2008-2026, Joachim Metz + * + * Refer to AUTHORS for acknowledgements. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include +#include + +#if defined( HAVE_STDLIB_H ) || defined( HAVE_WINAPI ) +#include +#endif + +#include "pypff_error.h" +#include "pypff_integer.h" +#include "pypff_libcerror.h" +#include "pypff_libpff.h" +#include "pypff_name_to_id_map_entry.h" +#include "pypff_python.h" +#include "pypff_unused.h" + +PyMethodDef pypff_name_to_id_map_entry_object_methods[] = { + + { "get_type", + (PyCFunction) pypff_name_to_id_map_entry_get_type, + METH_NOARGS, + "get_type() -> Integer\n" + "\n" + "Retrieves the name to ID map entry type." }, + + { "get_number", + (PyCFunction) pypff_name_to_id_map_entry_get_number, + METH_NOARGS, + "get_number() -> Integer or None\n" + "\n" + "Retrieves the numeric name value." }, + + { "get_string", + (PyCFunction) pypff_name_to_id_map_entry_get_string, + METH_NOARGS, + "get_string() -> Unicode string or None\n" + "\n" + "Retrieves the string name value." }, + + { "get_guid", + (PyCFunction) pypff_name_to_id_map_entry_get_guid, + METH_NOARGS, + "get_guid() -> Bytes\n" + "\n" + "Retrieves the property-set GUID." }, + + /* Sentinel */ + { NULL, NULL, 0, NULL } +}; + +PyGetSetDef pypff_name_to_id_map_entry_object_get_set_definitions[] = { + + { "type", + (getter) pypff_name_to_id_map_entry_get_type, + (setter) 0, + "The name to ID map entry type.", + NULL }, + + { "number", + (getter) pypff_name_to_id_map_entry_get_number, + (setter) 0, + "The numeric name value.", + NULL }, + + { "string", + (getter) pypff_name_to_id_map_entry_get_string, + (setter) 0, + "The string name value.", + NULL }, + + { "guid", + (getter) pypff_name_to_id_map_entry_get_guid, + (setter) 0, + "The property-set GUID.", + NULL }, + + /* Sentinel */ + { NULL, NULL, NULL, NULL, NULL } +}; + +PyTypeObject pypff_name_to_id_map_entry_type_object = { + PyVarObject_HEAD_INIT( NULL, 0 ) + + /* tp_name */ + "pypff.name_to_id_map_entry", + /* tp_basicsize */ + sizeof( pypff_name_to_id_map_entry_t ), + /* tp_itemsize */ + 0, + /* tp_dealloc */ + (destructor) pypff_name_to_id_map_entry_free, + /* tp_print */ + 0, + /* tp_getattr */ + 0, + /* tp_setattr */ + 0, + /* tp_compare */ + 0, + /* tp_repr */ + 0, + /* tp_as_number */ + 0, + /* tp_as_sequence */ + 0, + /* tp_as_mapping */ + 0, + /* tp_hash */ + 0, + /* tp_call */ + 0, + /* tp_str */ + 0, + /* tp_getattro */ + 0, + /* tp_setattro */ + 0, + /* tp_as_buffer */ + 0, + /* tp_flags */ + Py_TPFLAGS_DEFAULT, + /* tp_doc */ + "pypff name to ID map entry object (wraps libpff_name_to_id_map_entry_t)", + /* tp_traverse */ + 0, + /* tp_clear */ + 0, + /* tp_richcompare */ + 0, + /* tp_weaklistoffset */ + 0, + /* tp_iter */ + 0, + /* tp_iternext */ + 0, + /* tp_methods */ + pypff_name_to_id_map_entry_object_methods, + /* tp_members */ + 0, + /* tp_getset */ + pypff_name_to_id_map_entry_object_get_set_definitions, + /* tp_base */ + 0, + /* tp_dict */ + 0, + /* tp_descr_get */ + 0, + /* tp_descr_set */ + 0, + /* tp_dictoffset */ + 0, + /* tp_init */ + (initproc) pypff_name_to_id_map_entry_init, + /* tp_alloc */ + 0, + /* tp_new */ + 0, + /* tp_free */ + 0, + /* tp_is_gc */ + 0, + /* tp_bases */ + NULL, + /* tp_mro */ + NULL, + /* tp_cache */ + NULL, + /* tp_subclasses */ + NULL, + /* tp_weaklist */ + NULL, + /* tp_del */ + 0 +}; + +/* Creates a new name to ID map entry object + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_name_to_id_map_entry_new( + libpff_name_to_id_map_entry_t *name_to_id_map_entry, + PyObject *parent_object ) +{ + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry = NULL; + static char *function = "pypff_name_to_id_map_entry_new"; + + if( name_to_id_map_entry == NULL ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid name to ID map entry.", + function ); + + return( NULL ); + } + pypff_name_to_id_map_entry = PyObject_New( + struct pypff_name_to_id_map_entry, + &pypff_name_to_id_map_entry_type_object ); + + if( pypff_name_to_id_map_entry == NULL ) + { + PyErr_Format( + PyExc_MemoryError, + "%s: unable to initialize name to ID map entry.", + function ); + + return( NULL ); + } + pypff_name_to_id_map_entry->name_to_id_map_entry = name_to_id_map_entry; + pypff_name_to_id_map_entry->parent_object = parent_object; + + if( parent_object != NULL ) + { + Py_IncRef( + parent_object ); + } + return( (PyObject *) pypff_name_to_id_map_entry ); +} + +/* Initializes a name to ID map entry object + * Returns 0 if successful or -1 on error + */ +int pypff_name_to_id_map_entry_init( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry ) +{ + static char *function = "pypff_name_to_id_map_entry_init"; + + if( pypff_name_to_id_map_entry == NULL ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid name to ID map entry.", + function ); + + return( -1 ); + } + pypff_name_to_id_map_entry->name_to_id_map_entry = NULL; + pypff_name_to_id_map_entry->parent_object = NULL; + + return( 0 ); +} + +/* Frees a name to ID map entry object + */ +void pypff_name_to_id_map_entry_free( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry ) +{ + struct _typeobject *ob_type = NULL; + + if( pypff_name_to_id_map_entry == NULL ) + { + return; + } + ob_type = Py_TYPE( + pypff_name_to_id_map_entry ); + + if( pypff_name_to_id_map_entry->parent_object != NULL ) + { + Py_DecRef( + pypff_name_to_id_map_entry->parent_object ); + } + if( ( ob_type != NULL ) + && ( ob_type->tp_free != NULL ) ) + { + ob_type->tp_free( + (PyObject *) pypff_name_to_id_map_entry ); + } +} + +/* Retrieves the entry type + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_name_to_id_map_entry_get_type( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry, + PyObject *arguments PYPFF_ATTRIBUTE_UNUSED ) +{ + PyObject *integer_object = NULL; + libcerror_error_t *error = NULL; + static char *function = "pypff_name_to_id_map_entry_get_type"; + uint8_t entry_type = 0; + int result = 0; + + PYPFF_UNREFERENCED_PARAMETER( arguments ) + + if( ( pypff_name_to_id_map_entry == NULL ) + || ( pypff_name_to_id_map_entry->name_to_id_map_entry == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid name to ID map entry.", + function ); + + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_name_to_id_map_entry_get_type( + pypff_name_to_id_map_entry->name_to_id_map_entry, + &entry_type, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve entry type.", + function ); + + libcerror_error_free( + &error ); + + return( NULL ); + } +#if PY_MAJOR_VERSION >= 3 + integer_object = PyLong_FromLong( + (long) entry_type ); +#else + integer_object = PyInt_FromLong( + (long) entry_type ); +#endif + return( integer_object ); +} + +/* Retrieves the numeric name value + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_name_to_id_map_entry_get_number( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry, + PyObject *arguments PYPFF_ATTRIBUTE_UNUSED ) +{ + PyObject *integer_object = NULL; + libcerror_error_t *error = NULL; + static char *function = "pypff_name_to_id_map_entry_get_number"; + uint32_t number = 0; + uint8_t entry_type = 0; + int result = 0; + + PYPFF_UNREFERENCED_PARAMETER( arguments ) + + if( ( pypff_name_to_id_map_entry == NULL ) + || ( pypff_name_to_id_map_entry->name_to_id_map_entry == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid name to ID map entry.", + function ); + + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_name_to_id_map_entry_get_type( + pypff_name_to_id_map_entry->name_to_id_map_entry, + &entry_type, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve entry type.", + function ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + if( entry_type != LIBPFF_NAME_TO_ID_MAP_ENTRY_TYPE_NUMERIC ) + { + Py_IncRef( + Py_None ); + + return( Py_None ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_name_to_id_map_entry_get_number( + pypff_name_to_id_map_entry->name_to_id_map_entry, + &number, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve numeric name value.", + function ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + integer_object = pypff_integer_unsigned_new_from_64bit( + (uint64_t) number ); + + return( integer_object ); +} + +/* Retrieves the string name value + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_name_to_id_map_entry_get_string( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry, + PyObject *arguments PYPFF_ATTRIBUTE_UNUSED ) +{ + PyObject *string_object = NULL; + libcerror_error_t *error = NULL; + char *utf8_string = NULL; + static char *function = "pypff_name_to_id_map_entry_get_string"; + size_t utf8_string_size = 0; + uint8_t entry_type = 0; + int result = 0; + + PYPFF_UNREFERENCED_PARAMETER( arguments ) + + if( ( pypff_name_to_id_map_entry == NULL ) + || ( pypff_name_to_id_map_entry->name_to_id_map_entry == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid name to ID map entry.", + function ); + + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_name_to_id_map_entry_get_type( + pypff_name_to_id_map_entry->name_to_id_map_entry, + &entry_type, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve entry type.", + function ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + if( entry_type != LIBPFF_NAME_TO_ID_MAP_ENTRY_TYPE_STRING ) + { + Py_IncRef( + Py_None ); + + return( Py_None ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_name_to_id_map_entry_get_utf8_string_size( + pypff_name_to_id_map_entry->name_to_id_map_entry, + &utf8_string_size, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to determine size of string name value.", + function ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + if( utf8_string_size == 0 ) + { + Py_IncRef( + Py_None ); + + return( Py_None ); + } + utf8_string = (char *) PyMem_Malloc( + sizeof( char ) * utf8_string_size ); + + if( utf8_string == NULL ) + { + PyErr_Format( + PyExc_MemoryError, + "%s: unable to create UTF-8 string.", + function ); + + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_name_to_id_map_entry_get_utf8_string( + pypff_name_to_id_map_entry->name_to_id_map_entry, + (uint8_t *) utf8_string, + utf8_string_size, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve string name value.", + function ); + + libcerror_error_free( + &error ); + + PyMem_Free( + utf8_string ); + + return( NULL ); + } + string_object = PyUnicode_DecodeUTF8( + utf8_string, + (Py_ssize_t) utf8_string_size - 1, + NULL ); + + PyMem_Free( + utf8_string ); + + return( string_object ); +} + +/* Retrieves the property-set GUID + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_name_to_id_map_entry_get_guid( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry, + PyObject *arguments PYPFF_ATTRIBUTE_UNUSED ) +{ + PyObject *bytes_object = NULL; + libcerror_error_t *error = NULL; + static char *function = "pypff_name_to_id_map_entry_get_guid"; + uint8_t guid[ 16 ]; + int result = 0; + + PYPFF_UNREFERENCED_PARAMETER( arguments ) + + if( ( pypff_name_to_id_map_entry == NULL ) + || ( pypff_name_to_id_map_entry->name_to_id_map_entry == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid name to ID map entry.", + function ); + + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_name_to_id_map_entry_get_guid( + pypff_name_to_id_map_entry->name_to_id_map_entry, + guid, + 16, + &error ); + + Py_END_ALLOW_THREADS + + if( result != 1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve property-set GUID.", + function ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + bytes_object = PyBytes_FromStringAndSize( + (char *) guid, + 16 ); + + return( bytes_object ); +} diff --git a/pypff/pypff_name_to_id_map_entry.h b/pypff/pypff_name_to_id_map_entry.h new file mode 100644 index 00000000..4e380de5 --- /dev/null +++ b/pypff/pypff_name_to_id_map_entry.h @@ -0,0 +1,85 @@ +/* + * Python object wrapper of libpff_name_to_id_map_entry_t + * + * Copyright (C) 2008-2026, Joachim Metz + * + * Refer to AUTHORS for acknowledgements. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#if !defined( _PYPFF_NAME_TO_ID_MAP_ENTRY_H ) +#define _PYPFF_NAME_TO_ID_MAP_ENTRY_H + +#include +#include + +#include "pypff_libpff.h" +#include "pypff_python.h" + +#if defined( __cplusplus ) +extern "C" { +#endif + +typedef struct pypff_name_to_id_map_entry pypff_name_to_id_map_entry_t; + +struct pypff_name_to_id_map_entry +{ + /* Python object initialization + */ + PyObject_HEAD + + /* The libpff name to ID map entry + */ + libpff_name_to_id_map_entry_t *name_to_id_map_entry; + + /* The parent object + */ + PyObject *parent_object; +}; + +extern PyMethodDef pypff_name_to_id_map_entry_object_methods[]; +extern PyTypeObject pypff_name_to_id_map_entry_type_object; + +PyObject *pypff_name_to_id_map_entry_new( + libpff_name_to_id_map_entry_t *name_to_id_map_entry, + PyObject *parent_object ); + +int pypff_name_to_id_map_entry_init( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry ); + +void pypff_name_to_id_map_entry_free( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry ); + +PyObject *pypff_name_to_id_map_entry_get_type( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry, + PyObject *arguments ); + +PyObject *pypff_name_to_id_map_entry_get_number( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry, + PyObject *arguments ); + +PyObject *pypff_name_to_id_map_entry_get_string( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry, + PyObject *arguments ); + +PyObject *pypff_name_to_id_map_entry_get_guid( + pypff_name_to_id_map_entry_t *pypff_name_to_id_map_entry, + PyObject *arguments ); + +#if defined( __cplusplus ) +} +#endif + +#endif /* !defined( _PYPFF_NAME_TO_ID_MAP_ENTRY_H ) */ diff --git a/pypff/pypff_recipients.c b/pypff/pypff_recipients.c index 1744594a..7759b41d 100644 --- a/pypff/pypff_recipients.c +++ b/pypff/pypff_recipients.c @@ -37,6 +37,19 @@ PyMethodDef pypff_recipients_object_methods[] = { /* Functions to access the recipients values */ + { "get_number_of_recipients", + (PyCFunction) pypff_item_get_number_of_record_sets, + METH_NOARGS, + "get_number_of_recipients() -> Integer or None\n" + "\n" + "Retrieves the number of recipients." }, + + { "get_recipient", + (PyCFunction) pypff_recipients_get_recipient, + METH_VARARGS | METH_KEYWORDS, + "get_recipient(recipient_index) -> Object or None\n" + "\n" + "Retrieves the recipient specified by the index." }, /* Sentinel */ { NULL, NULL, 0, NULL } @@ -44,6 +57,18 @@ PyMethodDef pypff_recipients_object_methods[] = { PyGetSetDef pypff_recipients_object_get_set_definitions[] = { + { "number_of_recipients", + (getter) pypff_item_get_number_of_record_sets, + (setter) 0, + "The number of recipients.", + NULL }, + + { "recipients", + (getter) pypff_item_get_record_sets, + (setter) 0, + "The recipients.", + NULL }, + /* Sentinel */ { NULL, NULL, NULL, NULL, NULL } }; @@ -143,3 +168,41 @@ PyTypeObject pypff_recipients_type_object = { 0 }; +/* Retrieves a specific recipient by index + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_recipients_get_recipient( + pypff_item_t *pypff_recipients, + PyObject *arguments, + PyObject *keywords ) +{ + PyObject *recipient_object = NULL; + static char *function = "pypff_recipients_get_recipient"; + static char *keyword_list[] = { "recipient_index", NULL }; + int recipient_index = 0; + + if( pypff_recipients == NULL ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid recipients.", + function ); + + return( NULL ); + } + if( PyArg_ParseTupleAndKeywords( + arguments, + keywords, + "i", + keyword_list, + &recipient_index ) == 0 ) + { + return( NULL ); + } + recipient_object = pypff_item_get_record_set_by_index( + (PyObject *) pypff_recipients, + recipient_index ); + + return( recipient_object ); +} + diff --git a/pypff/pypff_recipients.h b/pypff/pypff_recipients.h index ed906e04..44852a25 100644 --- a/pypff/pypff_recipients.h +++ b/pypff/pypff_recipients.h @@ -35,6 +35,11 @@ extern "C" { extern PyMethodDef pypff_recipients_object_methods[]; extern PyTypeObject pypff_recipients_type_object; +PyObject *pypff_recipients_get_recipient( + pypff_item_t *pypff_recipients, + PyObject *arguments, + PyObject *keywords ); + #if defined( __cplusplus ) } #endif diff --git a/pypff/pypff_record_entry.c b/pypff/pypff_record_entry.c index 6c2124ee..420c3dee 100644 --- a/pypff/pypff_record_entry.c +++ b/pypff/pypff_record_entry.c @@ -31,6 +31,8 @@ #include "pypff_integer.h" #include "pypff_libcerror.h" #include "pypff_libpff.h" +#include "pypff_multi_value.h" +#include "pypff_name_to_id_map_entry.h" #include "pypff_python.h" #include "pypff_record_entry.h" #include "pypff_unused.h" @@ -51,6 +53,20 @@ PyMethodDef pypff_record_entry_object_methods[] = { "\n" "Retrieves the value type." }, + { "get_name_to_id_map_entry", + (PyCFunction) pypff_record_entry_get_name_to_id_map_entry, + METH_NOARGS, + "get_name_to_id_map_entry() -> Object or None\n" + "\n" + "Retrieves the name to ID map entry." }, + + { "get_multi_value", + (PyCFunction) pypff_record_entry_get_multi_value, + METH_NOARGS, + "get_multi_value() -> Object or None\n" + "\n" + "Retrieves the multi value." }, + { "get_data", (PyCFunction) pypff_record_entry_get_data, METH_NOARGS, @@ -118,6 +134,18 @@ PyGetSetDef pypff_record_entry_object_get_set_definitions[] = { "The value type.", NULL }, + { "name_to_id_map_entry", + (getter) pypff_record_entry_get_name_to_id_map_entry, + (setter) 0, + "The name to ID map entry.", + NULL }, + + { "multi_value", + (getter) pypff_record_entry_get_multi_value, + (setter) 0, + "The multi value.", + NULL }, + { "data", (getter) pypff_record_entry_get_data, (setter) 0, @@ -538,6 +566,165 @@ PyObject *pypff_record_entry_get_value_type( return( integer_object ); } +/* Retrieves the name to ID map entry + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_record_entry_get_name_to_id_map_entry( + pypff_record_entry_t *pypff_record_entry, + PyObject *arguments PYPFF_ATTRIBUTE_UNUSED ) +{ + PyObject *name_to_id_map_entry_object = NULL; + libcerror_error_t *error = NULL; + libpff_name_to_id_map_entry_t *name_to_id_map_entry = NULL; + static char *function = "pypff_record_entry_get_name_to_id_map_entry"; + int result = 0; + + PYPFF_UNREFERENCED_PARAMETER( arguments ) + + if( ( pypff_record_entry == NULL ) + || ( pypff_record_entry->record_entry == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid record entry.", + function ); + + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_record_entry_get_name_to_id_map_entry( + pypff_record_entry->record_entry, + &name_to_id_map_entry, + &error ); + + Py_END_ALLOW_THREADS + + if( result == -1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve name to ID map entry.", + function ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + if( result == 0 ) + { + Py_IncRef( + Py_None ); + + return( Py_None ); + } + name_to_id_map_entry_object = pypff_name_to_id_map_entry_new( + name_to_id_map_entry, + (PyObject *) pypff_record_entry ); + + return( name_to_id_map_entry_object ); +} + +/* Retrieves the multi value + * Returns a Python object if successful or NULL on error + */ +PyObject *pypff_record_entry_get_multi_value( + pypff_record_entry_t *pypff_record_entry, + PyObject *arguments PYPFF_ATTRIBUTE_UNUSED ) +{ + PyObject *multi_value_object = NULL; + libcerror_error_t *error = NULL; + libpff_multi_value_t *multi_value = NULL; + static char *function = "pypff_record_entry_get_multi_value"; + uint32_t value_type = 0; + int result = 0; + + PYPFF_UNREFERENCED_PARAMETER( arguments ) + + if( ( pypff_record_entry == NULL ) + || ( pypff_record_entry->record_entry == NULL ) ) + { + PyErr_Format( + PyExc_ValueError, + "%s: invalid record entry.", + function ); + + return( NULL ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_record_entry_get_value_type( + pypff_record_entry->record_entry, + &value_type, + &error ); + + Py_END_ALLOW_THREADS + + if( result == -1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve value type.", + function ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + if( ( result == 0 ) + || ( ( value_type & LIBPFF_VALUE_TYPE_MULTI_VALUE_FLAG ) == 0 ) ) + { + Py_IncRef( + Py_None ); + + return( Py_None ); + } + Py_BEGIN_ALLOW_THREADS + + result = libpff_record_entry_get_multi_value( + pypff_record_entry->record_entry, + &multi_value, + &error ); + + Py_END_ALLOW_THREADS + + if( result == -1 ) + { + pypff_error_raise( + error, + PyExc_IOError, + "%s: unable to retrieve multi value.", + function ); + + libcerror_error_free( + &error ); + + return( NULL ); + } + if( result == 0 ) + { + Py_IncRef( + Py_None ); + + return( Py_None ); + } + multi_value_object = pypff_multi_value_new( + multi_value, + (PyObject *) pypff_record_entry ); + + if( multi_value_object == NULL ) + { + libpff_multi_value_free( + &multi_value, + NULL ); + } + return( multi_value_object ); +} + /* Retrieves the data * Returns a Python object if successful or NULL on error */ diff --git a/pypff/pypff_record_entry.h b/pypff/pypff_record_entry.h index 4983e504..440cf308 100644 --- a/pypff/pypff_record_entry.h +++ b/pypff/pypff_record_entry.h @@ -71,6 +71,14 @@ PyObject *pypff_record_entry_get_value_type( pypff_record_entry_t *pypff_record_entry, PyObject *arguments ); +PyObject *pypff_record_entry_get_name_to_id_map_entry( + pypff_record_entry_t *pypff_record_entry, + PyObject *arguments ); + +PyObject *pypff_record_entry_get_multi_value( + pypff_record_entry_t *pypff_record_entry, + PyObject *arguments ); + PyObject *pypff_record_entry_get_data( pypff_record_entry_t *pypff_record_entry, PyObject *arguments ); diff --git a/pypff/pypff_task.c b/pypff/pypff_task.c new file mode 100644 index 00000000..b3005557 --- /dev/null +++ b/pypff/pypff_task.c @@ -0,0 +1,134 @@ +/* + * Python object wrapper of libpff task items + * + * Copyright (C) 2008-2026, Joachim Metz + * + * Refer to AUTHORS for acknowledgements. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "pypff_message.h" +#include "pypff_python.h" +#include "pypff_task.h" + +PyMethodDef pypff_task_object_methods[] = { + + /* Sentinel */ + { NULL, NULL, 0, NULL } +}; + +PyGetSetDef pypff_task_object_get_set_definitions[] = { + + /* Sentinel */ + { NULL, NULL, NULL, NULL, NULL } +}; + +PyTypeObject pypff_task_type_object = { + PyVarObject_HEAD_INIT( NULL, 0 ) + + /* tp_name */ + "pypff.task", + /* tp_basicsize */ + sizeof( pypff_item_t ), + /* tp_itemsize */ + 0, + /* tp_dealloc */ + 0, + /* tp_print */ + 0, + /* tp_getattr */ + 0, + /* tp_setattr */ + 0, + /* tp_compare */ + 0, + /* tp_repr */ + 0, + /* tp_as_number */ + 0, + /* tp_as_sequence */ + 0, + /* tp_as_mapping */ + 0, + /* tp_hash */ + 0, + /* tp_call */ + 0, + /* tp_str */ + 0, + /* tp_getattro */ + 0, + /* tp_setattro */ + 0, + /* tp_as_buffer */ + 0, + /* tp_flags */ + Py_TPFLAGS_DEFAULT, + /* tp_doc */ + "pypff task object (wraps task type libpff_item_t)", + /* tp_traverse */ + 0, + /* tp_clear */ + 0, + /* tp_richcompare */ + 0, + /* tp_weaklistoffset */ + 0, + /* tp_iter */ + 0, + /* tp_iternext */ + 0, + /* tp_methods */ + pypff_task_object_methods, + /* tp_members */ + 0, + /* tp_getset */ + pypff_task_object_get_set_definitions, + /* tp_base */ + &pypff_message_type_object, + /* tp_dict */ + 0, + /* tp_descr_get */ + 0, + /* tp_descr_set */ + 0, + /* tp_dictoffset */ + 0, + /* tp_init */ + 0, + /* tp_alloc */ + 0, + /* tp_new */ + 0, + /* tp_free */ + 0, + /* tp_is_gc */ + 0, + /* tp_bases */ + NULL, + /* tp_mro */ + NULL, + /* tp_cache */ + NULL, + /* tp_subclasses */ + NULL, + /* tp_weaklist */ + NULL, + /* tp_del */ + 0 +}; diff --git a/pypff/pypff_task.h b/pypff/pypff_task.h new file mode 100644 index 00000000..87cd3fe9 --- /dev/null +++ b/pypff/pypff_task.h @@ -0,0 +1,42 @@ +/* + * Python object wrapper of libpff task items + * + * Copyright (C) 2008-2026, Joachim Metz + * + * Refer to AUTHORS for acknowledgements. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#if !defined( _PYPFF_TASK_H ) +#define _PYPFF_TASK_H + +#include +#include + +#include "pypff_message.h" +#include "pypff_python.h" + +#if defined( __cplusplus ) +extern "C" { +#endif + +extern PyMethodDef pypff_task_object_methods[]; +extern PyTypeObject pypff_task_type_object; + +#if defined( __cplusplus ) +} +#endif + +#endif /* !defined( _PYPFF_TASK_H ) */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 460483ad..633d7862 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -36,6 +36,7 @@ endif check_SCRIPTS = \ pypff_test_file.py \ + pypff_test_module.py \ pypff_test_support.py check_PROGRAMS = \ diff --git a/tests/pypff_test_file.py b/tests/pypff_test_file.py index bd5e781c..e8684723 100644 --- a/tests/pypff_test_file.py +++ b/tests/pypff_test_file.py @@ -267,9 +267,15 @@ def test_get_message_store(self): pff_file.open(test_source) - _ = pff_file.get_message_store() + message_store = pff_file.get_message_store() - _ = pff_file.message_store + if message_store: + self.assertIsInstance(message_store, pypff.message_store) + + message_store = pff_file.message_store + + if message_store: + self.assertIsInstance(message_store, pypff.message_store) pff_file.close() diff --git a/tests/pypff_test_message.py b/tests/pypff_test_message.py index cfeccd02..68afbe4f 100644 --- a/tests/pypff_test_message.py +++ b/tests/pypff_test_message.py @@ -400,7 +400,16 @@ def test_get_recipients(self): if not message_item: raise unittest.SkipTest("missing message item") - _ = message_item.recipients + recipients = message_item.recipients + + if recipients: + self.assertIsInstance(recipients, pypff.recipients) + number_of_recipients = recipients.get_number_of_recipients() + self.assertEqual(number_of_recipients, recipients.number_of_recipients) + + if number_of_recipients: + self.assertIsNotNone(recipients.get_recipient(0)) + self.assertEqual(len(recipients.recipients), number_of_recipients) finally: pff_file.close() diff --git a/tests/pypff_test_module.py b/tests/pypff_test_module.py new file mode 100644 index 00000000..5b9a12dc --- /dev/null +++ b/tests/pypff_test_module.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# +# Python-bindings module type test script +# +# Copyright (C) 2008-2026, Joachim Metz +# +# Refer to AUTHORS for acknowledgements. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +import unittest + +import pypff + + +class ModuleTypeTests(unittest.TestCase): + """Tests the Python types exposed by the module.""" + + def test_specialized_types(self): + """Tests the specialized item and record-entry value types.""" + self.assertTrue(issubclass(pypff.message_store, pypff.item)) + self.assertTrue(issubclass(pypff.task, pypff.message)) + self.assertTrue(issubclass(pypff.recipients, pypff.item)) + self.assertIsInstance(pypff.multi_value, type) + self.assertIsInstance(pypff.name_to_id_map_entry, type) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/tests/pypff_test_record_entry.py b/tests/pypff_test_record_entry.py index b835141e..607a99c6 100644 --- a/tests/pypff_test_record_entry.py +++ b/tests/pypff_test_record_entry.py @@ -30,6 +30,17 @@ class RecordEntryTypeTests(unittest.TestCase): """Tests the record_entry type.""" + def _get_record_entries(self, item): + """Yields record entries from an item tree.""" + if not item: + return + + for record_set in item.record_sets: + yield from record_set.entries + + for sub_item in item.sub_items: + yield from self._get_record_entries(sub_item) + def test_get_entry_type(self): """Tests the get_entry_type function and entry_type property.""" test_source = getattr(unittest, "source", None) @@ -140,6 +151,75 @@ def test_get_data(self): finally: pff_file.close() + def test_get_name_to_id_map_entry(self): + """Tests the name_to_id_map_entry property.""" + test_source = getattr(unittest, "source", None) + if not test_source: + raise unittest.SkipTest("missing source") + + pff_file = pypff.file() + + pff_file.open(test_source) + + try: + map_entry = None + + for record_entry in self._get_record_entries(pff_file.root_folder): + map_entry = record_entry.get_name_to_id_map_entry() + if map_entry: + break + + if not map_entry: + raise unittest.SkipTest("missing name to ID map entry") + + self.assertIsInstance(map_entry, pypff.name_to_id_map_entry) + self.assertEqual(map_entry.get_type(), map_entry.type) + self.assertEqual(map_entry.get_guid(), map_entry.guid) + self.assertEqual(len(map_entry.guid), 16) + + number = map_entry.get_number() + string = map_entry.get_string() + self.assertTrue(number is not None or string is not None) + self.assertEqual(number, map_entry.number) + self.assertEqual(string, map_entry.string) + + finally: + pff_file.close() + + def test_get_multi_value(self): + """Tests the multi_value property.""" + test_source = getattr(unittest, "source", None) + if not test_source: + raise unittest.SkipTest("missing source") + + pff_file = pypff.file() + + pff_file.open(test_source) + + try: + multi_value = None + + for record_entry in self._get_record_entries(pff_file.root_folder): + multi_value = record_entry.get_multi_value() + if multi_value: + break + + if not multi_value: + raise unittest.SkipTest("missing multi value") + + self.assertIsInstance(multi_value, pypff.multi_value) + self.assertEqual( + multi_value.get_number_of_values(), multi_value.number_of_values + ) + + if multi_value.number_of_values: + self.assertIsInstance(multi_value.get_value_type(0), int) + value = multi_value.get_value(0) + self.assertTrue(value is None or isinstance(value, bytes)) + + finally: + pff_file.close() + # TODO: add tests for data_as_boolean # TODO: add tests for data_as_integer # TODO: add tests for data_as_datetime