s3:libsmb: Avoid duplicated code by making cli_read_sink() public
[metze/samba/wip.git] / source3 / libsmb / pylibsmb.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Samba-internal work in progress Python binding for libsmbclient
4  *
5  * Copyright (C) Volker Lendecke 2012
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <Python.h>
22 #include "includes.h"
23 #include "python/py3compat.h"
24 #include "libcli/smb/smbXcli_base.h"
25 #include "libsmb/libsmb.h"
26 #include "libcli/security/security.h"
27 #include "system/select.h"
28 #include "source4/libcli/util/pyerrors.h"
29 #include "auth/credentials/pycredentials.h"
30 #include "trans2.h"
31 #include "libsmb/clirap.h"
32
33 static PyTypeObject *get_pytype(const char *module, const char *type)
34 {
35         PyObject *mod;
36         PyTypeObject *result;
37
38         mod = PyImport_ImportModule(module);
39         if (mod == NULL) {
40                 PyErr_Format(PyExc_RuntimeError,
41                              "Unable to import %s to check type %s",
42                              module, type);
43                 return NULL;
44         }
45         result = (PyTypeObject *)PyObject_GetAttrString(mod, type);
46         Py_DECREF(mod);
47         if (result == NULL) {
48                 PyErr_Format(PyExc_RuntimeError,
49                              "Unable to find type %s in module %s",
50                              module, type);
51                 return NULL;
52         }
53         return result;
54 }
55
56 /*
57  * We're using "const char * const *" for keywords,
58  * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
59  * inevitable warnings to just one place.
60  */
61 static int ParseTupleAndKeywords(PyObject *args, PyObject *kw,
62                                  const char *format, const char * const *keywords,
63                                  ...)
64 {
65         char **_keywords = discard_const_p(char *, keywords);
66         va_list a;
67         int ret;
68         va_start(a, keywords);
69         ret = PyArg_VaParseTupleAndKeywords(args, kw, format,
70                                             _keywords, a);
71         va_end(a);
72         return ret;
73 }
74
75 struct py_cli_thread;
76
77 struct py_cli_oplock_break {
78         uint16_t fnum;
79         uint8_t level;
80 };
81
82 struct py_cli_state {
83         PyObject_HEAD
84         struct cli_state *cli;
85         bool is_smb1;
86         struct tevent_context *ev;
87         int (*req_wait_fn)(struct tevent_context *ev,
88                            struct tevent_req *req);
89         struct py_cli_thread *thread_state;
90
91         struct tevent_req *oplock_waiter;
92         struct py_cli_oplock_break *oplock_breaks;
93         struct py_tevent_cond *oplock_cond;
94 };
95
96 #ifdef HAVE_PTHREAD
97
98 #include <pthread.h>
99
100 struct py_cli_thread {
101
102         /*
103          * Pipe to make the poll thread wake up in our destructor, so
104          * that we can exit and join the thread.
105          */
106         int shutdown_pipe[2];
107         struct tevent_fd *shutdown_fde;
108         bool do_shutdown;
109         pthread_t id;
110
111         /*
112          * Thread state to release the GIL during the poll(2) syscall
113          */
114         PyThreadState *py_threadstate;
115 };
116
117 static void *py_cli_state_poll_thread(void *private_data)
118 {
119         struct py_cli_state *self = (struct py_cli_state *)private_data;
120         struct py_cli_thread *t = self->thread_state;
121         PyGILState_STATE gstate;
122
123         gstate = PyGILState_Ensure();
124
125         while (!t->do_shutdown) {
126                 int ret;
127                 ret = tevent_loop_once(self->ev);
128                 assert(ret == 0);
129         }
130         PyGILState_Release(gstate);
131         return NULL;
132 }
133
134 static void py_cli_state_trace_callback(enum tevent_trace_point point,
135                                         void *private_data)
136 {
137         struct py_cli_state *self = (struct py_cli_state *)private_data;
138         struct py_cli_thread *t = self->thread_state;
139
140         switch(point) {
141         case TEVENT_TRACE_BEFORE_WAIT:
142                 assert(t->py_threadstate == NULL);
143                 t->py_threadstate = PyEval_SaveThread();
144                 break;
145         case TEVENT_TRACE_AFTER_WAIT:
146                 assert(t->py_threadstate != NULL);
147                 PyEval_RestoreThread(t->py_threadstate);
148                 t->py_threadstate = NULL;
149                 break;
150         default:
151                 break;
152         }
153 }
154
155 static void py_cli_state_shutdown_handler(struct tevent_context *ev,
156                                           struct tevent_fd *fde,
157                                           uint16_t flags,
158                                           void *private_data)
159 {
160         struct py_cli_state *self = (struct py_cli_state *)private_data;
161         struct py_cli_thread *t = self->thread_state;
162
163         if ((flags & TEVENT_FD_READ) == 0) {
164                 return;
165         }
166         TALLOC_FREE(t->shutdown_fde);
167         t->do_shutdown = true;
168 }
169
170 static int py_cli_thread_destructor(struct py_cli_thread *t)
171 {
172         char c = 0;
173         ssize_t written;
174         int ret;
175
176         do {
177                 /*
178                  * This will wake the poll thread from the poll(2)
179                  */
180                 written = write(t->shutdown_pipe[1], &c, 1);
181         } while ((written == -1) && (errno == EINTR));
182
183         /*
184          * Allow the poll thread to do its own cleanup under the GIL
185          */
186         Py_BEGIN_ALLOW_THREADS
187         ret = pthread_join(t->id, NULL);
188         Py_END_ALLOW_THREADS
189         assert(ret == 0);
190
191         if (t->shutdown_pipe[0] != -1) {
192                 close(t->shutdown_pipe[0]);
193                 t->shutdown_pipe[0] = -1;
194         }
195         if (t->shutdown_pipe[1] != -1) {
196                 close(t->shutdown_pipe[1]);
197                 t->shutdown_pipe[1] = -1;
198         }
199         return 0;
200 }
201
202 static int py_tevent_cond_req_wait(struct tevent_context *ev,
203                                    struct tevent_req *req);
204
205 static bool py_cli_state_setup_mt_ev(struct py_cli_state *self)
206 {
207         struct py_cli_thread *t = NULL;
208         int ret;
209
210         self->ev = tevent_context_init_byname(NULL, "poll_mt");
211         if (self->ev == NULL) {
212                 goto fail;
213         }
214         samba_tevent_set_debug(self->ev, "pylibsmb_tevent_mt");
215         tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self);
216
217         self->req_wait_fn = py_tevent_cond_req_wait;
218
219         self->thread_state = talloc_zero(NULL, struct py_cli_thread);
220         if (self->thread_state == NULL) {
221                 goto fail;
222         }
223         t = self->thread_state;
224
225         ret = pipe(t->shutdown_pipe);
226         if (ret == -1) {
227                 goto fail;
228         }
229         t->shutdown_fde = tevent_add_fd(
230                 self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ,
231                 py_cli_state_shutdown_handler, self);
232         if (t->shutdown_fde == NULL) {
233                 goto fail;
234         }
235
236         PyEval_InitThreads();
237
238         ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self);
239         if (ret != 0) {
240                 goto fail;
241         }
242         talloc_set_destructor(self->thread_state, py_cli_thread_destructor);
243         return true;
244
245 fail:
246         if (t != NULL) {
247                 TALLOC_FREE(t->shutdown_fde);
248
249                 if (t->shutdown_pipe[0] != -1) {
250                         close(t->shutdown_pipe[0]);
251                         t->shutdown_pipe[0] = -1;
252                 }
253                 if (t->shutdown_pipe[1] != -1) {
254                         close(t->shutdown_pipe[1]);
255                         t->shutdown_pipe[1] = -1;
256                 }
257         }
258
259         TALLOC_FREE(self->thread_state);
260         TALLOC_FREE(self->ev);
261         return false;
262 }
263
264 struct py_tevent_cond {
265         pthread_mutex_t mutex;
266         pthread_cond_t cond;
267         bool is_done;
268 };
269
270 static void py_tevent_signalme(struct tevent_req *req);
271
272 static int py_tevent_cond_wait(struct py_tevent_cond *cond)
273 {
274         int ret, result;
275
276         result = pthread_mutex_init(&cond->mutex, NULL);
277         if (result != 0) {
278                 goto fail;
279         }
280         result = pthread_cond_init(&cond->cond, NULL);
281         if (result != 0) {
282                 goto fail_mutex;
283         }
284
285         result = pthread_mutex_lock(&cond->mutex);
286         if (result != 0) {
287                 goto fail_cond;
288         }
289
290         cond->is_done = false;
291
292         while (!cond->is_done) {
293
294                 Py_BEGIN_ALLOW_THREADS
295                 result = pthread_cond_wait(&cond->cond, &cond->mutex);
296                 Py_END_ALLOW_THREADS
297
298                 if (result != 0) {
299                         goto fail_unlock;
300                 }
301         }
302
303 fail_unlock:
304         ret = pthread_mutex_unlock(&cond->mutex);
305         assert(ret == 0);
306 fail_cond:
307         ret = pthread_cond_destroy(&cond->cond);
308         assert(ret == 0);
309 fail_mutex:
310         ret = pthread_mutex_destroy(&cond->mutex);
311         assert(ret == 0);
312 fail:
313         return result;
314 }
315
316 static int py_tevent_cond_req_wait(struct tevent_context *ev,
317                                    struct tevent_req *req)
318 {
319         struct py_tevent_cond cond;
320         tevent_req_set_callback(req, py_tevent_signalme, &cond);
321         return py_tevent_cond_wait(&cond);
322 }
323
324 static void py_tevent_cond_signal(struct py_tevent_cond *cond)
325 {
326         int ret;
327
328         ret = pthread_mutex_lock(&cond->mutex);
329         assert(ret == 0);
330
331         cond->is_done = true;
332
333         ret = pthread_cond_signal(&cond->cond);
334         assert(ret == 0);
335         ret = pthread_mutex_unlock(&cond->mutex);
336         assert(ret == 0);
337 }
338
339 static void py_tevent_signalme(struct tevent_req *req)
340 {
341         struct py_tevent_cond *cond = (struct py_tevent_cond *)
342                 tevent_req_callback_data_void(req);
343
344         py_tevent_cond_signal(cond);
345 }
346
347 #endif
348
349 static int py_tevent_req_wait(struct tevent_context *ev,
350                               struct tevent_req *req);
351
352 static bool py_cli_state_setup_ev(struct py_cli_state *self)
353 {
354         self->ev = tevent_context_init(NULL);
355         if (self->ev == NULL) {
356                 return false;
357         }
358
359         samba_tevent_set_debug(self->ev, "pylibsmb_tevent");
360
361         self->req_wait_fn = py_tevent_req_wait;
362
363         return true;
364 }
365
366 static int py_tevent_req_wait(struct tevent_context *ev,
367                               struct tevent_req *req)
368 {
369         while (tevent_req_is_in_progress(req)) {
370                 int ret;
371
372                 ret = tevent_loop_once(ev);
373                 if (ret != 0) {
374                         return ret;
375                 }
376         }
377         return 0;
378 }
379
380 static bool py_tevent_req_wait_exc(struct py_cli_state *self,
381                                    struct tevent_req *req)
382 {
383         int ret;
384
385         if (req == NULL) {
386                 PyErr_NoMemory();
387                 return false;
388         }
389         ret = self->req_wait_fn(self->ev, req);
390         if (ret != 0) {
391                 TALLOC_FREE(req);
392                 errno = ret;
393                 PyErr_SetFromErrno(PyExc_RuntimeError);
394                 return false;
395         }
396         return true;
397 }
398
399 static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
400                                   PyObject *kwds)
401 {
402         struct py_cli_state *self;
403
404         self = (struct py_cli_state *)type->tp_alloc(type, 0);
405         if (self == NULL) {
406                 return NULL;
407         }
408         self->cli = NULL;
409         self->is_smb1 = false;
410         self->ev = NULL;
411         self->thread_state = NULL;
412         self->oplock_waiter = NULL;
413         self->oplock_cond = NULL;
414         self->oplock_breaks = NULL;
415         return (PyObject *)self;
416 }
417
418 static void py_cli_got_oplock_break(struct tevent_req *req);
419
420 static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
421                              PyObject *kwds)
422 {
423         NTSTATUS status;
424         char *host, *share;
425         PyObject *creds = NULL;
426         struct cli_credentials *cli_creds;
427         PyObject *py_lp = Py_None;
428         PyObject *py_multi_threaded = Py_False;
429         bool multi_threaded = false;
430         PyObject *py_sign = Py_False;
431         bool sign = false;
432         int signing_state = SMB_SIGNING_DEFAULT;
433         PyObject *py_force_smb1 = Py_False;
434         bool force_smb1 = false;
435         struct tevent_req *req;
436         bool ret;
437         int flags = 0;
438
439         static const char *kwlist[] = {
440                 "host", "share", "lp", "credentials",
441                 "multi_threaded", "sign", "force_smb1",
442                 NULL
443         };
444
445         PyTypeObject *py_type_Credentials = get_pytype(
446                 "samba.credentials", "Credentials");
447         if (py_type_Credentials == NULL) {
448                 return -1;
449         }
450
451         ret = ParseTupleAndKeywords(
452                 args, kwds, "ssO|O!OOO", kwlist,
453                 &host, &share, &py_lp,
454                 py_type_Credentials, &creds,
455                 &py_multi_threaded,
456                 &py_sign,
457                 &py_force_smb1);
458
459         Py_DECREF(py_type_Credentials);
460
461         if (!ret) {
462                 return -1;
463         }
464
465         multi_threaded = PyObject_IsTrue(py_multi_threaded);
466         sign = PyObject_IsTrue(py_sign);
467         force_smb1 = PyObject_IsTrue(py_force_smb1);
468
469         if (sign) {
470                 signing_state = SMB_SIGNING_REQUIRED;
471         }
472
473         if (force_smb1) {
474                 /*
475                  * As most of the cli_*_send() function
476                  * don't support SMB2 (it's only plugged
477                  * into the sync wrapper functions currently)
478                  * we have a way to force SMB1.
479                  */
480                 flags = CLI_FULL_CONNECTION_FORCE_SMB1;
481         }
482
483         if (multi_threaded) {
484 #ifdef HAVE_PTHREAD
485                 ret = py_cli_state_setup_mt_ev(self);
486                 if (!ret) {
487                         return -1;
488                 }
489 #else
490                 PyErr_SetString(PyExc_RuntimeError,
491                                 "No PTHREAD support available");
492                 return -1;
493 #endif
494                 if (!force_smb1) {
495                         PyErr_SetString(PyExc_RuntimeError,
496                                         "multi_threaded is only possible on "
497                                         "SMB1 connections");
498                         return -1;
499                 }
500         } else {
501                 ret = py_cli_state_setup_ev(self);
502                 if (!ret) {
503                         return -1;
504                 }
505         }
506
507         if (creds == NULL) {
508                 cli_creds = cli_credentials_init_anon(NULL);
509         } else {
510                 cli_creds = PyCredentials_AsCliCredentials(creds);
511         }
512
513         req = cli_full_connection_creds_send(
514                 NULL, self->ev, "myname", host, NULL, 0, share, "?????",
515                 cli_creds, flags, signing_state);
516         if (!py_tevent_req_wait_exc(self, req)) {
517                 return -1;
518         }
519         status = cli_full_connection_creds_recv(req, &self->cli);
520         TALLOC_FREE(req);
521
522         if (!NT_STATUS_IS_OK(status)) {
523                 PyErr_SetNTSTATUS(status);
524                 return -1;
525         }
526
527         if (smbXcli_conn_protocol(self->cli->conn) < PROTOCOL_SMB2_02) {
528                 self->is_smb1 = true;
529         }
530
531         /*
532          * Oplocks require a multi threaded connection
533          */
534         if (self->thread_state == NULL) {
535                 return 0;
536         }
537
538         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
539                 self->ev, self->ev, self->cli);
540         if (self->oplock_waiter == NULL) {
541                 PyErr_NoMemory();
542                 return -1;
543         }
544         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
545                                 self);
546         return 0;
547 }
548
549 static void py_cli_got_oplock_break(struct tevent_req *req)
550 {
551         struct py_cli_state *self = (struct py_cli_state *)
552                 tevent_req_callback_data_void(req);
553         struct py_cli_oplock_break b;
554         struct py_cli_oplock_break *tmp;
555         size_t num_breaks;
556         NTSTATUS status;
557
558         status = cli_smb_oplock_break_waiter_recv(req, &b.fnum, &b.level);
559         TALLOC_FREE(req);
560         self->oplock_waiter = NULL;
561
562         if (!NT_STATUS_IS_OK(status)) {
563                 return;
564         }
565
566         num_breaks = talloc_array_length(self->oplock_breaks);
567         tmp = talloc_realloc(self->ev, self->oplock_breaks,
568                              struct py_cli_oplock_break, num_breaks+1);
569         if (tmp == NULL) {
570                 return;
571         }
572         self->oplock_breaks = tmp;
573         self->oplock_breaks[num_breaks] = b;
574
575         if (self->oplock_cond != NULL) {
576                 py_tevent_cond_signal(self->oplock_cond);
577         }
578
579         self->oplock_waiter = cli_smb_oplock_break_waiter_send(
580                 self->ev, self->ev, self->cli);
581         if (self->oplock_waiter == NULL) {
582                 return;
583         }
584         tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
585                                 self);
586 }
587
588 static PyObject *py_cli_get_oplock_break(struct py_cli_state *self,
589                                          PyObject *args)
590 {
591         size_t num_oplock_breaks;
592
593         if (!PyArg_ParseTuple(args, "")) {
594                 return NULL;
595         }
596
597         if (self->thread_state == NULL) {
598                 PyErr_SetString(PyExc_RuntimeError,
599                                 "get_oplock_break() only possible on "
600                                 "a multi_threaded connection");
601                 return NULL;
602         }
603
604         if (self->oplock_cond != NULL) {
605                 errno = EBUSY;
606                 PyErr_SetFromErrno(PyExc_RuntimeError);
607                 return NULL;
608         }
609
610         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
611
612         if (num_oplock_breaks == 0) {
613                 struct py_tevent_cond cond;
614                 int ret;
615
616                 self->oplock_cond = &cond;
617                 ret = py_tevent_cond_wait(&cond);
618                 self->oplock_cond = NULL;
619
620                 if (ret != 0) {
621                         errno = ret;
622                         PyErr_SetFromErrno(PyExc_RuntimeError);
623                         return NULL;
624                 }
625         }
626
627         num_oplock_breaks = talloc_array_length(self->oplock_breaks);
628         if (num_oplock_breaks > 0) {
629                 PyObject *result;
630
631                 result = Py_BuildValue(
632                         "{s:i,s:i}",
633                         "fnum", self->oplock_breaks[0].fnum,
634                         "level", self->oplock_breaks[0].level);
635
636                 memmove(&self->oplock_breaks[0], &self->oplock_breaks[1],
637                         sizeof(self->oplock_breaks[0]) *
638                         (num_oplock_breaks - 1));
639                 self->oplock_breaks = talloc_realloc(
640                         NULL, self->oplock_breaks, struct py_cli_oplock_break,
641                         num_oplock_breaks - 1);
642
643                 return result;
644         }
645         Py_RETURN_NONE;
646 }
647
648 static void py_cli_state_dealloc(struct py_cli_state *self)
649 {
650         TALLOC_FREE(self->thread_state);
651         TALLOC_FREE(self->oplock_waiter);
652         TALLOC_FREE(self->ev);
653
654         if (self->cli != NULL) {
655                 cli_shutdown(self->cli);
656                 self->cli = NULL;
657         }
658         Py_TYPE(self)->tp_free((PyObject *)self);
659 }
660
661 static PyObject *py_cli_settimeout(struct py_cli_state *self, PyObject *args)
662 {
663         unsigned int nmsecs = 0;
664         unsigned int omsecs = 0;
665
666         if (!PyArg_ParseTuple(args, "I", &nmsecs)) {
667                 return NULL;
668         }
669
670         omsecs = cli_set_timeout(self->cli, nmsecs);
671
672         return PyInt_FromLong(omsecs);
673 }
674
675 static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
676                                PyObject *kwds)
677 {
678         char *fname;
679         unsigned CreateFlags = 0;
680         unsigned DesiredAccess = FILE_GENERIC_READ;
681         unsigned FileAttributes = 0;
682         unsigned ShareAccess = 0;
683         unsigned CreateDisposition = FILE_OPEN;
684         unsigned CreateOptions = 0;
685         unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
686         unsigned SecurityFlags = 0;
687         uint16_t fnum;
688         struct tevent_req *req;
689         NTSTATUS status;
690
691         static const char *kwlist[] = {
692                 "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
693                 "ShareAccess", "CreateDisposition", "CreateOptions",
694                 "ImpersonationLevel", "SecurityFlags", NULL };
695
696         if (!ParseTupleAndKeywords(
697                     args, kwds, "s|IIIIIIII", kwlist,
698                     &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
699                     &ShareAccess, &CreateDisposition, &CreateOptions,
700                     &ImpersonationLevel, &SecurityFlags)) {
701                 return NULL;
702         }
703
704         req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
705                                 DesiredAccess, FileAttributes, ShareAccess,
706                                 CreateDisposition, CreateOptions,
707                                 ImpersonationLevel, SecurityFlags);
708         if (!py_tevent_req_wait_exc(self, req)) {
709                 return NULL;
710         }
711         status = cli_ntcreate_recv(req, &fnum, NULL);
712         TALLOC_FREE(req);
713
714         if (!NT_STATUS_IS_OK(status)) {
715                 PyErr_SetNTSTATUS(status);
716                 return NULL;
717         }
718         return Py_BuildValue("I", (unsigned)fnum);
719 }
720
721 static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
722 {
723         struct tevent_req *req;
724         int fnum;
725         NTSTATUS status;
726
727         if (!PyArg_ParseTuple(args, "i", &fnum)) {
728                 return NULL;
729         }
730
731         req = cli_close_send(NULL, self->ev, self->cli, fnum);
732         if (!py_tevent_req_wait_exc(self, req)) {
733                 return NULL;
734         }
735         status = cli_close_recv(req);
736         TALLOC_FREE(req);
737
738         if (!NT_STATUS_IS_OK(status)) {
739                 PyErr_SetNTSTATUS(status);
740                 return NULL;
741         }
742         Py_RETURN_NONE;
743 }
744
745 struct push_state {
746         char *data;
747         off_t nread;
748         off_t total_data;
749 };
750
751 /*
752  * cli_push() helper to write a chunk of data to a remote file
753  */
754 static size_t push_data(uint8_t *buf, size_t n, void *priv)
755 {
756         struct push_state *state = (struct push_state *)priv;
757         char *curr_ptr = NULL;
758         off_t remaining;
759         size_t copied_bytes;
760
761         if (state->nread >= state->total_data) {
762                 return 0;
763         }
764
765         curr_ptr = state->data + state->nread;
766         remaining = state->total_data - state->nread;
767         copied_bytes = MIN(remaining, n);
768
769         memcpy(buf, curr_ptr, copied_bytes);
770         state->nread += copied_bytes;
771         return copied_bytes;
772 }
773
774 /*
775  * Writes a file with the contents specified
776  */
777 static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args,
778                                  PyObject *kwargs)
779 {
780         uint16_t fnum;
781         const char *filename = NULL;
782         char *data = NULL;
783         Py_ssize_t size = 0;
784         NTSTATUS status;
785         struct tevent_req *req = NULL;
786         struct push_state state;
787
788         if (!PyArg_ParseTuple(args, "s"PYARG_BYTES_LEN":savefile", &filename,
789                               &data, &size)) {
790                 return NULL;
791         }
792
793         /* create a new file handle for writing to */
794         req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
795                                 FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL,
796                                 FILE_SHARE_READ|FILE_SHARE_WRITE,
797                                 FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE,
798                                 SMB2_IMPERSONATION_IMPERSONATION, 0);
799         if (!py_tevent_req_wait_exc(self, req)) {
800                 return NULL;
801         }
802         status = cli_ntcreate_recv(req, &fnum, NULL);
803         TALLOC_FREE(req);
804         PyErr_NTSTATUS_IS_ERR_RAISE(status);
805
806         /* write the new file contents */
807         state.data = data;
808         state.nread = 0;
809         state.total_data = size;
810
811         req = cli_push_send(NULL, self->ev, self->cli, fnum, 0, 0, 0,
812                             push_data, &state);
813         if (!py_tevent_req_wait_exc(self, req)) {
814                 return NULL;
815         }
816         status = cli_push_recv(req);
817         TALLOC_FREE(req);
818         PyErr_NTSTATUS_IS_ERR_RAISE(status);
819
820         /* close the file handle */
821         req = cli_close_send(NULL, self->ev, self->cli, fnum);
822         if (!py_tevent_req_wait_exc(self, req)) {
823                 return NULL;
824         }
825         status = cli_close_recv(req);
826         PyErr_NTSTATUS_IS_ERR_RAISE(status);
827
828         Py_RETURN_NONE;
829 }
830
831 static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
832                               PyObject *kwds)
833 {
834         int fnum;
835         unsigned mode = 0;
836         char *buf;
837         Py_ssize_t buflen;
838         unsigned long long offset;
839         struct tevent_req *req;
840         NTSTATUS status;
841         size_t written;
842
843         static const char *kwlist[] = {
844                 "fnum", "buffer", "offset", "mode", NULL };
845
846         if (!ParseTupleAndKeywords(
847                     args, kwds, "I" PYARG_BYTES_LEN "K|I", kwlist,
848                     &fnum, &buf, &buflen, &offset, &mode)) {
849                 return NULL;
850         }
851
852         req = cli_write_send(NULL, self->ev, self->cli, fnum, mode,
853                              (uint8_t *)buf, offset, buflen);
854         if (!py_tevent_req_wait_exc(self, req)) {
855                 return NULL;
856         }
857         status = cli_write_recv(req, &written);
858         TALLOC_FREE(req);
859
860         if (!NT_STATUS_IS_OK(status)) {
861                 PyErr_SetNTSTATUS(status);
862                 return NULL;
863         }
864         return Py_BuildValue("K", (unsigned long long)written);
865 }
866
867 /*
868  * Returns the size of the given file
869  */
870 static NTSTATUS py_smb_filesize(struct py_cli_state *self, uint16_t fnum,
871                                 off_t *size)
872 {
873         NTSTATUS status;
874
875         if (self->is_smb1) {
876                 uint8_t *rdata = NULL;
877                 struct tevent_req *req = NULL;
878
879                 req = cli_qfileinfo_send(NULL, self->ev, self->cli, fnum,
880                                          SMB_QUERY_FILE_ALL_INFO, 68,
881                                          CLI_BUFFER_SIZE);
882                 if (!py_tevent_req_wait_exc(self, req)) {
883                         return NT_STATUS_INTERNAL_ERROR;
884                 }
885                 status = cli_qfileinfo_recv(req, NULL, NULL, &rdata, NULL);
886                 if (NT_STATUS_IS_OK(status)) {
887                         *size = IVAL2_TO_SMB_BIG_UINT(rdata, 48);
888                 }
889                 TALLOC_FREE(req);
890                 TALLOC_FREE(rdata);
891         } else {
892                 status = cli_qfileinfo_basic(self->cli, fnum, NULL, size,
893                                              NULL, NULL, NULL, NULL, NULL);
894         }
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                                  PyObject *kwargs)
903 {
904         NTSTATUS status;
905         const char *filename = NULL;
906         struct tevent_req *req = NULL;
907         uint16_t fnum;
908         off_t size;
909         char *buf = NULL;
910         off_t nread = 0;
911         PyObject *result = NULL;
912
913         if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
914                 return NULL;
915         }
916
917         /* get a read file handle */
918         req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
919                                 FILE_READ_DATA, 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(const char *mntpoint, struct file_info *finfo,
1110                             const char *mask, void *state)
1111 {
1112         PyObject *result = (PyObject *)state;
1113         PyObject *file = NULL;
1114         int ret;
1115
1116         /* suppress '.' and '..' in the results we return */
1117         if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
1118                 return NT_STATUS_OK;
1119         }
1120
1121         /*
1122          * Build a dictionary representing the file info.
1123          * Note: Windows does not always return short_name (so it may be None)
1124          */
1125         file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}",
1126                              "name", finfo->name,
1127                              "attrib", (int)finfo->mode,
1128                              "short_name", finfo->short_name,
1129                              "size", PyLong_FromUnsignedLongLong(finfo->size),
1130                              "mtime",
1131                              convert_timespec_to_time_t(finfo->mtime_ts));
1132
1133         if (file == NULL) {
1134                 return NT_STATUS_NO_MEMORY;
1135         }
1136
1137         ret = PyList_Append(result, file);
1138         if (ret == -1) {
1139                 return NT_STATUS_INTERNAL_ERROR;
1140         }
1141
1142         return NT_STATUS_OK;
1143 }
1144
1145 static NTSTATUS do_listing(struct py_cli_state *self,
1146                            const char *base_dir, const char *user_mask,
1147                            uint16_t attribute,
1148                            NTSTATUS (*callback_fn)(const char *,
1149                                                    struct file_info *,
1150                                                    const char *, void *),
1151                            void *priv)
1152 {
1153         char *mask = NULL;
1154         unsigned info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
1155         struct file_info *finfos = NULL;
1156         size_t i;
1157         size_t num_finfos = 0;
1158         NTSTATUS status;
1159
1160         if (user_mask == NULL) {
1161                 mask = talloc_asprintf(NULL, "%s\\*", base_dir);
1162         } else {
1163                 mask = talloc_asprintf(NULL, "%s\\%s", base_dir, user_mask);
1164         }
1165
1166         if (mask == NULL) {
1167                 return NT_STATUS_NO_MEMORY;
1168         }
1169         dos_format(mask);
1170
1171         if (self->is_smb1) {
1172                 struct tevent_req *req = NULL;
1173
1174                 req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
1175                                     info_level);
1176                 if (!py_tevent_req_wait_exc(self, req)) {
1177                         return NT_STATUS_INTERNAL_ERROR;
1178                 }
1179                 status = cli_list_recv(req, NULL, &finfos, &num_finfos);
1180                 TALLOC_FREE(req);
1181         } else {
1182                 status = cli_list(self->cli, mask, attribute, callback_fn,
1183                                   priv);
1184         }
1185         TALLOC_FREE(mask);
1186
1187         if (!NT_STATUS_IS_OK(status)) {
1188                 return status;
1189         }
1190
1191         /* invoke the callback for the async results (SMBv1 connections) */
1192         for (i = 0; i < num_finfos; i++) {
1193                 status = callback_fn(base_dir, &finfos[i], user_mask,
1194                                      priv);
1195                 if (!NT_STATUS_IS_OK(status)) {
1196                         TALLOC_FREE(finfos);
1197                         return status;
1198                 }
1199         }
1200
1201         TALLOC_FREE(finfos);
1202         return status;
1203 }
1204
1205 static PyObject *py_cli_list(struct py_cli_state *self,
1206                              PyObject *args,
1207                              PyObject *kwds)
1208 {
1209         char *base_dir;
1210         char *user_mask = NULL;
1211         unsigned attribute =
1212                 FILE_ATTRIBUTE_DIRECTORY |
1213                 FILE_ATTRIBUTE_SYSTEM |
1214                 FILE_ATTRIBUTE_HIDDEN;
1215         NTSTATUS status;
1216         PyObject *result;
1217         const char *kwlist[] = { "directory", "mask", "attribs", NULL };
1218
1219         if (!ParseTupleAndKeywords(args, kwds, "z|sH: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 /*
1242  * Deletes a file
1243  */
1244 static NTSTATUS unlink_file(struct py_cli_state *self, const char *filename)
1245 {
1246         NTSTATUS status;
1247         uint16_t attrs = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
1248
1249         if (self->is_smb1) {
1250                 struct tevent_req *req = NULL;
1251
1252                 req = cli_unlink_send(NULL, self->ev, self->cli, filename,
1253                                       attrs);
1254                 if (!py_tevent_req_wait_exc(self, req)) {
1255                         return NT_STATUS_INTERNAL_ERROR;
1256                 }
1257                 status = cli_unlink_recv(req);
1258                 TALLOC_FREE(req);
1259         } else {
1260                 status = cli_unlink(self->cli, filename, attrs);
1261         }
1262
1263         return status;
1264 }
1265
1266 static PyObject *py_smb_unlink(struct py_cli_state *self, PyObject *args)
1267 {
1268         NTSTATUS status;
1269         const char *filename = NULL;
1270
1271         if (!PyArg_ParseTuple(args, "s:unlink", &filename)) {
1272                 return NULL;
1273         }
1274
1275         status = unlink_file(self, filename);
1276         PyErr_NTSTATUS_NOT_OK_RAISE(status);
1277
1278         Py_RETURN_NONE;
1279 }
1280
1281 /*
1282  * Delete an empty directory
1283  */
1284 static NTSTATUS remove_dir(struct py_cli_state *self, const char *dirname)
1285 {
1286         NTSTATUS status;
1287
1288         if (self->is_smb1) {
1289                 struct tevent_req *req = NULL;
1290
1291                 req = cli_rmdir_send(NULL, self->ev, self->cli, dirname);
1292                 if (!py_tevent_req_wait_exc(self, req)) {
1293                         return NT_STATUS_INTERNAL_ERROR;
1294                 }
1295                 status = cli_rmdir_recv(req);
1296                 TALLOC_FREE(req);
1297         } else {
1298                 status = cli_rmdir(self->cli, dirname);
1299         }
1300         return status;
1301 }
1302
1303 static PyObject *py_smb_rmdir(struct py_cli_state *self, PyObject *args)
1304 {
1305         NTSTATUS status;
1306         const char *dirname;
1307
1308         if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
1309                 return NULL;
1310         }
1311
1312         status = remove_dir(self, dirname);
1313         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1314
1315         Py_RETURN_NONE;
1316 }
1317
1318 /*
1319  * Create a directory
1320  */
1321 static PyObject *py_smb_mkdir(struct py_cli_state *self, PyObject *args)
1322 {
1323         NTSTATUS status;
1324         const char *dirname;
1325
1326         if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
1327                 return NULL;
1328         }
1329
1330         if (self->is_smb1) {
1331                 struct tevent_req *req = NULL;
1332
1333                 req = cli_mkdir_send(NULL, self->ev, self->cli, dirname);
1334                 if (!py_tevent_req_wait_exc(self, req)) {
1335                         return NULL;
1336                 }
1337                 status = cli_mkdir_recv(req);
1338                 TALLOC_FREE(req);
1339         } else {
1340                 status = cli_mkdir(self->cli, dirname);
1341         }
1342         PyErr_NTSTATUS_IS_ERR_RAISE(status);
1343
1344         Py_RETURN_NONE;
1345 }
1346
1347 /*
1348  * Checks existence of a directory
1349  */
1350 static bool check_dir_path(struct py_cli_state *self, const char *path)
1351 {
1352         NTSTATUS status;
1353
1354         if (self->is_smb1) {
1355                 struct tevent_req *req = NULL;
1356
1357                 req = cli_chkpath_send(NULL, self->ev, self->cli, path);
1358                 if (!py_tevent_req_wait_exc(self, req)) {
1359                         return false;
1360                 }
1361                 status = cli_chkpath_recv(req);
1362                 TALLOC_FREE(req);
1363         } else {
1364                 status = cli_chkpath(self->cli, path);
1365         }
1366
1367         return NT_STATUS_IS_OK(status);
1368 }
1369
1370 static PyObject *py_smb_chkpath(struct py_cli_state *self, PyObject *args)
1371 {
1372         const char *path;
1373         bool dir_exists;
1374
1375         if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
1376                 return NULL;
1377         }
1378
1379         dir_exists = check_dir_path(self, path);
1380         return PyBool_FromLong(dir_exists);
1381 }
1382
1383 static PyMethodDef py_cli_state_methods[] = {
1384         { "settimeout", (PyCFunction)py_cli_settimeout, METH_VARARGS,
1385           "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
1386         { "create", (PyCFunction)py_cli_create, METH_VARARGS|METH_KEYWORDS,
1387           "Open a file" },
1388         { "close", (PyCFunction)py_cli_close, METH_VARARGS,
1389           "Close a file handle" },
1390         { "write", (PyCFunction)py_cli_write, METH_VARARGS|METH_KEYWORDS,
1391           "Write to a file handle" },
1392         { "read", (PyCFunction)py_cli_read, METH_VARARGS|METH_KEYWORDS,
1393           "Read from a file handle" },
1394         { "truncate", (PyCFunction)py_cli_ftruncate,
1395           METH_VARARGS|METH_KEYWORDS,
1396           "Truncate a file" },
1397         { "delete_on_close", (PyCFunction)py_cli_delete_on_close,
1398           METH_VARARGS|METH_KEYWORDS,
1399           "Set/Reset the delete on close flag" },
1400         { "list", (PyCFunction)py_cli_list, METH_VARARGS|METH_KEYWORDS,
1401           "list(directory, mask='*', attribs=DEFAULT_ATTRS) -> "
1402           "directory contents as a dictionary\n"
1403           "\t\tDEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | "
1404           "FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE\n\n"
1405           "\t\tList contents of a directory. The keys are, \n"
1406           "\t\t\tname: Long name of the directory item\n"
1407           "\t\t\tshort_name: Short name of the directory item\n"
1408           "\t\t\tsize: File size in bytes\n"
1409           "\t\t\tattrib: Attributes\n"
1410           "\t\t\tmtime: Modification time\n" },
1411         { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
1412           METH_VARARGS, "Wait for an oplock break" },
1413         { "unlink", (PyCFunction)py_smb_unlink,
1414           METH_VARARGS,
1415           "unlink(path) -> None\n\n \t\tDelete a file." },
1416         { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
1417           "mkdir(path) -> None\n\n \t\tCreate a directory." },
1418         { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
1419           "rmdir(path) -> None\n\n \t\tDelete a directory." },
1420         { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
1421           "chkpath(dir_path) -> True or False\n\n"
1422           "\t\tReturn true if directory exists, false otherwise." },
1423         { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
1424           "savefile(path, str) -> None\n\n"
1425           "\t\tWrite " PY_DESC_PY3_BYTES " str to file." },
1426         { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
1427           "loadfile(path) -> file contents as a " PY_DESC_PY3_BYTES
1428           "\n\n\t\tRead contents of a file." },
1429         { NULL, NULL, 0, NULL }
1430 };
1431
1432 static PyTypeObject py_cli_state_type = {
1433         PyVarObject_HEAD_INIT(NULL, 0)
1434         .tp_name = "libsmb_samba_internal.Conn",
1435         .tp_basicsize = sizeof(struct py_cli_state),
1436         .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1437         .tp_doc = "libsmb connection",
1438         .tp_new = py_cli_state_new,
1439         .tp_init = (initproc)py_cli_state_init,
1440         .tp_dealloc = (destructor)py_cli_state_dealloc,
1441         .tp_methods = py_cli_state_methods,
1442 };
1443
1444 static PyMethodDef py_libsmb_methods[] = {
1445         { NULL },
1446 };
1447
1448 void initlibsmb_samba_internal(void);
1449
1450 static struct PyModuleDef moduledef = {
1451     PyModuleDef_HEAD_INIT,
1452     .m_name = "libsmb_samba_internal",
1453     .m_doc = "libsmb wrapper",
1454     .m_size = -1,
1455     .m_methods = py_libsmb_methods,
1456 };
1457
1458 MODULE_INIT_FUNC(libsmb_samba_internal)
1459 {
1460         PyObject *m = NULL;
1461
1462         talloc_stackframe();
1463
1464         m = PyModule_Create(&moduledef);
1465         if (m == NULL) {
1466                 return m;
1467         }
1468         if (PyType_Ready(&py_cli_state_type) < 0) {
1469                 return NULL;
1470         }
1471         Py_INCREF(&py_cli_state_type);
1472         PyModule_AddObject(m, "Conn", (PyObject *)&py_cli_state_type);
1473         return m;
1474 }