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