s3:pylibsmb: add echo() support
[samba.git] / source3 / libsmb / pylibsmb.c
1 /*
2  * Unix SMB/CIFS implementation.
3  *
4  * SMB client Python bindings used internally by Samba (for things like
5  * samba-tool). These Python bindings may change without warning, and so
6  * should not be used outside of the Samba codebase.
7  *
8  * Copyright (C) Volker Lendecke 2012
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include <Python.h>
25 #include "includes.h"
26 #include "python/py3compat.h"
27 #include "python/modules.h"
28 #include "libcli/smb/smbXcli_base.h"
29 #include "libsmb/libsmb.h"
30 #include "libcli/security/security.h"
31 #include "system/select.h"
32 #include "source4/libcli/util/pyerrors.h"
33 #include "auth/credentials/pycredentials.h"
34 #include "trans2.h"
35 #include "libsmb/clirap.h"
36 #include "librpc/rpc/pyrpc_util.h"
37
38 #define LIST_ATTRIBUTE_MASK \
39         (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN)
40
41 #define SECINFO_DEFAULT_FLAGS \
42         (SECINFO_OWNER | SECINFO_GROUP | \
43          SECINFO_DACL | SECINFO_PROTECTED_DACL | SECINFO_UNPROTECTED_DACL | \
44          SECINFO_SACL | SECINFO_PROTECTED_SACL | SECINFO_UNPROTECTED_SACL)
45
46 static PyTypeObject *get_pytype(const char *module, const char *type)
47 {
48         PyObject *mod;
49         PyTypeObject *result;
50
51         mod = PyImport_ImportModule(module);
52         if (mod == NULL) {
53                 PyErr_Format(PyExc_RuntimeError,
54                              "Unable to import %s to check type %s",
55                              module, type);
56                 return NULL;
57         }
58         result = (PyTypeObject *)PyObject_GetAttrString(mod, type);
59         Py_DECREF(mod);
60         if (result == NULL) {
61                 PyErr_Format(PyExc_RuntimeError,
62                              "Unable to find type %s in module %s",
63                              module, type);
64                 return NULL;
65         }
66         return result;
67 }
68
69 /*
70  * We're using "const char * const *" for keywords,
71  * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
72  * inevitable warnings to just one place.
73  */
74 static int ParseTupleAndKeywords(PyObject *args, PyObject *kw,
75                                  const char *format, const char * const *keywords,
76                                  ...)
77 {
78         char **_keywords = discard_const_p(char *, keywords);
79         va_list a;
80         int ret;
81         va_start(a, keywords);
82         ret = PyArg_VaParseTupleAndKeywords(args, kw, format,
83                                             _keywords, a);
84         va_end(a);
85         return ret;
86 }
87
88 struct py_cli_thread;
89
90 struct py_cli_oplock_break {
91         uint16_t fnum;
92         uint8_t level;
93 };
94
95 struct py_cli_state {
96         PyObject_HEAD
97         struct cli_state *cli;
98         struct tevent_context *ev;
99         int (*req_wait_fn)(struct tevent_context *ev,
100                            struct tevent_req *req);
101         struct py_cli_thread *thread_state;
102
103         struct tevent_req *oplock_waiter;
104         struct py_cli_oplock_break *oplock_breaks;
105         struct py_tevent_cond *oplock_cond;
106 };
107
108 #ifdef HAVE_PTHREAD
109
110 #include <pthread.h>
111
112 struct py_cli_thread {
113
114         /*
115          * Pipe to make the poll thread wake up in our destructor, so
116          * that we can exit and join the thread.
117          */
118         int shutdown_pipe[2];
119         struct tevent_fd *shutdown_fde;
120         bool do_shutdown;
121         pthread_t id;
122
123         /*
124          * Thread state to release the GIL during the poll(2) syscall
125          */
126         PyThreadState *py_threadstate;
127 };
128
129 static void *py_cli_state_poll_thread(void *private_data)
130 {
131         struct py_cli_state *self = (struct py_cli_state *)private_data;
132         struct py_cli_thread *t = self->thread_state;
133         PyGILState_STATE gstate;
134
135         gstate = PyGILState_Ensure();
136
137         while (!t->do_shutdown) {
138                 int ret;
139                 ret = tevent_loop_once(self->ev);
140                 assert(ret == 0);
141         }
142         PyGILState_Release(gstate);
143         return NULL;
144 }
145
146 static void py_cli_state_trace_callback(enum tevent_trace_point point,
147                                         void *private_data)
148 {
149         struct py_cli_state *self = (struct py_cli_state *)private_data;
150         struct py_cli_thread *t = self->thread_state;
151
152         switch(point) {
153         case TEVENT_TRACE_BEFORE_WAIT:
154                 assert(t->py_threadstate == NULL);
155                 t->py_threadstate = PyEval_SaveThread();
156                 break;
157         case TEVENT_TRACE_AFTER_WAIT:
158                 assert(t->py_threadstate != NULL);
159                 PyEval_RestoreThread(t->py_threadstate);
160                 t->py_threadstate = NULL;
161                 break;
162         default:
163                 break;
164         }
165 }
166
167 static void py_cli_state_shutdown_handler(struct tevent_context *ev,
168                                           struct tevent_fd *fde,
169                                           uint16_t flags,
170                                           void *private_data)
171 {
172         struct py_cli_state *self = (struct py_cli_state *)private_data;
173         struct py_cli_thread *t = self->thread_state;
174
175         if ((flags & TEVENT_FD_READ) == 0) {
176                 return;
177         }
178         TALLOC_FREE(t->shutdown_fde);
179         t->do_shutdown = true;
180 }
181
182 static int py_cli_thread_destructor(struct py_cli_thread *t)
183 {
184         char c = 0;
185         ssize_t written;
186         int ret;
187
188         do {
189                 /*
190                  * This will wake the poll thread from the poll(2)
191                  */
192                 written = write(t->shutdown_pipe[1], &c, 1);
193         } while ((written == -1) && (errno == EINTR));
194
195         /*
196          * Allow the poll thread to do its own cleanup under the GIL
197          */
198         Py_BEGIN_ALLOW_THREADS
199         ret = pthread_join(t->id, NULL);
200         Py_END_ALLOW_THREADS
201         assert(ret == 0);
202
203         if (t->shutdown_pipe[0] != -1) {
204                 close(t->shutdown_pipe[0]);
205                 t->shutdown_pipe[0] = -1;
206         }
207         if (t->shutdown_pipe[1] != -1) {
208                 close(t->shutdown_pipe[1]);
209                 t->shutdown_pipe[1] = -1;
210         }
211         return 0;
212 }
213
214 static int py_tevent_cond_req_wait(struct tevent_context *ev,
215                                    struct tevent_req *req);
216
217 static bool py_cli_state_setup_mt_ev(struct py_cli_state *self)
218 {
219         struct py_cli_thread *t = NULL;
220         int ret;
221
222         self->ev = tevent_context_init_byname(NULL, "poll_mt");
223         if (self->ev == NULL) {
224                 goto fail;
225         }
226         samba_tevent_set_debug(self->ev, "pylibsmb_tevent_mt");
227         tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self);
228
229         self->req_wait_fn = py_tevent_cond_req_wait;
230
231         self->thread_state = talloc_zero(NULL, struct py_cli_thread);
232         if (self->thread_state == NULL) {
233                 goto fail;
234         }
235         t = self->thread_state;
236
237         ret = pipe(t->shutdown_pipe);
238         if (ret == -1) {
239                 goto fail;
240         }
241         t->shutdown_fde = tevent_add_fd(
242                 self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ,
243                 py_cli_state_shutdown_handler, self);
244         if (t->shutdown_fde == NULL) {
245                 goto fail;
246         }
247
248         PyEval_InitThreads();
249
250         ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self);
251         if (ret != 0) {
252                 goto fail;
253         }
254         talloc_set_destructor(self->thread_state, py_cli_thread_destructor);
255         return true;
256
257 fail:
258         if (t != NULL) {
259                 TALLOC_FREE(t->shutdown_fde);
260
261                 if (t->shutdown_pipe[0] != -1) {
262                         close(t->shutdown_pipe[0]);
263                         t->shutdown_pipe[0] = -1;
264                 }
265                 if (t->shutdown_pipe[1] != -1) {
266                         close(t->shutdown_pipe[1]);
267                         t->shutdown_pipe[1] = -1;
268                 }
269         }
270
271         TALLOC_FREE(self->thread_state);
272         TALLOC_FREE(self->ev);
273         return false;
274 }
275
276 struct py_tevent_cond {
277         pthread_mutex_t mutex;
278         pthread_cond_t cond;
279         bool is_done;
280 };
281
282 static void py_tevent_signalme(struct tevent_req *req);
283
284 static int py_tevent_cond_wait(struct py_tevent_cond *cond)
285 {
286         int ret, result;
287
288         result = pthread_mutex_init(&cond->mutex, NULL);
289         if (result != 0) {
290                 goto fail;
291         }
292         result = pthread_cond_init(&cond->cond, NULL);
293         if (result != 0) {
294                 goto fail_mutex;
295         }
296
297         result = pthread_mutex_lock(&cond->mutex);
298         if (result != 0) {
299                 goto fail_cond;
300         }
301
302         cond->is_done = false;
303
304         while (!cond->is_done) {
305
306                 Py_BEGIN_ALLOW_THREADS
307                 result = pthread_cond_wait(&cond->cond, &cond->mutex);
308                 Py_END_ALLOW_THREADS
309
310                 if (result != 0) {
311                         goto fail_unlock;
312                 }
313         }
314
315 fail_unlock:
316         ret = pthread_mutex_unlock(&cond->mutex);
317         assert(ret == 0);
318 fail_cond:
319         ret = pthread_cond_destroy(&cond->cond);
320         assert(ret == 0);
321 fail_mutex:
322         ret = pthread_mutex_destroy(&cond->mutex);
323         assert(ret == 0);
324 fail:
325         return result;
326 }
327
328 static int py_tevent_cond_req_wait(struct tevent_context *ev,
329                                    struct tevent_req *req)
330 {
331         struct py_tevent_cond cond;
332         tevent_req_set_callback(req, py_tevent_signalme, &cond);
333         return py_tevent_cond_wait(&cond);
334 }
335
336 static void py_tevent_cond_signal(struct py_tevent_cond *cond)
337 {
338         int ret;
339
340         ret = pthread_mutex_lock(&cond->mutex);
341         assert(ret == 0);
342
343         cond->is_done = true;
344
345         ret = pthread_cond_signal(&cond->cond);
346         assert(ret == 0);
347         ret = pthread_mutex_unlock(&cond->mutex);
348         assert(ret == 0);
349 }
350
351 static void py_tevent_signalme(struct tevent_req *req)
352 {
353         struct py_tevent_cond *cond = (struct py_tevent_cond *)
354                 tevent_req_callback_data_void(req);
355
356         py_tevent_cond_signal(cond);
357 }
358
359 #endif
360
361 static int py_tevent_req_wait(struct tevent_context *ev,
362                               struct tevent_req *req);
363
364 static bool py_cli_state_setup_ev(struct py_cli_state *self)
365 {
366         self->ev = tevent_context_init(NULL);
367         if (self->ev == NULL) {
368                 return false;
369         }
370
371         samba_tevent_set_debug(self->ev, "pylibsmb_tevent");
372
373         self->req_wait_fn = py_tevent_req_wait;
374
375         return true;
376 }
377
378 static int py_tevent_req_wait(struct tevent_context *ev,
379                               struct tevent_req *req)
380 {
381         while (tevent_req_is_in_progress(req)) {
382                 int ret;
383
384                 ret = tevent_loop_once(ev);
385                 if (ret != 0) {
386                         return ret;
387                 }
388         }
389         return 0;
390 }
391
392 static bool py_tevent_req_wait_exc(struct py_cli_state *self,
393                                    struct tevent_req *req)
394 {
395         int ret;
396
397         if (req == NULL) {
398                 PyErr_NoMemory();
399                 return false;
400         }
401         ret = self->req_wait_fn(self->ev, req);
402         if (ret != 0) {
403                 TALLOC_FREE(req);
404                 errno = ret;
405                 PyErr_SetFromErrno(PyExc_RuntimeError);
406                 return false;
407         }
408         return true;
409 }
410
411 static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
412                                   PyObject *kwds)
413 {
414         struct py_cli_state *self;
415
416         self = (struct py_cli_state *)type->tp_alloc(type, 0);
417         if (self == NULL) {
418                 return NULL;
419         }
420         self->cli = NULL;
421         self->ev = NULL;
422         self->thread_state = NULL;
423         self->oplock_waiter = NULL;
424         self->oplock_cond = NULL;
425         self->oplock_breaks = NULL;
426         return (PyObject *)self;
427 }
428
429 static void py_cli_got_oplock_break(struct tevent_req *req);
430
431 static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
432                              PyObject *kwds)
433 {
434         NTSTATUS status;
435         char *host, *share;
436         PyObject *creds = NULL;
437         struct cli_credentials *cli_creds;
438         PyObject *py_lp = Py_None;
439         PyObject *py_multi_threaded = Py_False;
440         bool multi_threaded = false;
441         PyObject *py_force_smb1 = Py_False;
442         bool force_smb1 = false;
443         PyObject *py_ipc = Py_False;
444         bool use_ipc = false;
445         struct tevent_req *req;
446         bool ret;
447         int flags = 0;
448
449         static const char *kwlist[] = {
450                 "host", "share", "lp", "creds",
451                 "multi_threaded", "force_smb1",
452                 "ipc",
453                 NULL
454         };
455
456         PyTypeObject *py_type_Credentials = get_pytype(
457                 "samba.credentials", "Credentials");
458         if (py_type_Credentials == NULL) {
459                 return -1;
460         }
461
462         ret = ParseTupleAndKeywords(
463                 args, kwds, "ssO|O!OOO", kwlist,
464                 &host, &share, &py_lp,
465                 py_type_Credentials, &creds,
466                 &py_multi_threaded,
467                 &py_force_smb1,
468                 &py_ipc);
469
470         Py_DECREF(py_type_Credentials);
471
472         if (!ret) {
473                 return -1;
474         }
475
476         multi_threaded = PyObject_IsTrue(py_multi_threaded);
477         force_smb1 = PyObject_IsTrue(py_force_smb1);
478
479         if (force_smb1) {
480                 /*
481                  * As most of the cli_*_send() function
482                  * don't support SMB2 (it's only plugged
483                  * into the sync wrapper functions currently)
484                  * we have a way to force SMB1.
485                  */
486                 flags = CLI_FULL_CONNECTION_FORCE_SMB1;
487         }
488
489         use_ipc = PyObject_IsTrue(py_ipc);
490         if (use_ipc) {
491                 flags |= CLI_FULL_CONNECTION_IPC;
492         }
493
494         if (multi_threaded) {
495 #ifdef HAVE_PTHREAD
496                 ret = py_cli_state_setup_mt_ev(self);
497                 if (!ret) {
498                         return -1;
499                 }
500 #else
501                 PyErr_SetString(PyExc_RuntimeError,
502                                 "No PTHREAD support available");
503                 return -1;
504 #endif
505         } else {
506                 ret = py_cli_state_setup_ev(self);
507                 if (!ret) {
508                         return -1;
509                 }
510         }
511
512         if (creds == NULL) {
513                 cli_creds = cli_credentials_init_anon(NULL);
514         } else {
515                 cli_creds = PyCredentials_AsCliCredentials(creds);
516         }
517
518         req = cli_full_connection_creds_send(
519                 NULL, self->ev, "myname", host, NULL, 0, share, "?????",
520                 cli_creds, flags);
521         if (!py_tevent_req_wait_exc(self, req)) {
522                 return -1;
523         }
524         status = cli_full_connection_creds_recv(req, &self->cli);
525         TALLOC_FREE(req);
526
527         if (!NT_STATUS_IS_OK(status)) {
528                 PyErr_SetNTSTATUS(status);
529                 return -1;
530         }
531
532         /*
533          * Oplocks require a multi threaded connection
534          */
535         if (self->thread_state == NULL) {
536                 return 0;
537         }
538
539         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
540                 self->ev, self->ev, self->cli);
541         if (self->oplock_waiter == NULL) {
542                 PyErr_NoMemory();
543                 return -1;
544         }
545         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
546                                 self);
547         return 0;
548 }
549
550 static void py_cli_got_oplock_break(struct tevent_req *req)
551 {
552         struct py_cli_state *self = (struct py_cli_state *)
553                 tevent_req_callback_data_void(req);
554         struct py_cli_oplock_break b;
555         struct py_cli_oplock_break *tmp;
556         size_t num_breaks;
557         NTSTATUS status;
558
559         status = cli_smb_oplock_break_waiter_recv(req, &b.fnum, &b.level);
560         TALLOC_FREE(req);
561         self->oplock_waiter = NULL;
562
563         if (!NT_STATUS_IS_OK(status)) {
564                 return;
565         }
566
567         num_breaks = talloc_array_length(self->oplock_breaks);
568         tmp = talloc_realloc(self->ev, self->oplock_breaks,
569                              struct py_cli_oplock_break, num_breaks+1);
570         if (tmp == NULL) {
571                 return;
572         }
573         self->oplock_breaks = tmp;
574         self->oplock_breaks[num_breaks] = b;
575
576         if (self->oplock_cond != NULL) {
577                 py_tevent_cond_signal(self->oplock_cond);
578         }
579
580         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
581                 self->ev, self->ev, self->cli);
582         if (self->oplock_waiter == NULL) {
583                 return;
584         }
585         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
586                                 self);
587 }
588
589 static PyObject *py_cli_get_oplock_break(struct py_cli_state *self,
590                                          PyObject *args)
591 {
592         size_t num_oplock_breaks;
593
594         if (!PyArg_ParseTuple(args, "")) {
595                 return NULL;
596         }
597
598         if (self->thread_state == NULL) {
599                 PyErr_SetString(PyExc_RuntimeError,
600                                 "get_oplock_break() only possible on "
601                                 "a multi_threaded connection");
602                 return NULL;
603         }
604
605         if (self->oplock_cond != NULL) {
606                 errno = EBUSY;
607                 PyErr_SetFromErrno(PyExc_RuntimeError);
608                 return NULL;
609         }
610
611         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
612
613         if (num_oplock_breaks == 0) {
614                 struct py_tevent_cond cond;
615                 int ret;
616
617                 self->oplock_cond = &cond;
618                 ret = py_tevent_cond_wait(&cond);
619                 self->oplock_cond = NULL;
620
621                 if (ret != 0) {
622                         errno = ret;
623                         PyErr_SetFromErrno(PyExc_RuntimeError);
624                         return NULL;
625                 }
626         }
627
628         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
629         if (num_oplock_breaks > 0) {
630                 PyObject *result;
631
632                 result = Py_BuildValue(
633                         "{s:i,s:i}",
634                         "fnum", self->oplock_breaks[0].fnum,
635                         "level", self->oplock_breaks[0].level);
636
637                 memmove(&self->oplock_breaks[0], &self->oplock_breaks[1],
638                         sizeof(self->oplock_breaks[0]) *
639                         (num_oplock_breaks - 1));
640                 self->oplock_breaks = talloc_realloc(
641                         NULL, self->oplock_breaks, struct py_cli_oplock_break,
642                         num_oplock_breaks - 1);
643
644                 return result;
645         }
646         Py_RETURN_NONE;
647 }
648
649 static void py_cli_state_dealloc(struct py_cli_state *self)
650 {
651         TALLOC_FREE(self->thread_state);
652         TALLOC_FREE(self->oplock_waiter);
653         TALLOC_FREE(self->ev);
654
655         if (self->cli != NULL) {
656                 cli_shutdown(self->cli);
657                 self->cli = NULL;
658         }
659         Py_TYPE(self)->tp_free((PyObject *)self);
660 }
661
662 static PyObject *py_cli_settimeout(struct py_cli_state *self, PyObject *args)
663 {
664         unsigned int nmsecs = 0;
665         unsigned int omsecs = 0;
666
667         if (!PyArg_ParseTuple(args, "I", &nmsecs)) {
668                 return NULL;
669         }
670
671         omsecs = cli_set_timeout(self->cli, nmsecs);
672
673         return PyLong_FromLong(omsecs);
674 }
675
676 static PyObject *py_cli_echo(struct py_cli_state *self,
677                              PyObject *Py_UNUSED(ignored))
678 {
679         DATA_BLOB data = data_blob_string_const("keepalive");
680         struct tevent_req *req = NULL;
681         NTSTATUS status;
682
683         req = cli_echo_send(NULL, self->ev, self->cli, 1, data);
684         if (!py_tevent_req_wait_exc(self, req)) {
685                 return NULL;
686         }
687         status = cli_echo_recv(req);
688         TALLOC_FREE(req);
689         PyErr_NTSTATUS_NOT_OK_RAISE(status);
690
691         Py_RETURN_NONE;
692 }
693
694 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
695                                PyObject *kwds)
696 {
697         char *fname;
698         unsigned CreateFlags = 0;
699         unsigned DesiredAccess = FILE_GENERIC_READ;
700         unsigned FileAttributes = 0;
701         unsigned ShareAccess = 0;
702         unsigned CreateDisposition = FILE_OPEN;
703         unsigned CreateOptions = 0;
704         unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
705         unsigned SecurityFlags = 0;
706         uint16_t fnum;
707         struct tevent_req *req;
708         NTSTATUS status;
709
710         static const char *kwlist[] = {
711                 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
712                 "ShareAccess", "CreateDisposition", "CreateOptions",
713                 "ImpersonationLevel", "SecurityFlags", NULL };
714
715         if (!ParseTupleAndKeywords(
716                     args, kwds, "s|IIIIIIII", kwlist,
717                     &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
718                     &ShareAccess, &CreateDisposition, &CreateOptions,
719                     &ImpersonationLevel, &SecurityFlags)) {
720                 return NULL;
721         }
722
723         req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
724                                 DesiredAccess, FileAttributes, ShareAccess,
725                                 CreateDisposition, CreateOptions,
726                                 ImpersonationLevel, SecurityFlags);
727         if (!py_tevent_req_wait_exc(self, req)) {
728                 return NULL;
729         }
730         status = cli_ntcreate_recv(req, &fnum, NULL);
731         TALLOC_FREE(req);
732
733         if (!NT_STATUS_IS_OK(status)) {
734                 PyErr_SetNTSTATUS(status);
735                 return NULL;
736         }
737         return Py_BuildValue("I", (unsigned)fnum);
738 }
739
740 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
741 {
742         struct tevent_req *req;
743         int fnum;
744         NTSTATUS status;
745
746         if (!PyArg_ParseTuple(args, "i", &fnum)) {
747                 return NULL;
748         }
749
750         req = cli_close_send(NULL, self->ev, self->cli, fnum);
751         if (!py_tevent_req_wait_exc(self, req)) {
752                 return NULL;
753         }
754         status = cli_close_recv(req);
755         TALLOC_FREE(req);
756
757         if (!NT_STATUS_IS_OK(status)) {
758                 PyErr_SetNTSTATUS(status);
759                 return NULL;
760         }
761         Py_RETURN_NONE;
762 }
763
764 static PyObject *py_cli_rename(
765         struct py_cli_state *self, PyObject *args, PyObject *kwds)
766 {
767         char *fname_src = NULL, *fname_dst = NULL;
768         int replace = false;
769         struct tevent_req *req = NULL;
770         NTSTATUS status;
771         bool ok;
772
773         static const char *kwlist[] = { "src", "dst", "replace", NULL };
774
775         ok = ParseTupleAndKeywords(
776                 args, kwds, "ss|p", kwlist, &fname_src, &fname_dst, &replace);
777         if (!ok) {
778                 return NULL;
779         }
780
781         req = cli_rename_send(
782                 NULL, self->ev, self->cli, fname_src, fname_dst, replace);
783         if (!py_tevent_req_wait_exc(self, req)) {
784                 return NULL;
785         }
786         status = cli_rename_recv(req);
787         TALLOC_FREE(req);
788
789         if (!NT_STATUS_IS_OK(status)) {
790                 PyErr_SetNTSTATUS(status);
791                 return NULL;
792         }
793         Py_RETURN_NONE;
794 }
795
796
797 struct push_state {
798         char *data;
799         off_t nread;
800         off_t total_data;
801 };
802
803 /*
804  * cli_push() helper to write a chunk of data to a remote file
805  */
806 static size_t push_data(uint8_t *buf, size_t n, void *priv)
807 {
808         struct push_state *state = (struct push_state *)priv;
809         char *curr_ptr = NULL;
810         off_t remaining;
811         size_t copied_bytes;
812
813         if (state->nread >= state->total_data) {
814                 return 0;
815         }
816
817         curr_ptr = state->data + state->nread;
818         remaining = state->total_data - state->nread;
819         copied_bytes = MIN(remaining, n);
820
821         memcpy(buf, curr_ptr, copied_bytes);
822         state->nread += copied_bytes;
823         return copied_bytes;
824 }
825
826 /*
827  * Writes a file with the contents specified
828  */
829 static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args)
830 {
831         uint16_t fnum;
832         const char *filename = NULL;
833         char *data = NULL;
834         Py_ssize_t size = 0;
835         NTSTATUS status;
836         struct tevent_req *req = NULL;
837         struct push_state state;
838
839         if (!PyArg_ParseTuple(args, "s"PYARG_BYTES_LEN":savefile", &filename,
840                               &data, &size)) {
841                 return NULL;
842         }
843
844         /* create a new file handle for writing to */
845         req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
846                                 FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL,
847                                 FILE_SHARE_READ|FILE_SHARE_WRITE,
848                                 FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE,
849                                 SMB2_IMPERSONATION_IMPERSONATION, 0);
850         if (!py_tevent_req_wait_exc(self, req)) {
851                 return NULL;
852         }
853         status = cli_ntcreate_recv(req, &fnum, NULL);
854         TALLOC_FREE(req);
855         PyErr_NTSTATUS_NOT_OK_RAISE(status);
856
857         /* write the new file contents */
858         state.data = data;
859         state.nread = 0;
860         state.total_data = size;
861
862         req = cli_push_send(NULL, self->ev, self->cli, fnum, 0, 0, 0,
863                             push_data, &state);
864         if (!py_tevent_req_wait_exc(self, req)) {
865                 return NULL;
866         }
867         status = cli_push_recv(req);
868         TALLOC_FREE(req);
869         PyErr_NTSTATUS_NOT_OK_RAISE(status);
870
871         /* close the file handle */
872         req = cli_close_send(NULL, self->ev, self->cli, fnum);
873         if (!py_tevent_req_wait_exc(self, req)) {
874                 return NULL;
875         }
876         status = cli_close_recv(req);
877         PyErr_NTSTATUS_NOT_OK_RAISE(status);
878
879         Py_RETURN_NONE;
880 }
881
882 static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
883                               PyObject *kwds)
884 {
885         int fnum;
886         unsigned mode = 0;
887         char *buf;
888         Py_ssize_t buflen;
889         unsigned long long offset;
890         struct tevent_req *req;
891         NTSTATUS status;
892         size_t written;
893
894         static const char *kwlist[] = {
895                 "fnum", "buffer", "offset", "mode", NULL };
896
897         if (!ParseTupleAndKeywords(
898                     args, kwds, "i" PYARG_BYTES_LEN "K|I", kwlist,
899                     &fnum, &buf, &buflen, &offset, &mode)) {
900                 return NULL;
901         }
902
903         req = cli_write_send(NULL, self->ev, self->cli, fnum, mode,
904                              (uint8_t *)buf, offset, buflen);
905         if (!py_tevent_req_wait_exc(self, req)) {
906                 return NULL;
907         }
908         status = cli_write_recv(req, &written);
909         TALLOC_FREE(req);
910
911         if (!NT_STATUS_IS_OK(status)) {
912                 PyErr_SetNTSTATUS(status);
913                 return NULL;
914         }
915         return Py_BuildValue("K", (unsigned long long)written);
916 }
917
918 /*
919  * Returns the size of the given file
920  */
921 static NTSTATUS py_smb_filesize(struct py_cli_state *self, uint16_t fnum,
922                                 off_t *size)
923 {
924         NTSTATUS status;
925         struct tevent_req *req = NULL;
926
927         req = cli_qfileinfo_basic_send(NULL, self->ev, self->cli, fnum);
928         if (!py_tevent_req_wait_exc(self, req)) {
929                 return NT_STATUS_INTERNAL_ERROR;
930         }
931         status = cli_qfileinfo_basic_recv(
932                 req, NULL, size, NULL, NULL, NULL, NULL, NULL);
933         TALLOC_FREE(req);
934         return status;
935 }
936
937 /*
938  * Loads the specified file's contents and returns it
939  */
940 static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args)
941 {
942         NTSTATUS status;
943         const char *filename = NULL;
944         struct tevent_req *req = NULL;
945         uint16_t fnum;
946         off_t size;
947         char *buf = NULL;
948         off_t nread = 0;
949         PyObject *result = NULL;
950
951         if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
952                 return NULL;
953         }
954
955         /* get a read file handle */
956         req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
957                                 FILE_READ_DATA | FILE_READ_ATTRIBUTES,
958                                 FILE_ATTRIBUTE_NORMAL,
959                                 FILE_SHARE_READ, FILE_OPEN, 0,
960                                 SMB2_IMPERSONATION_IMPERSONATION, 0);
961         if (!py_tevent_req_wait_exc(self, req)) {
962                 return NULL;
963         }
964         status = cli_ntcreate_recv(req, &fnum, NULL);
965         TALLOC_FREE(req);
966         PyErr_NTSTATUS_NOT_OK_RAISE(status);
967
968         /* get a buffer to hold the file contents */
969         status = py_smb_filesize(self, fnum, &size);
970         PyErr_NTSTATUS_NOT_OK_RAISE(status);
971
972         result = PyBytes_FromStringAndSize(NULL, size);
973         if (result == NULL) {
974                 return NULL;
975         }
976
977         /* read the file contents */
978         buf = PyBytes_AS_STRING(result);
979         req = cli_pull_send(NULL, self->ev, self->cli, fnum, 0, size,
980                             size, cli_read_sink, &buf);
981         if (!py_tevent_req_wait_exc(self, req)) {
982                 Py_XDECREF(result);
983                 return NULL;
984         }
985         status = cli_pull_recv(req, &nread);
986         TALLOC_FREE(req);
987         if (!NT_STATUS_IS_OK(status)) {
988                 Py_XDECREF(result);
989                 PyErr_SetNTSTATUS(status);
990                 return NULL;
991         }
992
993         /* close the file handle */
994         req = cli_close_send(NULL, self->ev, self->cli, fnum);
995         if (!py_tevent_req_wait_exc(self, req)) {
996                 Py_XDECREF(result);
997                 return NULL;
998         }
999         status = cli_close_recv(req);
1000         TALLOC_FREE(req);
1001         if (!NT_STATUS_IS_OK(status)) {
1002                 Py_XDECREF(result);
1003                 PyErr_SetNTSTATUS(status);
1004                 return NULL;
1005         }
1006
1007         /* sanity-check we read the expected number of bytes */
1008         if (nread > size) {
1009                 Py_XDECREF(result);
1010                 PyErr_Format(PyExc_IOError,
1011                              "read invalid - got %zu requested %zu",
1012                              nread, size);
1013                 return NULL;
1014         }
1015
1016         if (nread < size) {
1017                 if (_PyBytes_Resize(&result, nread) < 0) {
1018                         return NULL;
1019                 }
1020         }
1021
1022         return result;
1023 }
1024
1025 static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
1026                              PyObject *kwds)
1027 {
1028         int fnum;
1029         unsigned long long offset;
1030         unsigned size;
1031         struct tevent_req *req;
1032         NTSTATUS status;
1033         char *buf;
1034         size_t received;
1035         PyObject *result;
1036
1037         static const char *kwlist[] = {
1038                 "fnum", "offset", "size", NULL };
1039
1040         if (!ParseTupleAndKeywords(
1041                     args, kwds, "iKI", kwlist, &fnum, &offset,
1042                     &size)) {
1043                 return NULL;
1044         }
1045
1046         result = PyBytes_FromStringAndSize(NULL, size);
1047         if (result == NULL) {
1048                 return NULL;
1049         }
1050         buf = PyBytes_AS_STRING(result);
1051
1052         req = cli_read_send(NULL, self->ev, self->cli, fnum,
1053                             buf, offset, size);
1054         if (!py_tevent_req_wait_exc(self, req)) {
1055                 Py_XDECREF(result);
1056                 return NULL;
1057         }
1058         status = cli_read_recv(req, &received);
1059         TALLOC_FREE(req);
1060
1061         if (!NT_STATUS_IS_OK(status)) {
1062                 Py_XDECREF(result);
1063                 PyErr_SetNTSTATUS(status);
1064                 return NULL;
1065         }
1066
1067         if (received > size) {
1068                 Py_XDECREF(result);
1069                 PyErr_Format(PyExc_IOError,
1070                              "read invalid - got %zu requested %u",
1071                              received, size);
1072                 return NULL;
1073         }
1074
1075         if (received < size) {
1076                 if (_PyBytes_Resize(&result, received) < 0) {
1077                         return NULL;
1078                 }
1079         }
1080
1081         return result;
1082 }
1083
1084 static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args,
1085                                   PyObject *kwds)
1086 {
1087         int fnum;
1088         unsigned long long size;
1089         struct tevent_req *req;
1090         NTSTATUS status;
1091
1092         static const char *kwlist[] = {
1093                 "fnum", "size", NULL };
1094
1095         if (!ParseTupleAndKeywords(
1096                     args, kwds, "IK", kwlist, &fnum, &size)) {
1097                 return NULL;
1098         }
1099
1100         req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size);
1101         if (!py_tevent_req_wait_exc(self, req)) {
1102                 return NULL;
1103         }
1104         status = cli_ftruncate_recv(req);
1105         TALLOC_FREE(req);
1106
1107         if (!NT_STATUS_IS_OK(status)) {
1108                 PyErr_SetNTSTATUS(status);
1109                 return NULL;
1110         }
1111         Py_RETURN_NONE;
1112 }
1113
1114 static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
1115                                         PyObject *args,
1116                                         PyObject *kwds)
1117 {
1118         unsigned fnum, flag;
1119         struct tevent_req *req;
1120         NTSTATUS status;
1121
1122         static const char *kwlist[] = {
1123                 "fnum", "flag", NULL };
1124
1125         if (!ParseTupleAndKeywords(
1126                     args, kwds, "II", kwlist, &fnum, &flag)) {
1127                 return NULL;
1128         }
1129
1130         req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum,
1131                                           flag);
1132         if (!py_tevent_req_wait_exc(self, req)) {
1133                 return NULL;
1134         }
1135         status = cli_nt_delete_on_close_recv(req);
1136         TALLOC_FREE(req);
1137
1138         if (!NT_STATUS_IS_OK(status)) {
1139                 PyErr_SetNTSTATUS(status);
1140                 return NULL;
1141         }
1142         Py_RETURN_NONE;
1143 }
1144
1145 /*
1146  * Helper to add directory listing entries to an overall Python list
1147  */
1148 static NTSTATUS list_helper(struct file_info *finfo,
1149                             const char *mask, void *state)
1150 {
1151         PyObject *result = (PyObject *)state;
1152         PyObject *file = NULL;
1153         PyObject *size = NULL;
1154         int ret;
1155
1156         /* suppress '.' and '..' in the results we return */
1157         if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1158                 return NT_STATUS_OK;
1159         }
1160         size = PyLong_FromUnsignedLongLong(finfo->size);
1161         /*
1162          * Build a dictionary representing the file info.
1163          * Note: Windows does not always return short_name (so it may be None)
1164          */
1165         file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}",
1166                              "name", finfo->name,
1167                              "attrib", (int)finfo->attr,
1168                              "short_name", finfo->short_name,
1169                              "size", size,
1170                              "mtime",
1171                              convert_timespec_to_time_t(finfo->mtime_ts));
1172
1173         Py_CLEAR(size);
1174
1175         if (file == NULL) {
1176                 return NT_STATUS_NO_MEMORY;
1177         }
1178
1179         ret = PyList_Append(result, file);
1180         Py_CLEAR(file);
1181         if (ret == -1) {
1182                 return NT_STATUS_INTERNAL_ERROR;
1183         }
1184
1185         return NT_STATUS_OK;
1186 }
1187
1188 struct do_listing_state {
1189         const char *mask;
1190         NTSTATUS (*callback_fn)(
1191                 struct file_info *finfo,
1192                 const char *mask,
1193                 void *private_data);
1194         void *private_data;
1195         NTSTATUS status;
1196 };
1197
1198 static void do_listing_cb(struct tevent_req *subreq)
1199 {
1200         struct do_listing_state *state = tevent_req_callback_data_void(subreq);
1201         struct file_info *finfo = NULL;
1202
1203         state->status = cli_list_recv(subreq, NULL, &finfo);
1204         if (!NT_STATUS_IS_OK(state->status)) {
1205                 return;
1206         }
1207         state->callback_fn(finfo, state->mask, state->private_data);
1208         TALLOC_FREE(finfo);
1209 }
1210
1211 static NTSTATUS do_listing(struct py_cli_state *self,
1212                            const char *base_dir, const char *user_mask,
1213                            uint16_t attribute,
1214                            NTSTATUS (*callback_fn)(struct file_info *,
1215                                                    const char *, void *),
1216                            void *priv)
1217 {
1218         char *mask = NULL;
1219         unsigned int info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
1220         struct do_listing_state state = {
1221                 .mask = mask,
1222                 .callback_fn = callback_fn,
1223                 .private_data = priv,
1224         };
1225         struct tevent_req *req = NULL;
1226         NTSTATUS status;
1227
1228         if (user_mask == NULL) {
1229                 mask = talloc_asprintf(NULL, "%s\\*", base_dir);
1230         } else {
1231                 mask = talloc_asprintf(NULL, "%s\\%s", base_dir, user_mask);
1232         }
1233
1234         if (mask == NULL) {
1235                 return NT_STATUS_NO_MEMORY;
1236         }
1237         dos_format(mask);
1238
1239         req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
1240                             info_level);
1241         if (req == NULL) {
1242                 status = NT_STATUS_NO_MEMORY;
1243                 goto done;
1244         }
1245         tevent_req_set_callback(req, do_listing_cb, &state);
1246
1247         if (!py_tevent_req_wait_exc(self, req)) {
1248                 return NT_STATUS_INTERNAL_ERROR;
1249         }
1250         TALLOC_FREE(req);
1251
1252         status = state.status;
1253         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
1254                 status = NT_STATUS_OK;
1255         }
1256
1257 done:
1258         TALLOC_FREE(mask);
1259         return status;
1260 }
1261
1262 static PyObject *py_cli_list(struct py_cli_state *self,
1263                              PyObject *args,
1264                              PyObject *kwds)
1265 {
1266         char *base_dir;
1267         char *user_mask = NULL;
1268         unsigned int attribute = LIST_ATTRIBUTE_MASK;
1269         NTSTATUS status;
1270         PyObject *result = NULL;
1271         const char *kwlist[] = { "directory", "mask", "attribs", NULL };
1272
1273         if (!ParseTupleAndKeywords(args, kwds, "z|sI:list", kwlist,
1274                                    &base_dir, &user_mask, &attribute)) {
1275                 return NULL;
1276         }
1277
1278         result = Py_BuildValue("[]");
1279         if (result == NULL) {
1280                 return NULL;
1281         }
1282
1283         status = do_listing(self, base_dir, user_mask, attribute,
1284                             list_helper, result);
1285
1286         if (!NT_STATUS_IS_OK(status)) {
1287                 Py_XDECREF(result);
1288                 PyErr_SetNTSTATUS(status);
1289                 return NULL;
1290         }
1291
1292         return result;
1293 }
1294
1295 static PyObject *py_smb_unlink(struct py_cli_state *self, PyObject *args)
1296 {
1297         NTSTATUS status;
1298         const char *filename = NULL;
1299         struct tevent_req *req = NULL;
1300         const uint32_t attrs = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
1301
1302         if (!PyArg_ParseTuple(args, "s:unlink", &filename)) {
1303                 return NULL;
1304         }
1305
1306         req = cli_unlink_send(NULL, self->ev, self->cli, filename, attrs);
1307         if (!py_tevent_req_wait_exc(self, req)) {
1308                 return NULL;
1309         }
1310         status = cli_unlink_recv(req);
1311         TALLOC_FREE(req);
1312         PyErr_NTSTATUS_NOT_OK_RAISE(status);
1313
1314         Py_RETURN_NONE;
1315 }
1316
1317 static PyObject *py_smb_rmdir(struct py_cli_state *self, PyObject *args)
1318 {
1319         NTSTATUS status;
1320         struct tevent_req *req = NULL;
1321         const char *dirname = NULL;
1322
1323         if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
1324                 return NULL;
1325         }
1326
1327         req = cli_rmdir_send(NULL, self->ev, self->cli, dirname);
1328         if (!py_tevent_req_wait_exc(self, req)) {
1329                 return NULL;
1330         }
1331         status = cli_rmdir_recv(req);
1332         TALLOC_FREE(req);
1333         PyErr_NTSTATUS_NOT_OK_RAISE(status);
1334
1335         Py_RETURN_NONE;
1336 }
1337
1338 /*
1339  * Create a directory
1340  */
1341 static PyObject *py_smb_mkdir(struct py_cli_state *self, PyObject *args)
1342 {
1343         NTSTATUS status;
1344         const char *dirname = NULL;
1345         struct tevent_req *req = NULL;
1346
1347         if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
1348                 return NULL;
1349         }
1350
1351         req = cli_mkdir_send(NULL, self->ev, self->cli, dirname);
1352         if (!py_tevent_req_wait_exc(self, req)) {
1353                 return NULL;
1354         }
1355         status = cli_mkdir_recv(req);
1356         TALLOC_FREE(req);
1357         PyErr_NTSTATUS_NOT_OK_RAISE(status);
1358
1359         Py_RETURN_NONE;
1360 }
1361
1362 /*
1363  * Checks existence of a directory
1364  */
1365 static bool check_dir_path(struct py_cli_state *self, const char *path)
1366 {
1367         NTSTATUS status;
1368         struct tevent_req *req = NULL;
1369
1370         req = cli_chkpath_send(NULL, self->ev, self->cli, path);
1371         if (!py_tevent_req_wait_exc(self, req)) {
1372                 return false;
1373         }
1374         status = cli_chkpath_recv(req);
1375         TALLOC_FREE(req);
1376
1377         return NT_STATUS_IS_OK(status);
1378 }
1379
1380 static PyObject *py_smb_chkpath(struct py_cli_state *self, PyObject *args)
1381 {
1382         const char *path = NULL;
1383         bool dir_exists;
1384
1385         if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
1386                 return NULL;
1387         }
1388
1389         dir_exists = check_dir_path(self, path);
1390         return PyBool_FromLong(dir_exists);
1391 }
1392
1393 static PyObject *py_smb_get_sd(struct py_cli_state *self, PyObject *args)
1394 {
1395         int fnum;
1396         unsigned sinfo;
1397         struct tevent_req *req = NULL;
1398         struct security_descriptor *sd = NULL;
1399         NTSTATUS status;
1400
1401         if (!PyArg_ParseTuple(args, "iI:get_acl", &fnum, &sinfo)) {
1402                 return NULL;
1403         }
1404
1405         req = cli_query_security_descriptor_send(
1406                 NULL, self->ev, self->cli, fnum, sinfo);
1407         if (!py_tevent_req_wait_exc(self, req)) {
1408                 return false;
1409         }
1410         status = cli_query_security_descriptor_recv(req, NULL, &sd);
1411         PyErr_NTSTATUS_NOT_OK_RAISE(status);
1412
1413         return py_return_ndr_struct(
1414                 "samba.dcerpc.security", "descriptor", sd, sd);
1415 }
1416
1417 static PyObject *py_smb_set_sd(struct py_cli_state *self, PyObject *args)
1418 {
1419         PyObject *py_sd = NULL;
1420         struct tevent_req *req = NULL;
1421         struct security_descriptor *sd = NULL;
1422         uint16_t fnum;
1423         unsigned int sinfo;
1424         NTSTATUS status;
1425
1426         if (!PyArg_ParseTuple(args, "iOI:set_sd", &fnum, &py_sd, &sinfo)) {
1427                 return NULL;
1428         }
1429
1430         sd = pytalloc_get_type(py_sd, struct security_descriptor);
1431         if (!sd) {
1432                 PyErr_Format(PyExc_TypeError,
1433                         "Expected dcerpc.security.descriptor as argument, got %s",
1434                         pytalloc_get_name(py_sd));
1435                 return NULL;
1436         }
1437
1438         req = cli_set_security_descriptor_send(
1439                 NULL, self->ev, self->cli, fnum, sinfo, sd);
1440         if (!py_tevent_req_wait_exc(self, req)) {
1441                 return false;
1442         }
1443
1444         status = cli_set_security_descriptor_recv(req);
1445         PyErr_NTSTATUS_NOT_OK_RAISE(status);
1446
1447         Py_RETURN_NONE;
1448 }
1449
1450 static PyMethodDef py_cli_state_methods[] = {
1451         { "settimeout", (PyCFunction)py_cli_settimeout, METH_VARARGS,
1452           "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
1453         { "echo", (PyCFunction)py_cli_echo, METH_NOARGS,
1454           "Ping the server connection" },
1455         { "create", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_create),
1456                 METH_VARARGS|METH_KEYWORDS,
1457           "Open a file" },
1458         { "close", (PyCFunction)py_cli_close, METH_VARARGS,
1459           "Close a file handle" },
1460         { "write", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_write),
1461                 METH_VARARGS|METH_KEYWORDS,
1462           "Write to a file handle" },
1463         { "read", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_read),
1464                 METH_VARARGS|METH_KEYWORDS,
1465           "Read from a file handle" },
1466         { "truncate", PY_DISCARD_FUNC_SIG(PyCFunction,
1467                         py_cli_ftruncate),
1468           METH_VARARGS|METH_KEYWORDS,
1469           "Truncate a file" },
1470         { "delete_on_close", PY_DISCARD_FUNC_SIG(PyCFunction,
1471                                          py_cli_delete_on_close),
1472           METH_VARARGS|METH_KEYWORDS,
1473           "Set/Reset the delete on close flag" },
1474         { "list", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_list),
1475                 METH_VARARGS|METH_KEYWORDS,
1476           "list(directory, mask='*', attribs=DEFAULT_ATTRS) -> "
1477           "directory contents as a dictionary\n"
1478           "\t\tDEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | "
1479           "FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE\n\n"
1480           "\t\tList contents of a directory. The keys are, \n"
1481           "\t\t\tname: Long name of the directory item\n"
1482           "\t\t\tshort_name: Short name of the directory item\n"
1483           "\t\t\tsize: File size in bytes\n"
1484           "\t\t\tattrib: Attributes\n"
1485           "\t\t\tmtime: Modification time\n" },
1486         { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
1487           METH_VARARGS, "Wait for an oplock break" },
1488         { "unlink", (PyCFunction)py_smb_unlink,
1489           METH_VARARGS,
1490           "unlink(path) -> None\n\n \t\tDelete a file." },
1491         { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
1492           "mkdir(path) -> None\n\n \t\tCreate a directory." },
1493         { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
1494           "rmdir(path) -> None\n\n \t\tDelete a directory." },
1495         { "rename",
1496           PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_rename),
1497           METH_VARARGS|METH_KEYWORDS,
1498           "rename(src,dst) -> None\n\n \t\tRename a file." },
1499         { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
1500           "chkpath(dir_path) -> True or False\n\n"
1501           "\t\tReturn true if directory exists, false otherwise." },
1502         { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
1503           "savefile(path, str) -> None\n\n"
1504           "\t\tWrite " PY_DESC_PY3_BYTES " str to file." },
1505         { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
1506           "loadfile(path) -> file contents as a " PY_DESC_PY3_BYTES
1507           "\n\n\t\tRead contents of a file." },
1508         { "get_sd", (PyCFunction)py_smb_get_sd, METH_VARARGS,
1509           "get_sd(fnum[, security_info=0]) -> security_descriptor object\n\n"
1510           "\t\tGet security descriptor for opened file." },
1511         { "set_sd", (PyCFunction)py_smb_set_sd, METH_VARARGS,
1512           "set_sd(fnum, security_descriptor[, security_info=0]) -> None\n\n"
1513           "\t\tSet security descriptor for opened file." },
1514         { NULL, NULL, 0, NULL }
1515 };
1516
1517 static PyTypeObject py_cli_state_type = {
1518         PyVarObject_HEAD_INIT(NULL, 0)
1519         .tp_name = "libsmb_samba_cwrapper.LibsmbCConn",
1520         .tp_basicsize = sizeof(struct py_cli_state),
1521         .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1522         .tp_doc = "libsmb cwrapper connection",
1523         .tp_new = py_cli_state_new,
1524         .tp_init = (initproc)py_cli_state_init,
1525         .tp_dealloc = (destructor)py_cli_state_dealloc,
1526         .tp_methods = py_cli_state_methods,
1527 };
1528
1529 static PyMethodDef py_libsmb_methods[] = {
1530         {0},
1531 };
1532
1533 void initlibsmb_samba_cwrapper(void);
1534
1535 static struct PyModuleDef moduledef = {
1536     PyModuleDef_HEAD_INIT,
1537     .m_name = "libsmb_samba_cwrapper",
1538     .m_doc = "libsmb wrapper",
1539     .m_size = -1,
1540     .m_methods = py_libsmb_methods,
1541 };
1542
1543 MODULE_INIT_FUNC(libsmb_samba_cwrapper)
1544 {
1545         PyObject *m = NULL;
1546
1547         talloc_stackframe();
1548
1549         m = PyModule_Create(&moduledef);
1550         if (m == NULL) {
1551                 return m;
1552         }
1553         if (PyType_Ready(&py_cli_state_type) < 0) {
1554                 return NULL;
1555         }
1556         Py_INCREF(&py_cli_state_type);
1557         PyModule_AddObject(m, "LibsmbCConn", (PyObject *)&py_cli_state_type);
1558
1559 #define ADD_FLAGS(val)  PyModule_AddObject(m, #val, PyLong_FromLong(val))
1560
1561         ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
1562         ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
1563         ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
1564         ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
1565         ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
1566         ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
1567         ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
1568         ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
1569         ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
1570         ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
1571         ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
1572         ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
1573         ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
1574         ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
1575         ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
1576         ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
1577
1578         ADD_FLAGS(FILE_SHARE_READ);
1579         ADD_FLAGS(FILE_SHARE_WRITE);
1580         ADD_FLAGS(FILE_SHARE_DELETE);
1581
1582         return m;
1583 }