Give better error messages for TypeError, which will arise if e.g. you
[samba.git] / source / python / py_tdbpack.c
1 /* -*- c-file-style: "python"; indent-tabs-mode: nil; -*-
2          
3    Python wrapper for Samba tdb pack/unpack functions
4    Copyright (C) Martin Pool 2002
5
6
7    NOTE PYTHON STYLE GUIDE
8    http://www.python.org/peps/pep-0007.html
9    
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26
27
28 #include "Python.h"
29
30 static int pytdbpack_calc_reqd_len(char *format_str,
31                                    PyObject *val_seq);
32
33 static PyObject *pytdbpack_unpack_item(char,
34                                       char **pbuf,
35                                       int *plen);
36 static int
37 pytdbpack_calc_item_len(char format_ch,
38                         PyObject *val_obj);
39
40 static PyObject *pytdbpack_pack_data(const char *format_str,
41                                      PyObject *val_seq,
42                                      unsigned char *buf);
43
44
45         
46 static const char * pytdbpack_docstring =
47 "Convert between Python values and Samba binary encodings.
48
49 This module is conceptually similar to the standard 'struct' module, but it
50 uses both a different binary format and a different description string.
51
52 Samba's encoding is based on that used inside DCE-RPC and SMB: a
53 little-endian, unpadded, non-self-describing binary format.  It is intended
54 that these functions be as similar as possible to the routines in Samba's
55 tdb/tdbutil module, with appropriate adjustments for Python datatypes.
56
57 Python strings are used to specify the format of data to be packed or
58 unpacked.
59
60 Strings in TDBs are typically stored in DOS codepages.  The caller of this
61 module must make appropriate translations if necessary, typically to and from
62 Unicode objects.
63
64 tdbpack format strings:
65
66     'f':  NULL-terminated string in DOS codepage
67
68     'P':  same as 'f'
69
70     'd':  4 byte little-endian number
71
72     'w':  2 byte little-endian number
73
74     'P': \"Pointer\" value -- in the subset of DCERPC used by Samba, this is
75           really just an \"exists\" or \"does not exist\" flag.  The boolean
76           value of the Python object is used.
77     
78     'B': 4-byte LE length, followed by that many bytes of binary data.
79          Corresponds to a Python byte string of the appropriate length.
80
81     '$': Special flag indicating that the preceding format code should be
82          repeated while data remains.  This is only supported for unpacking.
83
84     Every code corresponds to a single Python object, except 'B' which
85     corresponds to two values (length and contents), and '$', which produces
86     however many make sense.
87 ";
88
89
90 static char const pytdbpack_pack_doc[] = 
91 "pack(format, values) -> buffer
92 Pack Python objects into Samba binary format according to format string.
93
94 arguments:
95     format -- string of tdbpack format characters
96     values -- sequence of value objects corresponding 1:1 to format characters
97
98 returns:
99     buffer -- string containing packed data
100
101 raises:
102     IndexError -- if there are not the same number of format codes as of
103         values
104     ValueError -- if any of the format characters is illegal
105     TypeError  -- if the format is not a string, or values is not a sequence,
106         or any of the values is of the wrong type for the corresponding
107         format character
108 ";
109
110
111 static char const pytdbpack_unpack_doc[] =
112 "unpack(format, buffer) -> (values, rest)
113 Unpack Samba binary data according to format string.
114
115 arguments:
116     format -- string of tdbpack characters
117     buffer -- string of packed binary data
118
119 returns:
120     2-tuple of:
121         values -- sequence of values corresponding 1:1 to format characters
122         rest -- string containing data that was not decoded, or '' if the
123             whole string was consumed
124
125 raises:
126     IndexError -- if there is insufficient data in the buffer for the
127         format (or if the data is corrupt and contains a variable-length
128         field extending past the end)
129     ValueError -- if any of the format characters is illegal
130
131 notes:
132     Because unconsumed data is returned, you can feed it back in to the
133     unpacker to extract further fields.  Alternatively, if you wish to modify
134     some fields near the start of the data, you may be able to save time by
135     only unpacking and repacking the necessary part.
136 ";
137
138
139
140 /*
141   Game plan is to first of all walk through the arguments and calculate the
142   total length that will be required.  We allocate a Python string of that
143   size, then walk through again and fill it in.
144
145   We just borrow references to all the passed arguments, since none of them
146   need to be permanently stored.  We transfer ownership to the returned
147   object.
148  */     
149 static PyObject *
150 pytdbpack_pack(PyObject *self,
151                PyObject *args)
152 {
153         char *format_str;
154         PyObject *val_seq, *fast_seq, *buf_str;
155         int reqd_len;
156         char *packed_buf;
157
158         /* TODO: Test passing wrong types or too many arguments */
159         if (!PyArg_ParseTuple(args, "sO", &format_str, &val_seq))
160                 return NULL;
161
162         /* Convert into a list or tuple (if not already one), so that we can
163          * index more easily. */
164         fast_seq = PySequence_Fast(val_seq,
165                                    __FUNCTION__ ": argument 2 must be sequence");
166         if (!fast_seq)
167                 return NULL;
168                         
169         reqd_len = pytdbpack_calc_reqd_len(format_str, fast_seq);
170         if (reqd_len == -1)     /* exception was thrown */
171                 return NULL;
172
173         /* Allocate space.
174          
175            This design causes an unnecessary copying of the data when Python
176            constructs an object, and that might possibly be avoided by using a
177            Buffer object of some kind instead.  I'm not doing that for now
178            though.  */
179         packed_buf = malloc(reqd_len);
180         if (!packed_buf) {
181                 PyErr_Format(PyExc_MemoryError,
182                              "%s: couldn't allocate %d bytes for packed buffer",
183                              __FUNCTION__, reqd_len);
184                 return NULL;
185         }       
186         
187         if (!pytdbpack_pack_data(format_str, fast_seq, packed_buf)) {
188                 free(packed_buf);
189                 return NULL;
190         }
191
192         buf_str = PyString_FromStringAndSize(packed_buf, reqd_len);
193         free(packed_buf);       /* get rid of tmp buf */
194         
195         return buf_str;
196 }
197
198
199
200 static PyObject *
201 pytdbpack_unpack(PyObject *self,
202                  PyObject *args)
203 {
204         char *format_str, *packed_str, *ppacked;
205         PyObject *val_list = NULL, *ret_tuple = NULL;
206         PyObject *rest_string = NULL;
207         int format_len, packed_len;
208         int i;
209         char last_format = '#';
210         
211         /* get arguments */
212         if (!PyArg_ParseTuple(args, "ss#", &format_str, &packed_str, &packed_len))
213                 return NULL;
214
215         format_len = strlen(format_str);
216         
217         /* allocate list to hold results */
218         val_list = PyList_New(format_len);
219         if (!val_list)
220                 goto failed;
221         ret_tuple = PyTuple_New(2);
222         if (!ret_tuple)
223                 goto failed;
224         
225         /* For every object, unpack.  */
226         for (ppacked = packed_str, i = 0; i < format_len; i++) {
227                 PyObject *val_obj;
228                 char format;
229
230                 format = format_str[i];
231                 if (format == '$') {
232                         if (i == 0) {
233                                 PyErr_Format(PyExc_ValueError,
234                                              "%s: '$' may not be first character in format",
235                                              __FUNCTION__);
236                                 goto failed;
237                         }
238                         else {
239                                 format = last_format; /* repeat */
240                         }
241                 }
242
243                 val_obj = pytdbpack_unpack_item(format,
244                                                 &ppacked,
245                                                 &packed_len);
246                 if (!val_obj)
247                         goto failed;
248
249                 PyList_SET_ITEM(val_list, i, val_obj);
250                 last_format = format;
251         }
252
253         /* put leftovers in box for lunch tomorrow */
254         rest_string = PyString_FromStringAndSize(ppacked, packed_len);
255         if (!rest_string)
256                 goto failed;
257
258         /* return (values, rest) tuple; give up references to them */
259         PyTuple_SET_ITEM(ret_tuple, 0, val_list);
260         val_list = NULL;
261         PyTuple_SET_ITEM(ret_tuple, 1, rest_string);
262         val_list = NULL;
263         return ret_tuple;
264
265   failed:
266         /* handle failure: deallocate anything */
267         Py_XDECREF(val_list);
268         Py_XDECREF(ret_tuple);
269         Py_XDECREF(rest_string);
270         return NULL;
271 }
272
273
274 /*
275   Internal routine that calculates how many bytes will be required to
276   encode the values in the format.
277
278   Also checks that the value list is the right size for the format list.
279
280   Returns number of bytes (may be 0), or -1 if there's something wrong, in
281   which case a Python exception has been raised.
282
283   Arguments:
284
285     val_seq: a Fast Sequence (list or tuple), being all the values
286 */
287 static int
288 pytdbpack_calc_reqd_len(char *format_str,
289                         PyObject *val_seq)
290 {
291         int len = 0;
292         char *p;
293         int val_i;
294         int val_len;
295
296         val_len = PySequence_Fast_GET_SIZE(val_seq);
297
298         for (p = format_str, val_i = 0; *p; p++, val_i++) {
299                 char ch = *p;
300                 PyObject *val_obj;
301                 int item_len;
302
303                 if (val_i >= val_len) {
304                         PyErr_Format(PyExc_IndexError,
305                                      "samba.tdbpack.pack: value list is too short for format string");
306                         return -1;
307                 }
308
309                 /* borrow a reference to the item */
310                 val_obj = PySequence_Fast_GET_ITEM(val_seq, val_i);
311                 if (!val_obj)
312                         return -1;
313
314                 item_len = pytdbpack_calc_item_len(ch, val_obj);
315                 if (item_len == -1)
316                         return -1;
317                 else
318                         len += item_len;
319         }
320
321         if (val_i != val_len) {
322                 PyErr_Format(PyExc_IndexError,
323                              "%s: value list is wrong length for format string",
324                              __FUNCTION__);
325                 return -1;
326         }
327
328         return len;
329 }
330
331
332 static PyObject *pytdbpack_bad_type(char ch,
333                                     const char *expected,
334                                     PyObject *val_obj)
335 {
336         PyObject *r = PyObject_Repr(val_obj);
337         if (!r)
338                 return NULL;
339         PyErr_Format(PyExc_TypeError,
340                      "tdbpack: format '%c' requires %s, not %s",
341                      ch, expected, PyString_AS_STRING(r));
342         Py_DECREF(r);
343         return val_obj;
344 }
345
346
347 /*
348  * Calculate the number of bytes required to pack a single value.  While doing
349  * this, also conduct some initial checks that the argument types are
350  * reasonable.
351  *
352  * Returns -1 on exception.
353  */
354 static int
355 pytdbpack_calc_item_len(char ch,
356                         PyObject *val_obj)
357 {
358         if (ch == 'd' || ch == 'w') {
359                 if (!PyInt_Check(val_obj)) {
360                         pytdbpack_bad_type(ch, "Int", val_obj);
361                         return -1;
362                 }
363                 if (ch == 'w')
364                         return 2;
365                 else 
366                         return 4;
367         } else if (ch == 'p') {
368                 return 4;
369         }
370         else if (ch == 'f' || ch == 'P' || ch == 'B') {
371                 /* nul-terminated 8-bit string */
372                 if (!PyString_Check(val_obj)) {
373                         pytdbpack_bad_type(ch, "String", val_obj);
374                 }
375                 
376                 if (ch == 'B') {
377                         /* byte buffer; just use Python string's length, plus
378                            a preceding word */
379                         return 4 + PyString_GET_SIZE(val_obj);
380                 }
381                 else {
382                         /* one nul character */
383                         return 1 + PyString_GET_SIZE(val_obj);
384                 }               
385         }
386         else {  
387                 PyErr_Format(PyExc_ValueError,
388                              "tdbpack: format character '%c' is not supported",
389                              ch);
390                 
391                 return -1;
392         }
393 }
394
395
396 /*
397   XXX: glib and Samba have quicker macro for doing the endianness conversions,
398   but I don't know of one in plain libc, and it's probably not a big deal.  I
399   realize this is kind of dumb because we'll almost always be on x86, but
400   being safe is important.
401 */
402 static void pack_int32(unsigned long val_long, unsigned char **pbuf)
403 {
404         (*pbuf)[0] =         val_long & 0xff;
405         (*pbuf)[1] = (val_long >> 8)  & 0xff;
406         (*pbuf)[2] = (val_long >> 16) & 0xff;
407         (*pbuf)[3] = (val_long >> 24) & 0xff;
408         (*pbuf) += 4;
409 }
410
411
412 static void pack_bytes(long len, const char *from,
413                        unsigned char **pbuf)
414 {
415         memcpy(*pbuf, from, len);
416         (*pbuf) += len;
417 }
418
419
420 static void
421 unpack_err_too_short(void)
422 {
423         PyErr_Format(PyExc_IndexError,
424                      __FUNCTION__ ": data too short for unpack format");
425 }
426
427
428 static PyObject *
429 unpack_int32(char **pbuf, int *plen)
430 {
431         long v;
432         unsigned char *b;
433         
434         if (*plen < 4) {
435                 unpack_err_too_short();
436                 return NULL;
437         }
438
439         b = *pbuf;
440         v = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
441         
442         (*pbuf) += 4;
443         (*plen) -= 4;
444
445         return PyInt_FromLong(v);
446 }
447
448
449 static PyObject *unpack_int16(char **pbuf, int *plen)
450 {
451         long v;
452         unsigned char *b;
453         
454         if (*plen < 2) {
455                 unpack_err_too_short();
456                 return NULL;
457         }
458
459         b = *pbuf;
460         v = b[0] | b[1]<<8;
461         
462         (*pbuf) += 2;
463         (*plen) -= 2;
464
465         return PyInt_FromLong(v);
466 }
467
468
469 static PyObject *
470 unpack_string(char **pbuf, int *plen)
471 {
472         int len;
473         char *nul_ptr, *start;
474
475         start = *pbuf;
476         
477         nul_ptr = memchr(start, '\0', *plen);
478         if (!nul_ptr) {
479                 unpack_err_too_short();
480                 return NULL;
481         }
482
483         len = nul_ptr - start;
484
485         *pbuf += len + 1;       /* skip \0 */
486         *plen -= len + 1;
487
488         return PyString_FromStringAndSize(start, len);
489 }
490
491
492 static PyObject *
493 unpack_buffer(char **pbuf, int *plen)
494 {
495         /* first get 32-bit len */
496         long slen;
497         unsigned char *b;
498         unsigned char *start;
499         
500         if (*plen < 4) {
501                 unpack_err_too_short();
502                 return NULL;
503         }
504         
505         b = *pbuf;
506         slen = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
507
508         if (slen < 0) { /* surely you jest */
509                 PyErr_Format(PyExc_ValueError,
510                              __FUNCTION__ ": buffer seems to have negative length");
511                 return NULL;
512         }
513
514         (*pbuf) += 4;
515         (*plen) -= 4;
516         start = *pbuf;
517
518         if (*plen < slen) {
519                 PyErr_Format(PyExc_IndexError,
520                              __FUNCTION__ ": not enough data to unpack buffer: "
521                              "need %d bytes, have %d",
522                              (int) slen, *plen);
523                 return NULL;
524         }
525
526         (*pbuf) += slen;
527         (*plen) -= slen;
528
529         return PyString_FromStringAndSize(start, slen);
530 }
531
532
533 /* Unpack a single field from packed data, according to format character CH.
534    Remaining data is at *PBUF, of *PLEN.
535
536    *PBUF is advanced, and *PLEN reduced to reflect the amount of data that has
537    been consumed.
538
539    Returns a reference to the unpacked Python object, or NULL for failure.
540 */
541 static PyObject *pytdbpack_unpack_item(char ch,
542                                        char **pbuf,
543                                        int *plen)
544 {
545         if (ch == 'w') {        /* 16-bit int */
546                 return unpack_int16(pbuf, plen);
547         }
548         else if (ch == 'd' || ch == 'p') { /* 32-bit int */
549                 /* pointers can just come through as integers */
550                 return unpack_int32(pbuf, plen);
551         }
552         else if (ch == 'f' || ch == 'P') { /* nul-term string  */
553                 return unpack_string(pbuf, plen);
554         }
555         else if (ch == 'B') { /* length, buffer */
556                 return unpack_buffer(pbuf, plen);
557         }
558         else {
559                 PyErr_Format(PyExc_ValueError,
560                              __FUNCTION__ ": format character '%c' is not supported",
561                              ch);
562                 
563                 return NULL;
564         }
565 }
566
567
568
569 /*
570   Pack a single item VAL_OBJ, encoded using format CH, into a buffer at *PBUF,
571   and advance the pointer.  Buffer length has been pre-calculated so we are
572   sure that there is enough space.
573
574 */
575 static PyObject *
576 pytdbpack_pack_item(char ch,
577                     PyObject *val_obj,
578                     unsigned char **pbuf)
579 {
580         if (ch == 'w') {
581                 unsigned long val_long = PyInt_AsLong(val_obj);
582                 (*pbuf)[0] = val_long & 0xff;
583                 (*pbuf)[1] = (val_long >> 8) & 0xff;
584                 (*pbuf) += 2;
585         }
586         else if (ch == 'd') {
587                 /* 4-byte LE number */
588                 pack_int32(PyInt_AsLong(val_obj), pbuf);
589         }
590         else if (ch == 'p') {
591                 /* "Pointer" value -- in the subset of DCERPC used by Samba,
592                    this is really just an "exists" or "does not exist"
593                    flag. */
594                 pack_int32(PyObject_IsTrue(val_obj), pbuf);
595         }
596         else if (ch == 'f' || ch == 'P') {
597                 int size;
598                 char *sval;
599
600                 size = PyString_GET_SIZE(val_obj);
601                 sval = PyString_AS_STRING(val_obj);
602                 pack_bytes(size+1, sval, pbuf); /* include nul */
603         }
604         else if (ch == 'B') {
605                 int size;
606                 char *sval;
607
608                 size = PyString_GET_SIZE(val_obj);
609                 pack_int32(size, pbuf);
610                 sval = PyString_AS_STRING(val_obj);
611                 pack_bytes(size, sval, pbuf); /* do not include nul */
612         }
613         else {
614                 /* this ought to be caught while calculating the length, but
615                    just in case. */
616                 PyErr_Format(PyExc_ValueError,
617                              "%s: format character '%c' is not supported",
618                              __FUNCTION__, ch);
619                 
620                 return NULL;
621         }
622                 
623         return Py_None;
624 }
625
626
627 /*
628   Pack data according to FORMAT_STR from the elements of VAL_SEQ into
629   PACKED_BUF.
630
631   The string has already been checked out, so we know that VAL_SEQ is large
632   enough to hold the packed data, and that there are enough value items.
633   (However, their types may not have been thoroughly checked yet.)
634
635   In addition, val_seq is a Python Fast sequence.
636
637   Returns NULL for error (with exception set), or None.
638 */
639 PyObject *
640 pytdbpack_pack_data(const char *format_str,
641                     PyObject *val_seq,
642                     unsigned char *packed_buf)
643 {
644         int i;
645
646         for (i = 0; format_str[i]; i++) {
647                 char ch = format_str[i];
648                 PyObject *val_obj;
649
650                 /* borrow a reference to the item */
651                 val_obj = PySequence_Fast_GET_ITEM(val_seq, i);
652                 if (!val_obj)
653                         return NULL;
654
655                 if (!pytdbpack_pack_item(ch, val_obj, &packed_buf))
656                         return NULL;
657         }
658
659         return Py_None;
660 }
661
662
663
664
665
666 static PyMethodDef pytdbpack_methods[] = {
667         { "pack", pytdbpack_pack, METH_VARARGS, (char *) pytdbpack_pack_doc },
668         { "unpack", pytdbpack_unpack, METH_VARARGS, (char *) pytdbpack_unpack_doc },
669 };
670
671 DL_EXPORT(void)
672 inittdbpack(void)
673 {
674         Py_InitModule3("tdbpack", pytdbpack_methods,
675                        (char *) pytdbpack_docstring);
676 }