pylibsmb: Avoid a segfault if no credentials are passed to libsmb.Conn()
[metze/samba/wip.git] / source3 / libsmb / pylibsmb.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Samba-internal work in progress Python binding for libsmbclient
4  *
5  * Copyright (C) Volker Lendecke 2012
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <Python.h>
22 #include "includes.h"
23 #include "libsmb/libsmb.h"
24 #include "libcli/security/security.h"
25 #include "system/select.h"
26 #include "source4/libcli/util/pyerrors.h"
27 #include "auth/credentials/pycredentials.h"
28 #include "trans2.h"
29
30 static PyTypeObject *get_pytype(const char *module, const char *type)
31 {
32         PyObject *mod;
33         PyTypeObject *result;
34
35         mod = PyImport_ImportModule(module);
36         if (mod == NULL) {
37                 PyErr_Format(PyExc_RuntimeError,
38                              "Unable to import %s to check type %s",
39                              module, type);
40                 return NULL;
41         }
42         result = (PyTypeObject *)PyObject_GetAttrString(mod, type);
43         Py_DECREF(mod);
44         if (result == NULL) {
45                 PyErr_Format(PyExc_RuntimeError,
46                              "Unable to find type %s in module %s",
47                              module, type);
48                 return NULL;
49         }
50         return result;
51 }
52
53 /*
54  * We're using "const char **" for keywords,
55  * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
56  * inevitable warnings to just one place.
57  */
58 static int ParseTupleAndKeywords(PyObject *args, PyObject *kw,
59                                  const char *format, const char **keywords,
60                                  ...)
61 {
62         va_list a;
63         int ret;
64         va_start(a, keywords);
65         ret = PyArg_VaParseTupleAndKeywords(args, kw, format,
66                                             (char **)keywords, a);
67         va_end(a);
68         return ret;
69 }
70
71 struct py_cli_thread;
72
73 struct py_cli_oplock_break {
74         uint16_t fnum;
75         uint8_t level;
76 };
77
78 struct py_cli_state {
79         PyObject_HEAD
80         struct cli_state *cli;
81         struct tevent_context *ev;
82         struct py_cli_thread *thread_state;
83
84         struct tevent_req *oplock_waiter;
85         struct py_cli_oplock_break *oplock_breaks;
86         struct py_tevent_cond *oplock_cond;
87 };
88
89 #if HAVE_PTHREAD
90
91 #include <pthread.h>
92
93 struct py_cli_thread {
94
95         /*
96          * Pipe to make the poll thread wake up in our destructor, so
97          * that we can exit and join the thread.
98          */
99         int shutdown_pipe[2];
100         struct tevent_fd *shutdown_fde;
101         bool do_shutdown;
102         pthread_t id;
103
104         /*
105          * Thread state to release the GIL during the poll(2) syscall
106          */
107         PyThreadState *py_threadstate;
108 };
109
110 static void *py_cli_state_poll_thread(void *private_data)
111 {
112         struct py_cli_state *self = (struct py_cli_state *)private_data;
113         struct py_cli_thread *t = self->thread_state;
114         PyGILState_STATE gstate;
115
116         gstate = PyGILState_Ensure();
117
118         while (!t->do_shutdown) {
119                 int ret;
120                 ret = tevent_loop_once(self->ev);
121                 assert(ret == 0);
122         }
123         PyGILState_Release(gstate);
124         return NULL;
125 }
126
127 static void py_cli_state_trace_callback(enum tevent_trace_point point,
128                                         void *private_data)
129 {
130         struct py_cli_state *self = (struct py_cli_state *)private_data;
131         struct py_cli_thread *t = self->thread_state;
132
133         switch(point) {
134         case TEVENT_TRACE_BEFORE_WAIT:
135                 assert(t->py_threadstate == NULL);
136                 t->py_threadstate = PyEval_SaveThread();
137                 break;
138         case TEVENT_TRACE_AFTER_WAIT:
139                 assert(t->py_threadstate != NULL);
140                 PyEval_RestoreThread(t->py_threadstate);
141                 t->py_threadstate = NULL;
142                 break;
143         default:
144                 break;
145         }
146 }
147
148 static void py_cli_state_shutdown_handler(struct tevent_context *ev,
149                                           struct tevent_fd *fde,
150                                           uint16_t flags,
151                                           void *private_data)
152 {
153         struct py_cli_state *self = (struct py_cli_state *)private_data;
154         struct py_cli_thread *t = self->thread_state;
155
156         if ((flags & TEVENT_FD_READ) == 0) {
157                 return;
158         }
159         TALLOC_FREE(t->shutdown_fde);
160         t->do_shutdown = true;
161 }
162
163 static int py_cli_thread_destructor(struct py_cli_thread *t)
164 {
165         char c = 0;
166         ssize_t written;
167         int ret;
168
169         do {
170                 /*
171                  * This will wake the poll thread from the poll(2)
172                  */
173                 written = write(t->shutdown_pipe[1], &c, 1);
174         } while ((written == -1) && (errno == EINTR));
175
176         /*
177          * Allow the poll thread to do its own cleanup under the GIL
178          */
179         Py_BEGIN_ALLOW_THREADS
180         ret = pthread_join(t->id, NULL);
181         Py_END_ALLOW_THREADS
182         assert(ret == 0);
183
184         if (t->shutdown_pipe[0] != -1) {
185                 close(t->shutdown_pipe[0]);
186                 t->shutdown_pipe[0] = -1;
187         }
188         if (t->shutdown_pipe[1] != -1) {
189                 close(t->shutdown_pipe[1]);
190                 t->shutdown_pipe[1] = -1;
191         }
192         return 0;
193 }
194
195 static bool py_cli_state_setup_ev(struct py_cli_state *self)
196 {
197         struct py_cli_thread *t = NULL;
198         int ret;
199
200         self->ev = tevent_context_init_byname(NULL, "poll_mt");
201         if (self->ev == NULL) {
202                 goto fail;
203         }
204         samba_tevent_set_debug(self->ev, "pylibsmb_tevent_mt");
205         tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self);
206
207         self->thread_state = talloc_zero(NULL, struct py_cli_thread);
208         if (self->thread_state == NULL) {
209                 goto fail;
210         }
211         t = self->thread_state;
212
213         ret = pipe(t->shutdown_pipe);
214         if (ret == -1) {
215                 goto fail;
216         }
217         t->shutdown_fde = tevent_add_fd(
218                 self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ,
219                 py_cli_state_shutdown_handler, self);
220         if (t->shutdown_fde == NULL) {
221                 goto fail;
222         }
223
224         PyEval_InitThreads();
225
226         ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self);
227         if (ret != 0) {
228                 goto fail;
229         }
230         talloc_set_destructor(self->thread_state, py_cli_thread_destructor);
231         return true;
232
233 fail:
234         if (t != NULL) {
235                 TALLOC_FREE(t->shutdown_fde);
236
237                 if (t->shutdown_pipe[0] != -1) {
238                         close(t->shutdown_pipe[0]);
239                         t->shutdown_pipe[0] = -1;
240                 }
241                 if (t->shutdown_pipe[1] != -1) {
242                         close(t->shutdown_pipe[1]);
243                         t->shutdown_pipe[1] = -1;
244                 }
245         }
246
247         TALLOC_FREE(self->thread_state);
248         TALLOC_FREE(self->ev);
249         return false;
250 }
251
252 struct py_tevent_cond {
253         pthread_mutex_t mutex;
254         pthread_cond_t cond;
255         bool is_done;
256 };
257
258 static void py_tevent_signalme(struct tevent_req *req);
259
260 static int py_tevent_cond_wait(struct py_tevent_cond *cond)
261 {
262         int ret, result;
263
264         result = pthread_mutex_init(&cond->mutex, NULL);
265         if (result != 0) {
266                 goto fail;
267         }
268         result = pthread_cond_init(&cond->cond, NULL);
269         if (result != 0) {
270                 goto fail_mutex;
271         }
272
273         result = pthread_mutex_lock(&cond->mutex);
274         if (result != 0) {
275                 goto fail_cond;
276         }
277
278         cond->is_done = false;
279
280         while (!cond->is_done) {
281
282                 Py_BEGIN_ALLOW_THREADS
283                 result = pthread_cond_wait(&cond->cond, &cond->mutex);
284                 Py_END_ALLOW_THREADS
285
286                 if (result != 0) {
287                         goto fail_unlock;
288                 }
289         }
290
291 fail_unlock:
292         ret = pthread_mutex_unlock(&cond->mutex);
293         assert(ret == 0);
294 fail_cond:
295         ret = pthread_cond_destroy(&cond->cond);
296         assert(ret == 0);
297 fail_mutex:
298         ret = pthread_mutex_destroy(&cond->mutex);
299         assert(ret == 0);
300 fail:
301         return result;
302 }
303
304 static int py_tevent_req_wait(struct tevent_context *ev,
305                               struct tevent_req *req)
306 {
307         struct py_tevent_cond cond;
308         tevent_req_set_callback(req, py_tevent_signalme, &cond);
309         return py_tevent_cond_wait(&cond);
310 }
311
312 static void py_tevent_cond_signal(struct py_tevent_cond *cond)
313 {
314         int ret;
315
316         ret = pthread_mutex_lock(&cond->mutex);
317         assert(ret == 0);
318
319         cond->is_done = true;
320
321         ret = pthread_cond_signal(&cond->cond);
322         assert(ret == 0);
323         ret = pthread_mutex_unlock(&cond->mutex);
324         assert(ret == 0);
325 }
326
327 static void py_tevent_signalme(struct tevent_req *req)
328 {
329         struct py_tevent_cond *cond = (struct py_tevent_cond *)
330                 tevent_req_callback_data_void(req);
331
332         py_tevent_cond_signal(cond);
333 }
334
335 #else
336
337 static bool py_cli_state_setup_ev(struct py_cli_state *self)
338 {
339         self->ev = tevent_context_init(NULL);
340         if (self->ev == NULL) {
341                 return false;
342         }
343
344         samba_tevent_set_debug(self->ev, "pylibsmb_tevent");
345
346         return true;
347 }
348
349 static int py_tevent_req_wait(struct tevent_context *ev,
350                               struct tevent_req *req)
351 {
352         while (tevent_req_is_in_progress(req)) {
353                 int ret;
354
355                 ret = tevent_loop_once(ev);
356                 if (ret != 0) {
357                         return ret;
358                 }
359         }
360         return 0;
361 }
362
363 #endif
364
365 static bool py_tevent_req_wait_exc(struct tevent_context *ev,
366                                    struct tevent_req *req)
367 {
368         int ret;
369
370         if (req == NULL) {
371                 PyErr_NoMemory();
372                 return false;
373         }
374         ret = py_tevent_req_wait(ev, req);
375         if (ret != 0) {
376                 TALLOC_FREE(req);
377                 errno = ret;
378                 PyErr_SetFromErrno(PyExc_RuntimeError);
379                 return false;
380         }
381         return true;
382 }
383
384 static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
385                                   PyObject *kwds)
386 {
387         struct py_cli_state *self;
388
389         self = (struct py_cli_state *)type->tp_alloc(type, 0);
390         if (self == NULL) {
391                 return NULL;
392         }
393         self->cli = NULL;
394         self->ev = NULL;
395         self->thread_state = NULL;
396         self->oplock_waiter = NULL;
397         self->oplock_cond = NULL;
398         self->oplock_breaks = NULL;
399         return (PyObject *)self;
400 }
401
402 static void py_cli_got_oplock_break(struct tevent_req *req);
403
404 static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
405                              PyObject *kwds)
406 {
407         NTSTATUS status;
408         char *host, *share;
409         PyObject *creds = NULL;
410         struct cli_credentials *cli_creds;
411         bool ret;
412
413         static const char *kwlist[] = {
414                 "host", "share", "credentials", NULL
415         };
416
417         PyTypeObject *py_type_Credentials = get_pytype(
418                 "samba.credentials", "Credentials");
419         if (py_type_Credentials == NULL) {
420                 return -1;
421         }
422
423         ret = ParseTupleAndKeywords(
424                 args, kwds, "ss|O!", kwlist,
425                 &host, &share, py_type_Credentials, &creds);
426
427         Py_DECREF(py_type_Credentials);
428
429         if (!ret) {
430                 return -1;
431         }
432
433         if (!py_cli_state_setup_ev(self)) {
434                 return -1;
435         }
436
437         if (creds == NULL) {
438                 cli_creds = cli_credentials_init_anon(NULL);
439         } else {
440                 cli_creds = PyCredentials_AsCliCredentials(creds);
441         }
442
443         status = cli_full_connection(
444                 &self->cli, "myname", host, NULL, 0, share, "?????",
445                 cli_credentials_get_username(cli_creds),
446                 cli_credentials_get_domain(cli_creds),
447                 cli_credentials_get_password(cli_creds),
448                 0, 0);
449         if (!NT_STATUS_IS_OK(status)) {
450                 PyErr_SetNTSTATUS(status);
451                 return -1;
452         }
453
454         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
455                 self->ev, self->ev, self->cli);
456         if (self->oplock_waiter == NULL) {
457                 PyErr_NoMemory();
458                 return -1;
459         }
460         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
461                                 self);
462         return 0;
463 }
464
465 static void py_cli_got_oplock_break(struct tevent_req *req)
466 {
467         struct py_cli_state *self = (struct py_cli_state *)
468                 tevent_req_callback_data_void(req);
469         struct py_cli_oplock_break b;
470         struct py_cli_oplock_break *tmp;
471         size_t num_breaks;
472         NTSTATUS status;
473
474         status = cli_smb_oplock_break_waiter_recv(req, &b.fnum, &b.level);
475         TALLOC_FREE(req);
476         self->oplock_waiter = NULL;
477
478         if (!NT_STATUS_IS_OK(status)) {
479                 return;
480         }
481
482         num_breaks = talloc_array_length(self->oplock_breaks);
483         tmp = talloc_realloc(self->ev, self->oplock_breaks,
484                              struct py_cli_oplock_break, num_breaks+1);
485         if (tmp == NULL) {
486                 return;
487         }
488         self->oplock_breaks = tmp;
489         self->oplock_breaks[num_breaks] = b;
490
491         if (self->oplock_cond != NULL) {
492                 py_tevent_cond_signal(self->oplock_cond);
493         }
494
495         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
496                 self->ev, self->ev, self->cli);
497         if (self->oplock_waiter == NULL) {
498                 return;
499         }
500         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
501                                 self);
502 }
503
504 static PyObject *py_cli_get_oplock_break(struct py_cli_state *self,
505                                          PyObject *args)
506 {
507         size_t num_oplock_breaks;
508
509         if (!PyArg_ParseTuple(args, "")) {
510                 return NULL;
511         }
512
513         if (self->oplock_cond != NULL) {
514                 errno = EBUSY;
515                 PyErr_SetFromErrno(PyExc_RuntimeError);
516                 return NULL;
517         }
518
519         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
520
521         if (num_oplock_breaks == 0) {
522                 struct py_tevent_cond cond;
523                 int ret;
524
525                 self->oplock_cond = &cond;
526                 ret = py_tevent_cond_wait(&cond);
527                 self->oplock_cond = NULL;
528
529                 if (ret != 0) {
530                         errno = ret;
531                         PyErr_SetFromErrno(PyExc_RuntimeError);
532                         return NULL;
533                 }
534         }
535
536         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
537         if (num_oplock_breaks > 0) {
538                 PyObject *result;
539
540                 result = Py_BuildValue(
541                         "{s:i,s:i}",
542                         "fnum", self->oplock_breaks[0].fnum,
543                         "level", self->oplock_breaks[0].level);
544
545                 memmove(&self->oplock_breaks[0], &self->oplock_breaks[1],
546                         sizeof(self->oplock_breaks[0]) *
547                         (num_oplock_breaks - 1));
548                 self->oplock_breaks = talloc_realloc(
549                         NULL, self->oplock_breaks, struct py_cli_oplock_break,
550                         num_oplock_breaks - 1);
551
552                 return result;
553         }
554         Py_RETURN_NONE;
555 }
556
557 static void py_cli_state_dealloc(struct py_cli_state *self)
558 {
559         TALLOC_FREE(self->thread_state);
560         TALLOC_FREE(self->oplock_waiter);
561         TALLOC_FREE(self->ev);
562
563         if (self->cli != NULL) {
564                 cli_shutdown(self->cli);
565                 self->cli = NULL;
566         }
567         self->ob_type->tp_free((PyObject *)self);
568 }
569
570 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
571                                PyObject *kwds)
572 {
573         char *fname;
574         unsigned CreateFlags = 0;
575         unsigned DesiredAccess = FILE_GENERIC_READ;
576         unsigned FileAttributes = 0;
577         unsigned ShareAccess = 0;
578         unsigned CreateDisposition = FILE_OPEN;
579         unsigned CreateOptions = 0;
580         unsigned SecurityFlags = 0;
581         uint16_t fnum;
582         struct tevent_req *req;
583         NTSTATUS status;
584
585         static const char *kwlist[] = {
586                 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
587                 "ShareAccess", "CreateDisposition", "CreateOptions",
588                 "SecurityFlags", NULL };
589
590         if (!ParseTupleAndKeywords(
591                     args, kwds, "s|IIIIIII", kwlist,
592                     &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
593                     &ShareAccess, &CreateDisposition, &CreateOptions,
594                     &SecurityFlags)) {
595                 return NULL;
596         }
597
598         req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
599                                 DesiredAccess, FileAttributes, ShareAccess,
600                                 CreateDisposition, CreateOptions,
601                                 SecurityFlags);
602         if (!py_tevent_req_wait_exc(self->ev, req)) {
603                 return NULL;
604         }
605         status = cli_ntcreate_recv(req, &fnum);
606         TALLOC_FREE(req);
607
608         if (!NT_STATUS_IS_OK(status)) {
609                 PyErr_SetNTSTATUS(status);
610                 return NULL;
611         }
612         return Py_BuildValue("I", (unsigned)fnum);
613 }
614
615 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
616 {
617         struct tevent_req *req;
618         int fnum;
619         NTSTATUS status;
620
621         if (!PyArg_ParseTuple(args, "i", &fnum)) {
622                 return NULL;
623         }
624
625         req = cli_close_send(NULL, self->ev, self->cli, fnum);
626         if (!py_tevent_req_wait_exc(self->ev, req)) {
627                 return NULL;
628         }
629         status = cli_close_recv(req);
630         TALLOC_FREE(req);
631
632         if (!NT_STATUS_IS_OK(status)) {
633                 PyErr_SetNTSTATUS(status);
634                 return NULL;
635         }
636         Py_RETURN_NONE;
637 }
638
639 static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
640                               PyObject *kwds)
641 {
642         int fnum;
643         unsigned mode = 0;
644         char *buf;
645         int buflen;
646         unsigned long long offset;
647         struct tevent_req *req;
648         NTSTATUS status;
649         size_t written;
650
651         static const char *kwlist[] = {
652                 "fnum", "buffer", "offset", "mode", NULL };
653
654         if (!ParseTupleAndKeywords(
655                     args, kwds, "Is#K|I", kwlist,
656                     &fnum, &buf, &buflen, &offset, &mode)) {
657                 return NULL;
658         }
659
660         req = cli_write_andx_send(NULL, self->ev, self->cli, fnum, mode,
661                                   (uint8_t *)buf, offset, buflen);
662         if (!py_tevent_req_wait_exc(self->ev, req)) {
663                 return NULL;
664         }
665         status = cli_write_andx_recv(req, &written);
666         TALLOC_FREE(req);
667
668         if (!NT_STATUS_IS_OK(status)) {
669                 PyErr_SetNTSTATUS(status);
670                 return NULL;
671         }
672         return Py_BuildValue("K", (unsigned long long)written);
673 }
674
675 static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
676                              PyObject *kwds)
677 {
678         int fnum;
679         unsigned long long offset;
680         unsigned size;
681         struct tevent_req *req;
682         NTSTATUS status;
683         uint8_t *buf;
684         ssize_t buflen;
685         PyObject *result;
686
687         static const char *kwlist[] = {
688                 "fnum", "offset", "size", NULL };
689
690         if (!ParseTupleAndKeywords(
691                     args, kwds, "IKI", kwlist, &fnum, &offset,
692                     &size)) {
693                 return NULL;
694         }
695
696         req = cli_read_andx_send(NULL, self->ev, self->cli, fnum,
697                                  offset, size);
698         if (!py_tevent_req_wait_exc(self->ev, req)) {
699                 return NULL;
700         }
701         status = cli_read_andx_recv(req, &buflen, &buf);
702
703         if (!NT_STATUS_IS_OK(status)) {
704                 TALLOC_FREE(req);
705                 PyErr_SetNTSTATUS(status);
706                 return NULL;
707         }
708         result = Py_BuildValue("s#", (char *)buf, (int)buflen);
709         TALLOC_FREE(req);
710         return result;
711 }
712
713 static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args,
714                                   PyObject *kwds)
715 {
716         int fnum;
717         unsigned long long size;
718         struct tevent_req *req;
719         NTSTATUS status;
720
721         static const char *kwlist[] = {
722                 "fnum", "size", NULL };
723
724         if (!ParseTupleAndKeywords(
725                     args, kwds, "IK", kwlist, &fnum, &size)) {
726                 return NULL;
727         }
728
729         req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size);
730         if (!py_tevent_req_wait_exc(self->ev, req)) {
731                 return NULL;
732         }
733         status = cli_ftruncate_recv(req);
734         TALLOC_FREE(req);
735
736         if (!NT_STATUS_IS_OK(status)) {
737                 PyErr_SetNTSTATUS(status);
738                 return NULL;
739         }
740         Py_RETURN_NONE;
741 }
742
743 static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
744                                         PyObject *args,
745                                         PyObject *kwds)
746 {
747         unsigned fnum, flag;
748         struct tevent_req *req;
749         NTSTATUS status;
750
751         static const char *kwlist[] = {
752                 "fnum", "flag", NULL };
753
754         if (!ParseTupleAndKeywords(
755                     args, kwds, "II", kwlist, &fnum, &flag)) {
756                 return NULL;
757         }
758
759         req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum,
760                                           flag);
761         if (!py_tevent_req_wait_exc(self->ev, req)) {
762                 return NULL;
763         }
764         status = cli_nt_delete_on_close_recv(req);
765         TALLOC_FREE(req);
766
767         if (!NT_STATUS_IS_OK(status)) {
768                 PyErr_SetNTSTATUS(status);
769                 return NULL;
770         }
771         Py_RETURN_NONE;
772 }
773
774 static PyObject *py_cli_list(struct py_cli_state *self,
775                              PyObject *args,
776                              PyObject *kwds)
777 {
778         char *mask;
779         unsigned attribute =
780                 FILE_ATTRIBUTE_DIRECTORY |
781                 FILE_ATTRIBUTE_SYSTEM |
782                 FILE_ATTRIBUTE_HIDDEN;
783         unsigned info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
784         struct tevent_req *req;
785         NTSTATUS status;
786         struct file_info *finfos;
787         size_t i, num_finfos;
788         PyObject *result;
789
790         const char *kwlist[] = {
791                 "mask", "attribute", "info_level", NULL
792         };
793
794         if (!ParseTupleAndKeywords(
795                     args, kwds, "s|II", kwlist,
796                     &mask, &attribute, &info_level)) {
797                 return NULL;
798         }
799
800         req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
801                             info_level);
802         if (!py_tevent_req_wait_exc(self->ev, req)) {
803                 return NULL;
804         }
805         status = cli_list_recv(req, NULL, &finfos, &num_finfos);
806         TALLOC_FREE(req);
807
808         if (!NT_STATUS_IS_OK(status)) {
809                 PyErr_SetNTSTATUS(status);
810                 return NULL;
811         }
812
813         result = Py_BuildValue("[]");
814         if (result == NULL) {
815                 return NULL;
816         }
817
818         for (i=0; i<num_finfos; i++) {
819                 struct file_info *finfo = &finfos[i];
820                 PyObject *file;
821                 int ret;
822
823                 file = Py_BuildValue(
824                         "{s:s,s:i}",
825                         "name", finfo->name,
826                         "mode", (int)finfo->mode);
827                 if (file == NULL) {
828                         Py_XDECREF(result);
829                         return NULL;
830                 }
831
832                 ret = PyList_Append(result, file);
833                 if (ret == -1) {
834                         Py_XDECREF(result);
835                         return NULL;
836                 }
837         }
838
839         return result;
840 }
841
842 static PyMethodDef py_cli_state_methods[] = {
843         { "create", (PyCFunction)py_cli_create, METH_VARARGS|METH_KEYWORDS,
844           "Open a file" },
845         { "close", (PyCFunction)py_cli_close, METH_VARARGS,
846           "Close a file handle" },
847         { "write", (PyCFunction)py_cli_write, METH_VARARGS|METH_KEYWORDS,
848           "Write to a file handle" },
849         { "read", (PyCFunction)py_cli_read, METH_VARARGS|METH_KEYWORDS,
850           "Read from a file handle" },
851         { "truncate", (PyCFunction)py_cli_ftruncate,
852           METH_VARARGS|METH_KEYWORDS,
853           "Truncate a file" },
854         { "delete_on_close", (PyCFunction)py_cli_delete_on_close,
855           METH_VARARGS|METH_KEYWORDS,
856           "Set/Reset the delete on close flag" },
857         { "readdir", (PyCFunction)py_cli_list,
858           METH_VARARGS|METH_KEYWORDS,
859           "List a directory" },
860         { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
861           METH_VARARGS, "Wait for an oplock break" },
862         { NULL, NULL, 0, NULL }
863 };
864
865 static PyTypeObject py_cli_state_type = {
866         PyObject_HEAD_INIT(NULL)
867         .tp_name = "libsmb_samba_internal.Conn",
868         .tp_basicsize = sizeof(struct py_cli_state),
869         .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
870         .tp_doc = "libsmb connection",
871         .tp_new = py_cli_state_new,
872         .tp_init = (initproc)py_cli_state_init,
873         .tp_dealloc = (destructor)py_cli_state_dealloc,
874         .tp_methods = py_cli_state_methods,
875 };
876
877 static PyMethodDef py_libsmb_methods[] = {
878         { NULL },
879 };
880
881 void initlibsmb_samba_internal(void);
882 void initlibsmb_samba_internal(void)
883 {
884         PyObject *m;
885
886         talloc_stackframe();
887
888         m = Py_InitModule3("libsmb_samba_internal", py_libsmb_methods,
889                            "libsmb wrapper");
890
891         if (PyType_Ready(&py_cli_state_type) < 0) {
892                 return;
893         }
894         Py_INCREF(&py_cli_state_type);
895         PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type);
896 }