s3/libsmb/py: match input argument types with C types
[nivanova/samba-autobuild/.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 "libcli/smb/smbXcli_base.h"
28 #include "libsmb/libsmb.h"
29 #include "libcli/security/security.h"
30 #include "system/select.h"
31 #include "source4/libcli/util/pyerrors.h"
32 #include "auth/credentials/pycredentials.h"
33 #include "trans2.h"
34 #include "libsmb/clirap.h"
35 #include "librpc/rpc/pyrpc_util.h"
36
37 #define LIST_ATTRIBUTE_MASK \
38         (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN)
39
40 #define SECINFO_DEFAULT_FLAGS \
41         (SECINFO_OWNER | SECINFO_GROUP | \
42          SECINFO_DACL | SECINFO_PROTECTED_DACL | SECINFO_UNPROTECTED_DACL | \
43          SECINFO_SACL | SECINFO_PROTECTED_SACL | SECINFO_UNPROTECTED_SACL)
44
45 static PyTypeObject *get_pytype(const char *module, const char *type)
46 {
47         PyObject *mod;
48         PyTypeObject *result;
49
50         mod = PyImport_ImportModule(module);
51         if (mod == NULL) {
52                 PyErr_Format(PyExc_RuntimeError,
53                              "Unable to import %s to check type %s",
54                              module, type);
55                 return NULL;
56         }
57         result = (PyTypeObject *)PyObject_GetAttrString(mod, type);
58         Py_DECREF(mod);
59         if (result == NULL) {
60                 PyErr_Format(PyExc_RuntimeError,
61                              "Unable to find type %s in module %s",
62                              module, type);
63                 return NULL;
64         }
65         return result;
66 }
67
68 /*
69  * We're using "const char * const *" for keywords,
70  * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
71  * inevitable warnings to just one place.
72  */
73 static int ParseTupleAndKeywords(PyObject *args, PyObject *kw,
74                                  const char *format, const char * const *keywords,
75                                  ...)
76 {
77         char **_keywords = discard_const_p(char *, keywords);
78         va_list a;
79         int ret;
80         va_start(a, keywords);
81         ret = PyArg_VaParseTupleAndKeywords(args, kw, format,
82                                             _keywords, a);
83         va_end(a);
84         return ret;
85 }
86
87 struct py_cli_thread;
88
89 struct py_cli_oplock_break {
90         uint16_t fnum;
91         uint8_t level;
92 };
93
94 struct py_cli_state {
95         PyObject_HEAD
96         struct cli_state *cli;
97         bool is_smb1;
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->is_smb1 = false;
422         self->ev = NULL;
423         self->thread_state = NULL;
424         self->oplock_waiter = NULL;
425         self->oplock_cond = NULL;
426         self->oplock_breaks = NULL;
427         return (PyObject *)self;
428 }
429
430 static void py_cli_got_oplock_break(struct tevent_req *req);
431
432 static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
433                              PyObject *kwds)
434 {
435         NTSTATUS status;
436         char *host, *share;
437         PyObject *creds = NULL;
438         struct cli_credentials *cli_creds;
439         PyObject *py_lp = Py_None;
440         PyObject *py_multi_threaded = Py_False;
441         bool multi_threaded = false;
442         PyObject *py_sign = Py_False;
443         bool sign = false;
444         int signing_state = SMB_SIGNING_DEFAULT;
445         PyObject *py_force_smb1 = Py_False;
446         bool force_smb1 = 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", "sign", "force_smb1",
454                 NULL
455         };
456
457         PyTypeObject *py_type_Credentials = get_pytype(
458                 "samba.credentials", "Credentials");
459         if (py_type_Credentials == NULL) {
460                 return -1;
461         }
462
463         ret = ParseTupleAndKeywords(
464                 args, kwds, "ssO|O!OOO", kwlist,
465                 &host, &share, &py_lp,
466                 py_type_Credentials, &creds,
467                 &py_multi_threaded,
468                 &py_sign,
469                 &py_force_smb1);
470
471         Py_DECREF(py_type_Credentials);
472
473         if (!ret) {
474                 return -1;
475         }
476
477         multi_threaded = PyObject_IsTrue(py_multi_threaded);
478         sign = PyObject_IsTrue(py_sign);
479         force_smb1 = PyObject_IsTrue(py_force_smb1);
480
481         if (sign) {
482                 signing_state = SMB_SIGNING_REQUIRED;
483         }
484
485         if (force_smb1) {
486                 /*
487                  * As most of the cli_*_send() function
488                  * don't support SMB2 (it's only plugged
489                  * into the sync wrapper functions currently)
490                  * we have a way to force SMB1.
491                  */
492                 flags = CLI_FULL_CONNECTION_FORCE_SMB1;
493         }
494
495         if (multi_threaded) {
496 #ifdef HAVE_PTHREAD
497                 ret = py_cli_state_setup_mt_ev(self);
498                 if (!ret) {
499                         return -1;
500                 }
501 #else
502                 PyErr_SetString(PyExc_RuntimeError,
503                                 "No PTHREAD support available");
504                 return -1;
505 #endif
506                 if (!force_smb1) {
507                         PyErr_SetString(PyExc_RuntimeError,
508                                         "multi_threaded is only possible on "
509                                         "SMB1 connections");
510                         return -1;
511                 }
512         } else {
513                 ret = py_cli_state_setup_ev(self);
514                 if (!ret) {
515                         return -1;
516                 }
517         }
518
519         if (creds == NULL) {
520                 cli_creds = cli_credentials_init_anon(NULL);
521         } else {
522                 cli_creds = PyCredentials_AsCliCredentials(creds);
523         }
524
525         req = cli_full_connection_creds_send(
526                 NULL, self->ev, "myname", host, NULL, 0, share, "?????",
527                 cli_creds, flags, signing_state);
528         if (!py_tevent_req_wait_exc(self, req)) {
529                 return -1;
530         }
531         status = cli_full_connection_creds_recv(req, &self->cli);
532         TALLOC_FREE(req);
533
534         if (!NT_STATUS_IS_OK(status)) {
535                 PyErr_SetNTSTATUS(status);
536                 return -1;
537         }
538
539         if (smbXcli_conn_protocol(self->cli->conn) < PROTOCOL_SMB2_02) {
540                 self->is_smb1 = true;
541         }
542
543         /*
544          * Oplocks require a multi threaded connection
545          */
546         if (self->thread_state == NULL) {
547                 return 0;
548         }
549
550         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
551                 self->ev, self->ev, self->cli);
552         if (self->oplock_waiter == NULL) {
553                 PyErr_NoMemory();
554                 return -1;
555         }
556         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
557                                 self);
558         return 0;
559 }
560
561 static void py_cli_got_oplock_break(struct tevent_req *req)
562 {
563         struct py_cli_state *self = (struct py_cli_state *)
564                 tevent_req_callback_data_void(req);
565         struct py_cli_oplock_break b;
566         struct py_cli_oplock_break *tmp;
567         size_t num_breaks;
568         NTSTATUS status;
569
570         status = cli_smb_oplock_break_waiter_recv(req, &b.fnum, &b.level);
571         TALLOC_FREE(req);
572         self->oplock_waiter = NULL;
573
574         if (!NT_STATUS_IS_OK(status)) {
575                 return;
576         }
577
578         num_breaks = talloc_array_length(self->oplock_breaks);
579         tmp = talloc_realloc(self->ev, self->oplock_breaks,
580                              struct py_cli_oplock_break, num_breaks+1);
581         if (tmp == NULL) {
582                 return;
583         }
584         self->oplock_breaks = tmp;
585         self->oplock_breaks[num_breaks] = b;
586
587         if (self->oplock_cond != NULL) {
588                 py_tevent_cond_signal(self->oplock_cond);
589         }
590
591         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
592                 self->ev, self->ev, self->cli);
593         if (self->oplock_waiter == NULL) {
594                 return;
595         }
596         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
597                                 self);
598 }
599
600 static PyObject *py_cli_get_oplock_break(struct py_cli_state *self,
601                                          PyObject *args)
602 {
603         size_t num_oplock_breaks;
604
605         if (!PyArg_ParseTuple(args, "")) {
606                 return NULL;
607         }
608
609         if (self->thread_state == NULL) {
610                 PyErr_SetString(PyExc_RuntimeError,
611                                 "get_oplock_break() only possible on "
612                                 "a multi_threaded connection");
613                 return NULL;
614         }
615
616         if (self->oplock_cond != NULL) {
617                 errno = EBUSY;
618                 PyErr_SetFromErrno(PyExc_RuntimeError);
619                 return NULL;
620         }
621
622         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
623
624         if (num_oplock_breaks == 0) {
625                 struct py_tevent_cond cond;
626                 int ret;
627
628                 self->oplock_cond = &cond;
629                 ret = py_tevent_cond_wait(&cond);
630                 self->oplock_cond = NULL;
631
632                 if (ret != 0) {
633                         errno = ret;
634                         PyErr_SetFromErrno(PyExc_RuntimeError);
635                         return NULL;
636                 }
637         }
638
639         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
640         if (num_oplock_breaks > 0) {
641                 PyObject *result;
642
643                 result = Py_BuildValue(
644                         "{s:i,s:i}",
645                         "fnum", self->oplock_breaks[0].fnum,
646                         "level", self->oplock_breaks[0].level);
647
648                 memmove(&self->oplock_breaks[0], &self->oplock_breaks[1],
649                         sizeof(self->oplock_breaks[0]) *
650                         (num_oplock_breaks - 1));
651                 self->oplock_breaks = talloc_realloc(
652                         NULL, self->oplock_breaks, struct py_cli_oplock_break,
653                         num_oplock_breaks - 1);
654
655                 return result;
656         }
657         Py_RETURN_NONE;
658 }
659
660 static void py_cli_state_dealloc(struct py_cli_state *self)
661 {
662         TALLOC_FREE(self->thread_state);
663         TALLOC_FREE(self->oplock_waiter);
664         TALLOC_FREE(self->ev);
665
666         if (self->cli != NULL) {
667                 cli_shutdown(self->cli);
668                 self->cli = NULL;
669         }
670         Py_TYPE(self)->tp_free((PyObject *)self);
671 }
672
673 static PyObject *py_cli_settimeout(struct py_cli_state *self, PyObject *args)
674 {
675         unsigned int nmsecs = 0;
676         unsigned int omsecs = 0;
677
678         if (!PyArg_ParseTuple(args, "I", &nmsecs)) {
679                 return NULL;
680         }
681
682         omsecs = cli_set_timeout(self->cli, nmsecs);
683
684         return PyInt_FromLong(omsecs);
685 }
686
687 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
688                                PyObject *kwds)
689 {
690         char *fname;
691         unsigned CreateFlags = 0;
692         unsigned DesiredAccess = FILE_GENERIC_READ;
693         unsigned FileAttributes = 0;
694         unsigned ShareAccess = 0;
695         unsigned CreateDisposition = FILE_OPEN;
696         unsigned CreateOptions = 0;
697         unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
698         unsigned SecurityFlags = 0;
699         uint16_t fnum;
700         struct tevent_req *req;
701         NTSTATUS status;
702
703         static const char *kwlist[] = {
704                 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
705                 "ShareAccess", "CreateDisposition", "CreateOptions",
706                 "ImpersonationLevel", "SecurityFlags", NULL };
707
708         if (!ParseTupleAndKeywords(
709                     args, kwds, "s|IIIIIIII", kwlist,
710                     &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
711                     &ShareAccess, &CreateDisposition, &CreateOptions,
712                     &ImpersonationLevel, &SecurityFlags)) {
713                 return NULL;
714         }
715
716         req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
717                                 DesiredAccess, FileAttributes, ShareAccess,
718                                 CreateDisposition, CreateOptions,
719                                 ImpersonationLevel, SecurityFlags);
720         if (!py_tevent_req_wait_exc(self, req)) {
721                 return NULL;
722         }
723         status = cli_ntcreate_recv(req, &fnum, NULL);
724         TALLOC_FREE(req);
725
726         if (!NT_STATUS_IS_OK(status)) {
727                 PyErr_SetNTSTATUS(status);
728                 return NULL;
729         }
730         return Py_BuildValue("I", (unsigned)fnum);
731 }
732
733 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
734 {
735         struct tevent_req *req;
736         int fnum;
737         NTSTATUS status;
738
739         if (!PyArg_ParseTuple(args, "i", &fnum)) {
740                 return NULL;
741         }
742
743         req = cli_close_send(NULL, self->ev, self->cli, fnum);
744         if (!py_tevent_req_wait_exc(self, req)) {
745                 return NULL;
746         }
747         status = cli_close_recv(req);
748         TALLOC_FREE(req);
749
750         if (!NT_STATUS_IS_OK(status)) {
751                 PyErr_SetNTSTATUS(status);
752                 return NULL;
753         }
754         Py_RETURN_NONE;
755 }
756
757 struct push_state {
758         char *data;
759         off_t nread;
760         off_t total_data;
761 };
762
763 /*
764  * cli_push() helper to write a chunk of data to a remote file
765  */
766 static size_t push_data(uint8_t *buf, size_t n, void *priv)
767 {
768         struct push_state *state = (struct push_state *)priv;
769         char *curr_ptr = NULL;
770         off_t remaining;
771         size_t copied_bytes;
772
773         if (state->nread >= state->total_data) {
774                 return 0;
775         }
776
777         curr_ptr = state->data + state->nread;
778         remaining = state->total_data - state->nread;
779         copied_bytes = MIN(remaining, n);
780
781         memcpy(buf, curr_ptr, copied_bytes);
782         state->nread += copied_bytes;
783         return copied_bytes;
784 }
785
786 /*
787  * Writes a file with the contents specified
788  */
789 static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args,
790                                  PyObject *kwargs)
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
887         if (self->is_smb1) {
888                 uint8_t *rdata = NULL;
889                 struct tevent_req *req = NULL;
890
891                 req = cli_qfileinfo_send(NULL, self->ev, self->cli, fnum,
892                                          SMB_QUERY_FILE_ALL_INFO, 68,
893                                          CLI_BUFFER_SIZE);
894                 if (!py_tevent_req_wait_exc(self, req)) {
895                         return NT_STATUS_INTERNAL_ERROR;
896                 }
897                 status = cli_qfileinfo_recv(req, NULL, NULL, &rdata, NULL);
898                 if (NT_STATUS_IS_OK(status)) {
899                         *size = IVAL2_TO_SMB_BIG_UINT(rdata, 48);
900                 }
901                 TALLOC_FREE(req);
902                 TALLOC_FREE(rdata);
903         } else {
904                 status = cli_qfileinfo_basic(self->cli, fnum, NULL, size,
905                                              NULL, NULL, NULL, NULL, NULL);
906         }
907         return status;
908 }
909
910 /*
911  * Loads the specified file's contents and returns it
912  */
913 static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args,
914                                  PyObject *kwargs)
915 {
916         NTSTATUS status;
917         const char *filename = NULL;
918         struct tevent_req *req = NULL;
919         uint16_t fnum;
920         off_t size;
921         char *buf = NULL;
922         off_t nread = 0;
923         PyObject *result = NULL;
924
925         if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
926                 return NULL;
927         }
928
929         /* get a read file handle */
930         req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
931                                 FILE_READ_DATA | FILE_READ_ATTRIBUTES,
932                                 FILE_ATTRIBUTE_NORMAL,
933                                 FILE_SHARE_READ, FILE_OPEN, 0,
934                                 SMB2_IMPERSONATION_IMPERSONATION, 0);
935         if (!py_tevent_req_wait_exc(self, req)) {
936                 return NULL;
937         }
938         status = cli_ntcreate_recv(req, &fnum, NULL);
939         TALLOC_FREE(req);
940         PyErr_NTSTATUS_IS_ERR_RAISE(status);
941
942         /* get a buffer to hold the file contents */
943         status = py_smb_filesize(self, fnum, &size);
944         PyErr_NTSTATUS_IS_ERR_RAISE(status);
945
946         result = PyBytes_FromStringAndSize(NULL, size);
947         if (result == NULL) {
948                 return NULL;
949         }
950
951         /* read the file contents */
952         buf = PyBytes_AS_STRING(result);
953         req = cli_pull_send(NULL, self->ev, self->cli, fnum, 0, size,
954                             size, cli_read_sink, &buf);
955         if (!py_tevent_req_wait_exc(self, req)) {
956                 Py_XDECREF(result);
957                 return NULL;
958         }
959         status = cli_pull_recv(req, &nread);
960         TALLOC_FREE(req);
961         if (!NT_STATUS_IS_OK(status)) {
962                 Py_XDECREF(result);
963                 PyErr_SetNTSTATUS(status);
964                 return NULL;
965         }
966
967         /* close the file handle */
968         req = cli_close_send(NULL, self->ev, self->cli, fnum);
969         if (!py_tevent_req_wait_exc(self, req)) {
970                 Py_XDECREF(result);
971                 return NULL;
972         }
973         status = cli_close_recv(req);
974         TALLOC_FREE(req);
975         if (!NT_STATUS_IS_OK(status)) {
976                 Py_XDECREF(result);
977                 PyErr_SetNTSTATUS(status);
978                 return NULL;
979         }
980
981         /* sanity-check we read the expected number of bytes */
982         if (nread > size) {
983                 Py_XDECREF(result);
984                 PyErr_Format(PyExc_IOError,
985                              "read invalid - got %zu requested %zu",
986                              nread, size);
987                 return NULL;
988         }
989
990         if (nread < size) {
991                 if (_PyBytes_Resize(&result, nread) < 0) {
992                         return NULL;
993                 }
994         }
995
996         return result;
997 }
998
999 static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
1000                              PyObject *kwds)
1001 {
1002         int fnum;
1003         unsigned long long offset;
1004         unsigned size;
1005         struct tevent_req *req;
1006         NTSTATUS status;
1007         char *buf;
1008         size_t received;
1009         PyObject *result;
1010
1011         static const char *kwlist[] = {
1012                 "fnum", "offset", "size", NULL };
1013
1014         if (!ParseTupleAndKeywords(
1015                     args, kwds, "iKI", kwlist, &fnum, &offset,
1016                     &size)) {
1017                 return NULL;
1018         }
1019
1020         result = PyBytes_FromStringAndSize(NULL, size);
1021         if (result == NULL) {
1022                 return NULL;
1023         }
1024         buf = PyBytes_AS_STRING(result);
1025
1026         req = cli_read_send(NULL, self->ev, self->cli, fnum,
1027                             buf, offset, size);
1028         if (!py_tevent_req_wait_exc(self, req)) {
1029                 Py_XDECREF(result);
1030                 return NULL;
1031         }
1032         status = cli_read_recv(req, &received);
1033         TALLOC_FREE(req);
1034
1035         if (!NT_STATUS_IS_OK(status)) {
1036                 Py_XDECREF(result);
1037                 PyErr_SetNTSTATUS(status);
1038                 return NULL;
1039         }
1040
1041         if (received > size) {
1042                 Py_XDECREF(result);
1043                 PyErr_Format(PyExc_IOError,
1044                              "read invalid - got %zu requested %u",
1045                              received, size);
1046                 return NULL;
1047         }
1048
1049         if (received < size) {
1050                 if (_PyBytes_Resize(&result, received) < 0) {
1051                         return NULL;
1052                 }
1053         }
1054
1055         return result;
1056 }
1057
1058 static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args,
1059                                   PyObject *kwds)
1060 {
1061         int fnum;
1062         unsigned long long size;
1063         struct tevent_req *req;
1064         NTSTATUS status;
1065
1066         static const char *kwlist[] = {
1067                 "fnum", "size", NULL };
1068
1069         if (!ParseTupleAndKeywords(
1070                     args, kwds, "IK", kwlist, &fnum, &size)) {
1071                 return NULL;
1072         }
1073
1074         req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size);
1075         if (!py_tevent_req_wait_exc(self, req)) {
1076                 return NULL;
1077         }
1078         status = cli_ftruncate_recv(req);
1079         TALLOC_FREE(req);
1080
1081         if (!NT_STATUS_IS_OK(status)) {
1082                 PyErr_SetNTSTATUS(status);
1083                 return NULL;
1084         }
1085         Py_RETURN_NONE;
1086 }
1087
1088 static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
1089                                         PyObject *args,
1090                                         PyObject *kwds)
1091 {
1092         unsigned fnum, flag;
1093         struct tevent_req *req;
1094         NTSTATUS status;
1095
1096         static const char *kwlist[] = {
1097                 "fnum", "flag", NULL };
1098
1099         if (!ParseTupleAndKeywords(
1100                     args, kwds, "II", kwlist, &fnum, &flag)) {
1101                 return NULL;
1102         }
1103
1104         req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum,
1105                                           flag);
1106         if (!py_tevent_req_wait_exc(self, req)) {
1107                 return NULL;
1108         }
1109         status = cli_nt_delete_on_close_recv(req);
1110         TALLOC_FREE(req);
1111
1112         if (!NT_STATUS_IS_OK(status)) {
1113                 PyErr_SetNTSTATUS(status);
1114                 return NULL;
1115         }
1116         Py_RETURN_NONE;
1117 }
1118
1119 /*
1120  * Helper to add directory listing entries to an overall Python list
1121  */
1122 static NTSTATUS list_helper(const char *mntpoint, struct file_info *finfo,
1123                             const char *mask, void *state)
1124 {
1125         PyObject *result = (PyObject *)state;
1126         PyObject *file = NULL;
1127         PyObject *size = NULL;
1128         int ret;
1129
1130         /* suppress '.' and '..' in the results we return */
1131         if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1132                 return NT_STATUS_OK;
1133         }
1134         size = PyLong_FromUnsignedLongLong(finfo->size);
1135         /*
1136          * Build a dictionary representing the file info.
1137          * Note: Windows does not always return short_name (so it may be None)
1138          */
1139         file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}",
1140                              "name", finfo->name,
1141                              "attrib", (int)finfo->mode,
1142                              "short_name", finfo->short_name,
1143                              "size", size,
1144                              "mtime",
1145                              convert_timespec_to_time_t(finfo->mtime_ts));
1146
1147         Py_CLEAR(size);
1148
1149         if (file == NULL) {
1150                 return NT_STATUS_NO_MEMORY;
1151         }
1152
1153         ret = PyList_Append(result, file);
1154         Py_CLEAR(file);
1155         if (ret == -1) {
1156                 return NT_STATUS_INTERNAL_ERROR;
1157         }
1158
1159         return NT_STATUS_OK;
1160 }
1161
1162 static NTSTATUS do_listing(struct py_cli_state *self,
1163                            const char *base_dir, const char *user_mask,
1164                            uint16_t attribute,
1165                            NTSTATUS (*callback_fn)(const char *,
1166                                                    struct file_info *,
1167                                                    const char *, void *),
1168                            void *priv)
1169 {
1170         char *mask = NULL;
1171         unsigned int info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
1172         struct file_info *finfos = NULL;
1173         size_t i;
1174         size_t num_finfos = 0;
1175         NTSTATUS status;
1176
1177         if (user_mask == NULL) {
1178                 mask = talloc_asprintf(NULL, "%s\\*", base_dir);
1179         } else {
1180                 mask = talloc_asprintf(NULL, "%s\\%s", base_dir, user_mask);
1181         }
1182
1183         if (mask == NULL) {
1184                 return NT_STATUS_NO_MEMORY;
1185         }
1186         dos_format(mask);
1187
1188         if (self->is_smb1) {
1189                 struct tevent_req *req = NULL;
1190
1191                 req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
1192                                     info_level);
1193                 if (!py_tevent_req_wait_exc(self, req)) {
1194                         return NT_STATUS_INTERNAL_ERROR;
1195                 }
1196                 status = cli_list_recv(req, NULL, &finfos, &num_finfos);
1197                 TALLOC_FREE(req);
1198         } else {
1199                 status = cli_list(self->cli, mask, attribute, callback_fn,
1200                                   priv);
1201         }
1202         TALLOC_FREE(mask);
1203
1204         if (!NT_STATUS_IS_OK(status)) {
1205                 return status;
1206         }
1207
1208         /* invoke the callback for the async results (SMBv1 connections) */
1209         for (i = 0; i < num_finfos; i++) {
1210                 status = callback_fn(base_dir, &finfos[i], user_mask,
1211                                      priv);
1212                 if (!NT_STATUS_IS_OK(status)) {
1213                         TALLOC_FREE(finfos);
1214                         return status;
1215                 }
1216         }
1217
1218         TALLOC_FREE(finfos);
1219         return status;
1220 }
1221
1222 static PyObject *py_cli_list(struct py_cli_state *self,
1223                              PyObject *args,
1224                              PyObject *kwds)
1225 {
1226         char *base_dir;
1227         char *user_mask = NULL;
1228         unsigned int attribute = LIST_ATTRIBUTE_MASK;
1229         NTSTATUS status;
1230         PyObject *result = NULL;
1231         const char *kwlist[] = { "directory", "mask", "attribs", NULL };
1232
1233         if (!ParseTupleAndKeywords(args, kwds, "z|sI:list", kwlist,
1234                                    &base_dir, &user_mask, &attribute)) {
1235                 return NULL;
1236         }
1237
1238         result = Py_BuildValue("[]");
1239         if (result == NULL) {
1240                 return NULL;
1241         }
1242
1243         status = do_listing(self, base_dir, user_mask, attribute,
1244                             list_helper, result);
1245
1246         if (!NT_STATUS_IS_OK(status)) {
1247                 Py_XDECREF(result);
1248                 PyErr_SetNTSTATUS(status);
1249                 return NULL;
1250         }
1251
1252         return result;
1253 }
1254
1255 /*
1256  * Deletes a file
1257  */
1258 static NTSTATUS unlink_file(struct py_cli_state *self, const char *filename)
1259 {
1260         NTSTATUS status;
1261         uint16_t attrs = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
1262
1263         if (self->is_smb1) {
1264                 struct tevent_req *req = NULL;
1265
1266                 req = cli_unlink_send(NULL, self->ev, self->cli, filename,
1267                                       attrs);
1268                 if (!py_tevent_req_wait_exc(self, req)) {
1269                         return NT_STATUS_INTERNAL_ERROR;
1270                 }
1271                 status = cli_unlink_recv(req);
1272                 TALLOC_FREE(req);
1273         } else {
1274                 status = cli_unlink(self->cli, filename, attrs);
1275         }
1276
1277         return status;
1278 }
1279
1280 static PyObject *py_smb_unlink(struct py_cli_state *self, PyObject *args)
1281 {
1282         NTSTATUS status;
1283         const char *filename = NULL;
1284
1285         if (!PyArg_ParseTuple(args, "s:unlink", &filename)) {
1286                 return NULL;
1287         }
1288
1289         status = unlink_file(self, filename);
1290         PyErr_NTSTATUS_NOT_OK_RAISE(status);
1291
1292         Py_RETURN_NONE;
1293 }
1294
1295 /*
1296  * Delete an empty directory
1297  */
1298 static NTSTATUS remove_dir(struct py_cli_state *self, const char *dirname)
1299 {
1300         NTSTATUS status;
1301
1302         if (self->is_smb1) {
1303                 struct tevent_req *req = NULL;
1304
1305                 req = cli_rmdir_send(NULL, self->ev, self->cli, dirname);
1306                 if (!py_tevent_req_wait_exc(self, req)) {
1307                         return NT_STATUS_INTERNAL_ERROR;
1308                 }
1309                 status = cli_rmdir_recv(req);
1310                 TALLOC_FREE(req);
1311         } else {
1312                 status = cli_rmdir(self->cli, dirname);
1313         }
1314         return status;
1315 }
1316
1317 static PyObject *py_smb_rmdir(struct py_cli_state *self, PyObject *args)
1318 {
1319         NTSTATUS status;
1320         const char *dirname = NULL;
1321
1322         if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
1323                 return NULL;
1324         }
1325
1326         status = remove_dir(self, dirname);
1327         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1328
1329         Py_RETURN_NONE;
1330 }
1331
1332 /*
1333  * Create a directory
1334  */
1335 static PyObject *py_smb_mkdir(struct py_cli_state *self, PyObject *args)
1336 {
1337         NTSTATUS status;
1338         const char *dirname = NULL;
1339
1340         if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
1341                 return NULL;
1342         }
1343
1344         if (self->is_smb1) {
1345                 struct tevent_req *req = NULL;
1346
1347                 req = cli_mkdir_send(NULL, self->ev, self->cli, dirname);
1348                 if (!py_tevent_req_wait_exc(self, req)) {
1349                         return NULL;
1350                 }
1351                 status = cli_mkdir_recv(req);
1352                 TALLOC_FREE(req);
1353         } else {
1354                 status = cli_mkdir(self->cli, dirname);
1355         }
1356         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1357
1358         Py_RETURN_NONE;
1359 }
1360
1361 /*
1362  * Checks existence of a directory
1363  */
1364 static bool check_dir_path(struct py_cli_state *self, const char *path)
1365 {
1366         NTSTATUS status;
1367
1368         if (self->is_smb1) {
1369                 struct tevent_req *req = NULL;
1370
1371                 req = cli_chkpath_send(NULL, self->ev, self->cli, path);
1372                 if (!py_tevent_req_wait_exc(self, req)) {
1373                         return false;
1374                 }
1375                 status = cli_chkpath_recv(req);
1376                 TALLOC_FREE(req);
1377         } else {
1378                 status = cli_chkpath(self->cli, path);
1379         }
1380
1381         return NT_STATUS_IS_OK(status);
1382 }
1383
1384 static PyObject *py_smb_chkpath(struct py_cli_state *self, PyObject *args)
1385 {
1386         const char *path = NULL;
1387         bool dir_exists;
1388
1389         if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
1390                 return NULL;
1391         }
1392
1393         dir_exists = check_dir_path(self, path);
1394         return PyBool_FromLong(dir_exists);
1395 }
1396
1397 struct deltree_state {
1398         struct py_cli_state *self;
1399         const char *full_dirpath;
1400 };
1401
1402 static NTSTATUS delete_dir_tree(struct py_cli_state *self,
1403                                 const char *dirpath);
1404
1405 /*
1406  * Deletes a single item in the directory tree. This could be either a file
1407  * or a directory. This function gets invoked as a callback for every item in
1408  * the given directory's listings.
1409  */
1410 static NTSTATUS delete_tree_callback(const char *mntpoint,
1411                                      struct file_info *finfo,
1412                                      const char *mask, void *priv)
1413 {
1414         char *filepath = NULL;
1415         struct deltree_state *state = priv;
1416         NTSTATUS status;
1417
1418         /* skip '.' or '..' directory listings */
1419         if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1420                 return NT_STATUS_OK;
1421         }
1422
1423         /* get the absolute filepath */
1424         filepath = talloc_asprintf(NULL, "%s\\%s", state->full_dirpath,
1425                                    finfo->name);
1426         if (filepath == NULL) {
1427                 return NT_STATUS_NO_MEMORY;
1428         }
1429
1430         if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) {
1431
1432                 /* recursively delete the sub-directory and its contents */
1433                 status = delete_dir_tree(state->self, filepath);
1434         } else {
1435                 status = unlink_file(state->self, filepath);
1436         }
1437
1438         TALLOC_FREE(filepath);
1439         return status;
1440 }
1441
1442 /*
1443  * Removes a directory and all its contents
1444  */
1445 static NTSTATUS delete_dir_tree(struct py_cli_state *self,
1446                                 const char *filepath)
1447 {
1448         NTSTATUS status;
1449         const char *mask = "*";
1450         struct deltree_state state = { 0 };
1451
1452         /* go through the directory's contents, deleting each item */
1453         state.self = self;
1454         state.full_dirpath = filepath;
1455         status = do_listing(self, filepath, mask, LIST_ATTRIBUTE_MASK,
1456                             delete_tree_callback, &state);
1457
1458         /* remove the directory itself */
1459         if (NT_STATUS_IS_OK(status)) {
1460                 status = remove_dir(self, filepath);
1461         }
1462         return status;
1463 }
1464
1465 static PyObject *py_smb_deltree(struct py_cli_state *self, PyObject *args)
1466 {
1467         NTSTATUS status;
1468         const char *filepath = NULL;
1469         bool dir_exists;
1470
1471         if (!PyArg_ParseTuple(args, "s:deltree", &filepath)) {
1472                 return NULL;
1473         }
1474
1475         /* check whether we're removing a directory or a file */
1476         dir_exists = check_dir_path(self, filepath);
1477
1478         if (dir_exists) {
1479                 status = delete_dir_tree(self, filepath);
1480         } else {
1481                 status = unlink_file(self, filepath);
1482         }
1483
1484         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1485
1486         Py_RETURN_NONE;
1487 }
1488
1489 /*
1490  * Read ACL on a given file/directory as a security descriptor object
1491  */
1492 static PyObject *py_smb_getacl(struct py_cli_state *self, PyObject *args)
1493 {
1494         NTSTATUS status;
1495         const char *filename = NULL;
1496         unsigned int sinfo = SECINFO_DEFAULT_FLAGS;
1497         unsigned int access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
1498         uint16_t fnum;
1499         struct security_descriptor *sd = NULL;
1500
1501         /* there's no async version of cli_query_security_descriptor() */
1502         if (self->thread_state != NULL) {
1503                 PyErr_SetString(PyExc_RuntimeError,
1504                                 "get_acl() is not supported on "
1505                                 "a multi_threaded connection");
1506                 return NULL;
1507         }
1508
1509         if (!PyArg_ParseTuple(args, "s|II:get_acl", &filename, &sinfo,
1510                               &access_mask)) {
1511                 return NULL;
1512         }
1513
1514         /* get a file handle with the desired access */
1515         status = cli_ntcreate(self->cli, filename, 0, access_mask, 0,
1516                               FILE_SHARE_READ|FILE_SHARE_WRITE,
1517                               FILE_OPEN, 0x0, 0x0, &fnum, NULL);
1518         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1519
1520         /* query the security descriptor for this file */
1521         status = cli_query_security_descriptor(self->cli, fnum, sinfo,
1522                                                NULL, &sd);
1523         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1524
1525         /* close the file handle and convert the SD to a python struct */
1526         status = cli_close(self->cli, fnum);
1527         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1528
1529         return py_return_ndr_struct("samba.dcerpc.security", "descriptor",
1530                                     sd, sd);
1531 }
1532
1533 /*
1534  * Set ACL on file/directory using given security descriptor object
1535  */
1536 static PyObject *py_smb_setacl(struct py_cli_state *self, PyObject *args)
1537 {
1538         NTSTATUS status;
1539         char *filename = NULL;
1540         PyObject *py_sd = NULL;
1541         struct security_descriptor *sd = NULL;
1542         unsigned int sinfo = SECINFO_DEFAULT_FLAGS;
1543         uint16_t fnum;
1544
1545         /* there's no async version of cli_set_security_descriptor() */
1546         if (self->thread_state != NULL) {
1547                 PyErr_SetString(PyExc_RuntimeError,
1548                                 "set_acl() is not supported on "
1549                                 "a multi_threaded connection");
1550                 return NULL;
1551         }
1552
1553         if (!PyArg_ParseTuple(args, "sO|I:set_acl", &filename, &py_sd,
1554                               &sinfo)) {
1555                 return NULL;
1556         }
1557
1558         sd = pytalloc_get_type(py_sd, struct security_descriptor);
1559         if (!sd) {
1560                 PyErr_Format(PyExc_TypeError,
1561                         "Expected dcerpc.security.descriptor as argument, got %s",
1562                         talloc_get_name(pytalloc_get_ptr(py_sd)));
1563                 return NULL;
1564         }
1565
1566         status = cli_ntcreate(self->cli, filename, 0,
1567                               SEC_FLAG_MAXIMUM_ALLOWED, 0,
1568                               FILE_SHARE_READ|FILE_SHARE_WRITE,
1569                               FILE_OPEN, 0x0, 0x0, &fnum, NULL);
1570         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1571
1572         status = cli_set_security_descriptor(self->cli, fnum, sinfo, sd);
1573         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1574
1575         status = cli_close(self->cli, fnum);
1576         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1577
1578         Py_RETURN_NONE;
1579 }
1580
1581 static PyMethodDef py_cli_state_methods[] = {
1582         { "settimeout", (PyCFunction)py_cli_settimeout, METH_VARARGS,
1583           "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
1584         { "create", (PyCFunction)py_cli_create, METH_VARARGS|METH_KEYWORDS,
1585           "Open a file" },
1586         { "close", (PyCFunction)py_cli_close, METH_VARARGS,
1587           "Close a file handle" },
1588         { "write", (PyCFunction)py_cli_write, METH_VARARGS|METH_KEYWORDS,
1589           "Write to a file handle" },
1590         { "read", (PyCFunction)py_cli_read, METH_VARARGS|METH_KEYWORDS,
1591           "Read from a file handle" },
1592         { "truncate", (PyCFunction)py_cli_ftruncate,
1593           METH_VARARGS|METH_KEYWORDS,
1594           "Truncate a file" },
1595         { "delete_on_close", (PyCFunction)py_cli_delete_on_close,
1596           METH_VARARGS|METH_KEYWORDS,
1597           "Set/Reset the delete on close flag" },
1598         { "list", (PyCFunction)py_cli_list, METH_VARARGS|METH_KEYWORDS,
1599           "list(directory, mask='*', attribs=DEFAULT_ATTRS) -> "
1600           "directory contents as a dictionary\n"
1601           "\t\tDEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | "
1602           "FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE\n\n"
1603           "\t\tList contents of a directory. The keys are, \n"
1604           "\t\t\tname: Long name of the directory item\n"
1605           "\t\t\tshort_name: Short name of the directory item\n"
1606           "\t\t\tsize: File size in bytes\n"
1607           "\t\t\tattrib: Attributes\n"
1608           "\t\t\tmtime: Modification time\n" },
1609         { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
1610           METH_VARARGS, "Wait for an oplock break" },
1611         { "unlink", (PyCFunction)py_smb_unlink,
1612           METH_VARARGS,
1613           "unlink(path) -> None\n\n \t\tDelete a file." },
1614         { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
1615           "mkdir(path) -> None\n\n \t\tCreate a directory." },
1616         { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
1617           "rmdir(path) -> None\n\n \t\tDelete a directory." },
1618         { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
1619           "chkpath(dir_path) -> True or False\n\n"
1620           "\t\tReturn true if directory exists, false otherwise." },
1621         { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
1622           "savefile(path, str) -> None\n\n"
1623           "\t\tWrite " PY_DESC_PY3_BYTES " str to file." },
1624         { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
1625           "loadfile(path) -> file contents as a " PY_DESC_PY3_BYTES
1626           "\n\n\t\tRead contents of a file." },
1627         { "deltree", (PyCFunction)py_smb_deltree, METH_VARARGS,
1628           "deltree(path) -> None\n\n"
1629           "\t\tDelete a directory and all its contents." },
1630         { "get_acl", (PyCFunction)py_smb_getacl, METH_VARARGS,
1631           "get_acl(path[, security_info=0]) -> security_descriptor object\n\n"
1632           "\t\tGet security descriptor for file." },
1633         { "set_acl", (PyCFunction)py_smb_setacl, METH_VARARGS,
1634           "set_acl(path, security_descriptor[, security_info=0]) -> None\n\n"
1635           "\t\tSet security descriptor for file." },
1636         { NULL, NULL, 0, NULL }
1637 };
1638
1639 static PyTypeObject py_cli_state_type = {
1640         PyVarObject_HEAD_INIT(NULL, 0)
1641         .tp_name = "libsmb_samba_internal.Conn",
1642         .tp_basicsize = sizeof(struct py_cli_state),
1643         .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1644         .tp_doc = "libsmb connection",
1645         .tp_new = py_cli_state_new,
1646         .tp_init = (initproc)py_cli_state_init,
1647         .tp_dealloc = (destructor)py_cli_state_dealloc,
1648         .tp_methods = py_cli_state_methods,
1649 };
1650
1651 static PyMethodDef py_libsmb_methods[] = {
1652         { NULL },
1653 };
1654
1655 void initlibsmb_samba_internal(void);
1656
1657 static struct PyModuleDef moduledef = {
1658     PyModuleDef_HEAD_INIT,
1659     .m_name = "libsmb_samba_internal",
1660     .m_doc = "libsmb wrapper",
1661     .m_size = -1,
1662     .m_methods = py_libsmb_methods,
1663 };
1664
1665 MODULE_INIT_FUNC(libsmb_samba_internal)
1666 {
1667         PyObject *m = NULL;
1668
1669         talloc_stackframe();
1670
1671         m = PyModule_Create(&moduledef);
1672         if (m == NULL) {
1673                 return m;
1674         }
1675         if (PyType_Ready(&py_cli_state_type) < 0) {
1676                 return NULL;
1677         }
1678         Py_INCREF(&py_cli_state_type);
1679         PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type);
1680
1681 #define ADD_FLAGS(val)  PyModule_AddObject(m, #val, PyInt_FromLong(val))
1682
1683         ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
1684         ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
1685         ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
1686         ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
1687         ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
1688         ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
1689         ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
1690         ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
1691         ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
1692         ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
1693         ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
1694         ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
1695         ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
1696         ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
1697         ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
1698         ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
1699
1700         return m;
1701 }