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