1 /*
2  * Python object wrapper of libfwsi_item_list_t
3  *
4  * Copyright (C) 2010-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 <memory.h>
24 #include <narrow_string.h>
25 #include <types.h>
26 
27 #if defined( HAVE_STDLIB_H )
28 #include <stdlib.h>
29 #endif
30 
31 #include "pyfwsi_error.h"
32 #include "pyfwsi_file_entry.h"
33 #include "pyfwsi_item.h"
34 #include "pyfwsi_item_list.h"
35 #include "pyfwsi_items.h"
36 #include "pyfwsi_libcerror.h"
37 #include "pyfwsi_libclocale.h"
38 #include "pyfwsi_libfwsi.h"
39 #include "pyfwsi_network_location.h"
40 #include "pyfwsi_python.h"
41 #include "pyfwsi_root_folder.h"
42 #include "pyfwsi_unused.h"
43 #include "pyfwsi_volume.h"
44 
45 PyMethodDef pyfwsi_item_list_object_methods[] = {
46 
47 	{ "copy_from_byte_stream",
48 	  (PyCFunction) pyfwsi_item_list_copy_from_byte_stream,
49 	  METH_VARARGS | METH_KEYWORDS,
50 	  "copy_from_byte_stream(byte_stream, ascii_codepage=cp1252)\n"
51 	  "\n"
52 	  "Copies the the item list from the byte stream." },
53 
54 	/* Functions to access the items */
55 
56 	{ "get_number_of_items",
57 	  (PyCFunction) pyfwsi_item_list_get_number_of_items,
58 	  METH_NOARGS,
59 	  "get_number_of_items() -> Integer\n"
60 	  "\n"
61 	  "Retrieves the number of items." },
62 
63 	{ "get_item",
64 	  (PyCFunction) pyfwsi_item_list_get_item,
65 	  METH_VARARGS | METH_KEYWORDS,
66 	  "get_item(item_index) -> Object\n"
67 	  "\n"
68 	  "Retrieves a specific item." },
69 
70 	/* Sentinel */
71 	{ NULL, NULL, 0, NULL }
72 };
73 
74 PyGetSetDef pyfwsi_item_list_object_get_set_definitions[] = {
75 
76 	{ "number_of_items",
77 	  (getter) pyfwsi_item_list_get_number_of_items,
78 	  (setter) 0,
79 	  "The number of items.",
80 	  NULL },
81 
82 	{ "items",
83 	  (getter) pyfwsi_item_list_get_items,
84 	  (setter) 0,
85 	  "The items",
86 	  NULL },
87 
88 	/* Sentinel */
89 	{ NULL, NULL, NULL, NULL, NULL }
90 };
91 
92 PyTypeObject pyfwsi_item_list_type_object = {
93 	PyVarObject_HEAD_INIT( NULL, 0 )
94 
95 	/* tp_name */
96 	"pyfwsi.item_list",
97 	/* tp_basicsize */
98 	sizeof( pyfwsi_item_list_t ),
99 	/* tp_itemsize */
100 	0,
101 	/* tp_dealloc */
102 	(destructor) pyfwsi_item_list_free,
103 	/* tp_print */
104 	0,
105 	/* tp_getattr */
106 	0,
107 	/* tp_setattr */
108 	0,
109 	/* tp_compare */
110 	0,
111 	/* tp_repr */
112 	0,
113 	/* tp_as_number */
114 	0,
115 	/* tp_as_sequence */
116 	0,
117 	/* tp_as_mapping */
118 	0,
119 	/* tp_hash */
120 	0,
121 	/* tp_call */
122 	0,
123 	/* tp_str */
124 	0,
125 	/* tp_getattro */
126 	0,
127 	/* tp_setattro */
128 	0,
129 	/* tp_as_buffer */
130 	0,
131 	/* tp_flags */
132 	Py_TPFLAGS_DEFAULT,
133 	/* tp_doc */
134 	"pyfwsi item_list object (wraps libfwsi_item_list_t)",
135 	/* tp_traverse */
136 	0,
137 	/* tp_clear */
138 	0,
139 	/* tp_richcompare */
140 	0,
141 	/* tp_weaklistoffset */
142 	0,
143 	/* tp_iter */
144 	0,
145 	/* tp_iternext */
146 	0,
147 	/* tp_methods */
148 	pyfwsi_item_list_object_methods,
149 	/* tp_members */
150 	0,
151 	/* tp_getset */
152 	pyfwsi_item_list_object_get_set_definitions,
153 	/* tp_base */
154 	0,
155 	/* tp_dict */
156 	0,
157 	/* tp_descr_get */
158 	0,
159 	/* tp_descr_set */
160 	0,
161 	/* tp_dictoffset */
162 	0,
163 	/* tp_init */
164 	(initproc) pyfwsi_item_list_init,
165 	/* tp_alloc */
166 	0,
167 	/* tp_new */
168 	0,
169 	/* tp_free */
170 	0,
171 	/* tp_is_gc */
172 	0,
173 	/* tp_bases */
174 	NULL,
175 	/* tp_mro */
176 	NULL,
177 	/* tp_cache */
178 	NULL,
179 	/* tp_subclasses */
180 	NULL,
181 	/* tp_weaklist */
182 	NULL,
183 	/* tp_del */
184 	0
185 };
186 
187 /* Creates a new pyfwsi item list object
188  * Returns a Python object if successful or NULL on error
189  */
pyfwsi_item_list_new(void)190 PyObject *pyfwsi_item_list_new(
191            void )
192 {
193 	static char *function                = "pyfwsi_item_list_new";
194 	pyfwsi_item_list_t *pyfwsi_item_list = NULL;
195 
196 	pyfwsi_item_list = PyObject_New(
197 	                    struct pyfwsi_item_list,
198 	                    &pyfwsi_item_list_type_object );
199 
200 	if( pyfwsi_item_list == NULL )
201 	{
202 		PyErr_Format(
203 		 PyExc_MemoryError,
204 		 "%s: unable to initialize item list.",
205 		 function );
206 
207 		goto on_error;
208 	}
209 	if( pyfwsi_item_list_init(
210 	     pyfwsi_item_list ) != 0 )
211 	{
212 		PyErr_Format(
213 		 PyExc_MemoryError,
214 		 "%s: unable to initialize item list.",
215 		 function );
216 
217 		goto on_error;
218 	}
219 	return( (PyObject *) pyfwsi_item_list );
220 
221 on_error:
222 	if( pyfwsi_item_list != NULL )
223 	{
224 		Py_DecRef(
225 		 (PyObject *) pyfwsi_item_list );
226 	}
227 	return( NULL );
228 }
229 
230 /* Initializes an item list object
231  * Returns 0 if successful or -1 on error
232  */
pyfwsi_item_list_init(pyfwsi_item_list_t * pyfwsi_item_list)233 int pyfwsi_item_list_init(
234      pyfwsi_item_list_t *pyfwsi_item_list )
235 {
236 	static char *function    = "pyfwsi_item_list_init";
237 	libcerror_error_t *error = NULL;
238 
239 	if( pyfwsi_item_list == NULL )
240 	{
241 		PyErr_Format(
242 		 PyExc_TypeError,
243 		 "%s: invalid item list.",
244 		 function );
245 
246 		return( -1 );
247 	}
248 	pyfwsi_item_list->item_list = NULL;
249 
250 	if( libfwsi_item_list_initialize(
251 	     &( pyfwsi_item_list->item_list ),
252 	     &error ) != 1 )
253 	{
254 		pyfwsi_error_raise(
255 		 error,
256 		 PyExc_MemoryError,
257 		 "%s: unable to initialize item list.",
258 		 function );
259 
260 		libcerror_error_free(
261 		 &error );
262 
263 		return( -1 );
264 	}
265 	return( 0 );
266 }
267 
268 /* Frees an item list object
269  */
pyfwsi_item_list_free(pyfwsi_item_list_t * pyfwsi_item_list)270 void pyfwsi_item_list_free(
271       pyfwsi_item_list_t *pyfwsi_item_list )
272 {
273 	libcerror_error_t *error    = NULL;
274 	struct _typeobject *ob_type = NULL;
275 	static char *function       = "pyfwsi_item_list_free";
276 	int result                  = 0;
277 
278 	if( pyfwsi_item_list == NULL )
279 	{
280 		PyErr_Format(
281 		 PyExc_TypeError,
282 		 "%s: invalid item list.",
283 		 function );
284 
285 		return;
286 	}
287 	if( pyfwsi_item_list->item_list == NULL )
288 	{
289 		PyErr_Format(
290 		 PyExc_TypeError,
291 		 "%s: invalid item list - missing libfwsi item list.",
292 		 function );
293 
294 		return;
295 	}
296 	ob_type = Py_TYPE(
297 	           pyfwsi_item_list );
298 
299 	if( ob_type == NULL )
300 	{
301 		PyErr_Format(
302 		 PyExc_ValueError,
303 		 "%s: missing ob_type.",
304 		 function );
305 
306 		return;
307 	}
308 	if( ob_type->tp_free == NULL )
309 	{
310 		PyErr_Format(
311 		 PyExc_ValueError,
312 		 "%s: invalid ob_type - missing tp_free.",
313 		 function );
314 
315 		return;
316 	}
317 	Py_BEGIN_ALLOW_THREADS
318 
319 	result = libfwsi_item_list_free(
320 	          &( pyfwsi_item_list->item_list ),
321 	          &error );
322 
323 	Py_END_ALLOW_THREADS
324 
325 	if( result != 1 )
326 	{
327 		pyfwsi_error_raise(
328 		 error,
329 		 PyExc_MemoryError,
330 		 "%s: unable to free item list.",
331 		 function );
332 
333 		libcerror_error_free(
334 		 &error );
335 	}
336 	ob_type->tp_free(
337 	 (PyObject*) pyfwsi_item_list );
338 }
339 
340 /* Copies the item list from a byte stream
341  * Returns a Python object if successful or NULL on error
342  */
pyfwsi_item_list_copy_from_byte_stream(pyfwsi_item_list_t * pyfwsi_item_list,PyObject * arguments,PyObject * keywords)343 PyObject *pyfwsi_item_list_copy_from_byte_stream(
344            pyfwsi_item_list_t *pyfwsi_item_list,
345            PyObject *arguments,
346            PyObject *keywords )
347 {
348 	PyObject *string_object       = NULL;
349 	libcerror_error_t *error      = NULL;
350 	static char *function         = "pyfwsi_item_list_copy_from_byte_stream";
351 	static char *keyword_list[]   = { "byte_stream", "ascii_codepage", NULL };
352 	const char *byte_stream       = NULL;
353 	char *codepage_string         = NULL;
354 	Py_ssize_t byte_stream_size   = 0;
355 	size_t codepage_string_length = 0;
356 	uint32_t feature_flags        = 0;
357 	int ascii_codepage            = LIBFWSI_CODEPAGE_WINDOWS_1252;
358 	int result                    = 0;
359 
360 	if( pyfwsi_item_list == NULL )
361 	{
362 		PyErr_Format(
363 		 PyExc_TypeError,
364 		 "%s: invalid item list.",
365 		 function );
366 
367 		return( NULL );
368 	}
369 	if( PyArg_ParseTupleAndKeywords(
370 	     arguments,
371 	     keywords,
372 	     "O|s",
373 	     keyword_list,
374 	     &string_object,
375 	     &codepage_string ) == 0 )
376 	{
377 		return( NULL );
378 	}
379 	PyErr_Clear();
380 
381 #if PY_MAJOR_VERSION >= 3
382 	result = PyObject_IsInstance(
383 		  string_object,
384 		  (PyObject *) &PyBytes_Type );
385 #else
386 	result = PyObject_IsInstance(
387 		  string_object,
388 		  (PyObject *) &PyString_Type );
389 #endif
390 	if( result == -1 )
391 	{
392 		pyfwsi_error_fetch_and_raise(
393 	         PyExc_RuntimeError,
394 		 "%s: unable to determine if string object is of type string.",
395 		 function );
396 
397 		return( NULL );
398 	}
399 	else if( result == 0 )
400 	{
401 		PyErr_Format(
402 		 PyExc_TypeError,
403 		 "%s: unsupported string object type",
404 		 function );
405 
406 		return( NULL );
407 	}
408 	if( codepage_string != NULL )
409 	{
410 		codepage_string_length = narrow_string_length(
411 		                          codepage_string );
412 
413 		feature_flags = LIBCLOCALE_CODEPAGE_FEATURE_FLAG_HAVE_WINDOWS;
414 
415 		if( libclocale_codepage_copy_from_string(
416 		     &ascii_codepage,
417 		     codepage_string,
418 		     codepage_string_length,
419 		     feature_flags,
420 		     &error ) != 1 )
421 		{
422 			pyfwsi_error_raise(
423 			 error,
424 			 PyExc_RuntimeError,
425 			 "%s: unable to determine ASCII codepage.",
426 			 function );
427 
428 			libcerror_error_free(
429 			 &error );
430 
431 			return( NULL );
432 		}
433 	}
434 	PyErr_Clear();
435 
436 #if PY_MAJOR_VERSION >= 3
437 	byte_stream = PyBytes_AsString(
438 	               string_object );
439 
440 	byte_stream_size = PyBytes_Size(
441 	                    string_object );
442 #else
443 	byte_stream = PyString_AsString(
444 	               string_object );
445 
446 	byte_stream_size = PyString_Size(
447 	                    string_object );
448 #endif
449 /* TODO size bounds check */
450 
451 	Py_BEGIN_ALLOW_THREADS
452 
453 	result = libfwsi_item_list_copy_from_byte_stream(
454 	          pyfwsi_item_list->item_list,
455 	          (uint8_t *) byte_stream,
456 	          (size_t) byte_stream_size,
457 	          ascii_codepage,
458 	          &error );
459 
460 	Py_END_ALLOW_THREADS
461 
462 	if( result != 1 )
463 	{
464 		pyfwsi_error_raise(
465 		 error,
466 		 PyExc_IOError,
467 		 "%s: unable to copy item list from byte stream.",
468 		 function );
469 
470 		libcerror_error_free(
471 		 &error );
472 
473 		return( NULL );
474 	}
475 	Py_IncRef(
476 	 Py_None );
477 
478 	return( Py_None );
479 }
480 
481 /* Retrieves the number of items
482  * Returns a Python object if successful or NULL on error
483  */
pyfwsi_item_list_get_number_of_items(pyfwsi_item_list_t * pyfwsi_item_list,PyObject * arguments PYFWSI_ATTRIBUTE_UNUSED)484 PyObject *pyfwsi_item_list_get_number_of_items(
485            pyfwsi_item_list_t *pyfwsi_item_list,
486            PyObject *arguments PYFWSI_ATTRIBUTE_UNUSED )
487 {
488 	libcerror_error_t *error = NULL;
489 	PyObject *integer_object = NULL;
490 	static char *function    = "pyfwsi_item_list_get_number_of_items";
491 	int number_of_items      = 0;
492 	int result               = 0;
493 
494 	PYFWSI_UNREFERENCED_PARAMETER( arguments )
495 
496 	if( pyfwsi_item_list == NULL )
497 	{
498 		PyErr_Format(
499 		 PyExc_TypeError,
500 		 "%s: invalid item list.",
501 		 function );
502 
503 		return( NULL );
504 	}
505 	Py_BEGIN_ALLOW_THREADS
506 
507 	result = libfwsi_item_list_get_number_of_items(
508 	          pyfwsi_item_list->item_list,
509 	          &number_of_items,
510 	          &error );
511 
512 	Py_END_ALLOW_THREADS
513 
514 	if( result != 1 )
515 	{
516 		pyfwsi_error_raise(
517 		 error,
518 		 PyExc_IOError,
519 		 "%s: unable to retrieve number of items.",
520 		 function );
521 
522 		libcerror_error_free(
523 		 &error );
524 
525 		return( NULL );
526 	}
527 #if PY_MAJOR_VERSION >= 3
528 	integer_object = PyLong_FromLong(
529 	                  (long) number_of_items );
530 #else
531 	integer_object = PyInt_FromLong(
532 	                  (long) number_of_items );
533 #endif
534 	return( integer_object );
535 }
536 
537 /* Retrieves a specific item by index
538  * Returns a Python object if successful or NULL on error
539  */
pyfwsi_item_list_get_item_by_index(PyObject * pyfwsi_item_list,int item_index)540 PyObject *pyfwsi_item_list_get_item_by_index(
541            PyObject *pyfwsi_item_list,
542            int item_index )
543 {
544 	libcerror_error_t *error  = NULL;
545 	libfwsi_item_t *item      = NULL;
546 	PyObject *item_object     = NULL;
547 	PyTypeObject *type_object = NULL;
548 	static char *function     = "pyfwsi_item_list_get_item_by_index";
549 	int item_type             = 0;
550 	int result                = 0;
551 
552 	if( pyfwsi_item_list == NULL )
553 	{
554 		PyErr_Format(
555 		 PyExc_TypeError,
556 		 "%s: invalid item list.",
557 		 function );
558 
559 		return( NULL );
560 	}
561 	Py_BEGIN_ALLOW_THREADS
562 
563 	result = libfwsi_item_list_get_item(
564 	          ( (pyfwsi_item_list_t *) pyfwsi_item_list )->item_list,
565 	          item_index,
566 	          &item,
567 	          &error );
568 
569 	Py_END_ALLOW_THREADS
570 
571 	if( result != 1 )
572 	{
573 		pyfwsi_error_raise(
574 		 error,
575 		 PyExc_IOError,
576 		 "%s: unable to retrieve item: %d.",
577 		 function,
578 		 item_index );
579 
580 		libcerror_error_free(
581 		 &error );
582 
583 		goto on_error;
584 	}
585 	Py_BEGIN_ALLOW_THREADS
586 
587 	result = libfwsi_item_get_type(
588 	          item,
589 	          &item_type,
590 	          &error );
591 
592 	Py_END_ALLOW_THREADS
593 
594 	if( result != 1 )
595 	{
596 		pyfwsi_error_raise(
597 		 error,
598 		 PyExc_IOError,
599 		 "%s: unable to retrieve item: %d class type.",
600 		 function,
601 		 item_index );
602 
603 		libcerror_error_free(
604 		 &error );
605 
606 		goto on_error;
607 	}
608 	switch( item_type )
609 	{
610 		case LIBFWSI_ITEM_TYPE_FILE_ENTRY:
611 			type_object = &pyfwsi_file_entry_type_object;
612 			break;
613 
614 		case LIBFWSI_ITEM_TYPE_NETWORK_LOCATION:
615 			type_object = &pyfwsi_network_location_type_object;
616 			break;
617 
618 		case LIBFWSI_ITEM_TYPE_ROOT_FOLDER:
619 			type_object = &pyfwsi_root_folder_type_object;
620 			break;
621 
622 		case LIBFWSI_ITEM_TYPE_VOLUME:
623 			type_object = &pyfwsi_volume_type_object;
624 			break;
625 
626 		case LIBFWSI_ITEM_TYPE_UNKNOWN:
627 		case LIBFWSI_ITEM_TYPE_CDBURN:
628 		case LIBFWSI_ITEM_TYPE_CONTROL_PANEL:
629 		case LIBFWSI_ITEM_TYPE_DELEGATE:
630 		case LIBFWSI_ITEM_TYPE_GAME_FOLDER:
631 		case LIBFWSI_ITEM_TYPE_URI:
632 		case LIBFWSI_ITEM_TYPE_USERS_PROPERTY_VIEW:
633 		default:
634 			type_object = &pyfwsi_item_type_object;
635 			break;
636 	}
637 	item_object = pyfwsi_item_new(
638 	               type_object,
639 	               item,
640 	               pyfwsi_item_list );
641 
642 	if( item_object == NULL )
643 	{
644 		PyErr_Format(
645 		 PyExc_MemoryError,
646 		 "%s: unable to create item object.",
647 		 function );
648 
649 		goto on_error;
650 	}
651 	return( item_object );
652 
653 on_error:
654 	if( item != NULL )
655 	{
656 		libfwsi_item_free(
657 		 &item,
658 		 NULL );
659 	}
660 	return( NULL );
661 }
662 
663 /* Retrieves a specific item
664  * Returns a Python object if successful or NULL on error
665  */
pyfwsi_item_list_get_item(pyfwsi_item_list_t * pyfwsi_item_list,PyObject * arguments,PyObject * keywords)666 PyObject *pyfwsi_item_list_get_item(
667            pyfwsi_item_list_t *pyfwsi_item_list,
668            PyObject *arguments,
669            PyObject *keywords )
670 {
671 	PyObject *item_object       = NULL;
672 	static char *keyword_list[] = { "item_index", NULL };
673 	int item_index              = 0;
674 
675 	if( PyArg_ParseTupleAndKeywords(
676 	     arguments,
677 	     keywords,
678 	     "i",
679 	     keyword_list,
680 	     &item_index ) == 0 )
681 	{
682 		return( NULL );
683 	}
684 	item_object = pyfwsi_item_list_get_item_by_index(
685 	               (PyObject *) pyfwsi_item_list,
686 	               item_index );
687 
688 	return( item_object );
689 }
690 
691 /* Retrieves an items sequence and iterator object for the items
692  * Returns a Python object if successful or NULL on error
693  */
pyfwsi_item_list_get_items(pyfwsi_item_list_t * pyfwsi_item_list,PyObject * arguments PYFWSI_ATTRIBUTE_UNUSED)694 PyObject *pyfwsi_item_list_get_items(
695            pyfwsi_item_list_t *pyfwsi_item_list,
696            PyObject *arguments PYFWSI_ATTRIBUTE_UNUSED )
697 {
698 	libcerror_error_t *error = NULL;
699 	PyObject *items_object   = NULL;
700 	static char *function    = "pyfwsi_item_list_get_items";
701 	int number_of_items      = 0;
702 	int result               = 0;
703 
704 	PYFWSI_UNREFERENCED_PARAMETER( arguments )
705 
706 	if( pyfwsi_item_list == NULL )
707 	{
708 		PyErr_Format(
709 		 PyExc_TypeError,
710 		 "%s: invalid item list.",
711 		 function );
712 
713 		return( NULL );
714 	}
715 	Py_BEGIN_ALLOW_THREADS
716 
717 	result = libfwsi_item_list_get_number_of_items(
718 	          pyfwsi_item_list->item_list,
719 	          &number_of_items,
720 	          &error );
721 
722 	Py_END_ALLOW_THREADS
723 
724 	if( result != 1 )
725 	{
726 		pyfwsi_error_raise(
727 		 error,
728 		 PyExc_IOError,
729 		 "%s: unable to retrieve number of items.",
730 		 function );
731 
732 		libcerror_error_free(
733 		 &error );
734 
735 		return( NULL );
736 	}
737 	items_object = pyfwsi_items_new(
738 	                (PyObject *) pyfwsi_item_list,
739 	                &pyfwsi_item_list_get_item_by_index,
740 	                number_of_items );
741 
742 	if( items_object == NULL )
743 	{
744 		PyErr_Format(
745 		 PyExc_MemoryError,
746 		 "%s: unable to create items object.",
747 		 function );
748 
749 		return( NULL );
750 	}
751 	return( items_object );
752 }
753 
754