Support no_autoprops argument to Client.add.
[jelmer/subvertpy.git] / subvertpy / _ra.c
1 /*
2  * Copyright © 2008-2009 Jelmer Vernooij <jelmer@jelmer.uk>
3  * -*- coding: utf-8 -*-
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; either version 2.1 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 #include <stdbool.h>
20 #include <Python.h>
21 #include <apr_general.h>
22 #include <svn_types.h>
23 #include <svn_ra.h>
24 #include <svn_path.h>
25 #include <svn_props.h>
26 #include <apr_file_io.h>
27 #include <apr_portable.h>
28
29 #include <structmember.h>
30
31 #include "editor.h"
32 #include "util.h"
33 #include "ra.h"
34
35 #if ONLY_SINCE_SVN(1, 5)
36 #define REPORTER_T svn_ra_reporter3_t
37 #else
38 #define REPORTER_T svn_ra_reporter2_t
39 #endif
40
41 static PyObject *busy_exc;
42
43 static PyTypeObject Reporter_Type;
44 static PyTypeObject RemoteAccess_Type;
45 static PyTypeObject AuthProvider_Type;
46 static PyTypeObject CredentialsIter_Type;
47 static PyTypeObject Auth_Type;
48
49 static bool ra_check_svn_path(const char *path)
50 {
51         /* svn_ra_check_path will raise an assertion error if the path has a
52          * leading '/'. Raise a Python exception if there ar eleading '/'s so that 
53          * the Python interpreter won't crash and die. */
54         if (*path == '/') {
55                 PyErr_SetString(PyExc_ValueError, "invalid path has a leading '/'");
56                 return true;
57         }
58         return false;
59 }
60
61 static svn_error_t *py_commit_callback(const svn_commit_info_t *commit_info, void *baton, apr_pool_t *pool)
62 {
63         PyObject *fn = (PyObject *)baton, *ret;
64         PyGILState_STATE state;
65
66         if (fn == Py_None)
67                 return NULL;
68
69         state = PyGILState_Ensure();
70
71         ret = PyObject_CallFunction(fn, "lzz", 
72                                         commit_info->revision, commit_info->date, 
73                                         commit_info->author);
74         CB_CHECK_PYRETVAL(ret);
75         Py_DECREF(ret);
76         PyGILState_Release(state);
77         return NULL;
78 }
79
80 static PyObject *pyify_lock(const svn_lock_t *lock)
81 {
82         return Py_BuildValue("(ssszbLL)", 
83                                                  lock->path, lock->token, 
84                                                  lock->owner, lock->comment,
85                                                  lock->is_dav_comment,
86                                                  lock->creation_date,
87                                                  lock->expiration_date);
88 }
89
90 static svn_error_t *py_lock_func (void *baton, const char *path, int do_lock, 
91                                                    const svn_lock_t *lock, svn_error_t *ra_err, 
92                                                    apr_pool_t *pool)
93 {
94         PyObject *py_ra_err, *ret, *py_lock;
95         PyGILState_STATE state = PyGILState_Ensure();
96         if (ra_err != NULL) {
97                 py_ra_err = PyErr_NewSubversionException(ra_err);
98         } else {
99                 py_ra_err = Py_None;
100                 Py_INCREF(py_ra_err);
101         }
102         py_lock = pyify_lock(lock);
103         ret = PyObject_CallFunction((PyObject *)baton, "zbOO", path, do_lock, 
104                                                   py_lock, py_ra_err);
105         Py_DECREF(py_lock);
106         Py_DECREF(py_ra_err);
107         CB_CHECK_PYRETVAL(ret);
108         Py_DECREF(ret);
109         PyGILState_Release(state);
110         return NULL;
111 }
112
113 /** Connection to a remote Subversion repository. */
114 typedef struct {
115         PyObject_VAR_HEAD
116         svn_ra_session_t *ra;
117         apr_pool_t *pool;
118         const char *url;
119         PyObject *progress_func;
120         AuthObject *auth;
121         bool busy;
122         PyObject *client_string_func;
123         PyObject *open_tmp_file_func;
124         const char *root;
125         const char *corrected_url;
126 } RemoteAccessObject;
127
128 typedef struct {
129         PyObject_VAR_HEAD
130         const REPORTER_T *reporter;
131         void *report_baton;
132         apr_pool_t *pool;
133         RemoteAccessObject *ra;
134 } ReporterObject;
135
136 static PyObject *reporter_set_path(PyObject *self, PyObject *args)
137 {
138         char *path; 
139         svn_revnum_t revision; 
140         bool start_empty; 
141         char *lock_token = NULL;
142         svn_depth_t depth = svn_depth_infinity;
143         ReporterObject *reporter = (ReporterObject *)self;
144
145         if (!PyArg_ParseTuple(args, "slb|zi:set_path", &path, &revision, &start_empty, 
146                                                   &lock_token, &depth))
147                 return NULL;
148
149         if (reporter->ra == NULL) {
150                 PyErr_SetString(PyExc_RuntimeError,
151                         "Reporter already finished.");
152                 return NULL;
153         }
154
155 #if ONLY_SINCE_SVN(1, 5)
156         RUN_SVN(reporter->reporter->set_path(reporter->report_baton, 
157                                                                                                   path, revision, depth, start_empty, 
158                                          lock_token, reporter->pool));
159 #else
160         if (depth != svn_depth_infinity) {
161                 PyErr_SetString(PyExc_NotImplementedError, 
162                                                 "depth != infinity only supported for svn >= 1.5");
163                 return NULL;
164         }
165         RUN_SVN(reporter->reporter->set_path(reporter->report_baton, 
166                                                                                                   path, revision, start_empty, 
167                                          lock_token, reporter->pool));
168 #endif
169         Py_RETURN_NONE;
170 }
171
172 static PyObject *reporter_delete_path(PyObject *self, PyObject *args)
173 {
174         ReporterObject *reporter = (ReporterObject *)self;
175         char *path;
176         if (!PyArg_ParseTuple(args, "s:delete_path", &path))
177                 return NULL;
178
179         if (reporter->ra == NULL) {
180                 PyErr_SetString(PyExc_RuntimeError,
181                         "Reporter already finished.");
182                 return NULL;
183         }
184
185         RUN_SVN(reporter->reporter->delete_path(reporter->report_baton,
186                 path, reporter->pool));
187
188         Py_RETURN_NONE;
189 }
190
191 static PyObject *reporter_link_path(PyObject *self, PyObject *args)
192 {
193         char *path, *url;
194         svn_revnum_t revision;
195         bool start_empty;
196         char *lock_token = NULL;
197         ReporterObject *reporter = (ReporterObject *)self;
198         svn_depth_t depth = svn_depth_infinity;
199
200         if (!PyArg_ParseTuple(args, "sslb|zi:link_path", &path, &url, &revision,
201                         &start_empty, &lock_token, &depth))
202                 return NULL;
203
204         if (reporter->ra == NULL) {
205                 PyErr_SetString(PyExc_RuntimeError,
206                         "Reporter already finished.");
207                 return NULL;
208         }
209
210 #if ONLY_SINCE_SVN(1, 5)
211         RUN_SVN(reporter->reporter->link_path(reporter->report_baton, path, url, 
212                                 revision, depth, start_empty, lock_token, reporter->pool));
213 #else
214         if (depth != svn_depth_infinity) {
215                 PyErr_SetString(PyExc_NotImplementedError, 
216                                                 "depth != infinity only supported for svn >= 1.5");
217                 return NULL;
218         }
219         RUN_SVN(reporter->reporter->link_path(reporter->report_baton, path, url, 
220                                 revision, start_empty, lock_token, reporter->pool));
221 #endif
222
223         Py_RETURN_NONE;
224 }
225
226 static PyObject *reporter_finish(PyObject *self)
227 {
228         ReporterObject *reporter = (ReporterObject *)self;
229
230         if (reporter->ra == NULL) {
231                 PyErr_SetString(PyExc_RuntimeError,
232                         "Reporter already finished.");
233                 return NULL;
234         }
235
236         reporter->ra->busy = false;
237
238         RUN_SVN(reporter->reporter->finish_report(
239                 reporter->report_baton, reporter->pool));
240
241         apr_pool_destroy(reporter->pool);
242         Py_XDECREF(reporter->ra);
243         reporter->ra = NULL;
244
245         Py_RETURN_NONE;
246 }
247
248 static PyObject *reporter_abort(PyObject *self)
249 {
250         ReporterObject *reporter = (ReporterObject *)self;
251
252         if (reporter->ra == NULL) {
253                 PyErr_SetString(PyExc_RuntimeError,
254                         "Reporter already finished.");
255                 return NULL;
256         }
257
258         reporter->ra->busy = false;
259
260         RUN_SVN(reporter->reporter->abort_report(reporter->report_baton, 
261                                                                                                          reporter->pool));
262
263         apr_pool_destroy(reporter->pool);
264         Py_XDECREF(reporter->ra);
265         reporter->ra = NULL;
266
267         Py_RETURN_NONE;
268 }
269
270 static PyMethodDef reporter_methods[] = {
271         { "abort", (PyCFunction)reporter_abort, METH_NOARGS, 
272                 "S.abort()\n"
273                 "Abort this report." },
274         { "finish", (PyCFunction)reporter_finish, METH_NOARGS, 
275                 "S.finish()\n"
276                 "Finish this report." },
277         { "link_path", (PyCFunction)reporter_link_path, METH_VARARGS, 
278                 "S.link_path(path, url, revision, start_empty, lock_token=None)\n" },
279         { "set_path", (PyCFunction)reporter_set_path, METH_VARARGS, 
280                 "S.set_path(path, revision, start_empty, lock_token=None)\n" },
281         { "delete_path", (PyCFunction)reporter_delete_path, METH_VARARGS, 
282                 "S.delete_path(path)\n" },
283         { NULL, }
284 };
285
286 static void reporter_dealloc(PyObject *self)
287 {
288         ReporterObject *reporter = (ReporterObject *)self;
289         if (reporter->ra != NULL) {
290                 /* FIXME: Warn */
291                 apr_pool_destroy(reporter->pool);
292                 Py_DECREF(reporter->ra);
293         }
294         PyObject_Del(self);
295 }
296
297 static PyTypeObject Reporter_Type = {
298         PyVarObject_HEAD_INIT(NULL, 0)
299         "_ra.Reporter", /*      const char *tp_name;  For printing, in format "<module>.<name>" */
300         sizeof(ReporterObject),
301         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
302
303         /* Methods to implement standard operations */
304
305         reporter_dealloc, /*    destructor tp_dealloc;  */
306         NULL, /*        printfunc tp_print;     */
307         NULL, /*        getattrfunc tp_getattr; */
308         NULL, /*        setattrfunc tp_setattr; */
309         NULL, /*        cmpfunc tp_compare;     */
310         NULL, /*        reprfunc tp_repr;       */
311
312         /* Method suites for standard classes */
313
314         NULL, /*        PyNumberMethods *tp_as_number;  */
315         NULL, /*        PySequenceMethods *tp_as_sequence;      */
316         NULL, /*        PyMappingMethods *tp_as_mapping;        */
317
318         /* More standard operations (here for binary compatibility) */
319
320         NULL, /*        hashfunc tp_hash;       */
321         NULL, /*        ternaryfunc tp_call;    */
322         NULL, /*        reprfunc tp_str;        */
323         NULL, /*        getattrofunc tp_getattro;       */
324         NULL, /*        setattrofunc tp_setattro;       */
325
326         /* Functions to access object as input/output buffer */
327         NULL, /*        PyBufferProcs *tp_as_buffer;    */
328
329         /* Flags to define presence of optional/expanded features */
330         0, /*   long tp_flags;  */
331
332         NULL, /*        const char *tp_doc;  Documentation string */
333
334         /* Assigned meaning in release 2.0 */
335         /* call function for all accessible objects */
336         NULL, /*        traverseproc tp_traverse;       */
337
338         /* delete references to contained objects */
339         NULL, /*        inquiry tp_clear;       */
340
341         /* Assigned meaning in release 2.1 */
342         /* rich comparisons */
343         NULL, /*        richcmpfunc tp_richcompare;     */
344
345         /* weak reference enabler */
346         0, /*   Py_ssize_t tp_weaklistoffset;   */
347
348         /* Added in release 2.2 */
349         /* Iterators */
350         NULL, /*        getiterfunc tp_iter;    */
351         NULL, /*        iternextfunc tp_iternext;       */
352
353         /* Attribute descriptor and subclassing stuff */
354         reporter_methods, /*    struct PyMethodDef *tp_methods; */
355
356 };
357
358 /**
359  * Get libsvn_ra version information.
360  *
361  * :return: tuple with major, minor, patch version number and tag.
362  */
363 static PyObject *version(PyObject *self)
364 {
365         const svn_version_t *ver = svn_ra_version();
366         return Py_BuildValue("(iiis)", ver->major, ver->minor, 
367                                                  ver->patch, ver->tag);
368 }
369
370 SVN_VERSION_DEFINE(svn_api_version);
371
372 /**
373  * Get compile-time libsvn_ra version information.
374  *
375  * :return: tuple with major, minor, patch version number and tag.
376  */
377 static PyObject *api_version(PyObject *self)
378 {
379         const svn_version_t *ver = &svn_api_version;
380         return Py_BuildValue("(iiis)", ver->major, ver->minor, 
381                                                  ver->patch, ver->tag);
382 }
383
384 #if ONLY_SINCE_SVN(1, 5)
385 static svn_error_t *py_file_rev_handler(void *baton, const char *path, svn_revnum_t rev, apr_hash_t *rev_props, svn_boolean_t result_of_merge, svn_txdelta_window_handler_t *delta_handler, void **delta_baton, apr_array_header_t *prop_diffs, apr_pool_t *pool)
386 {
387         PyObject *fn = (PyObject *)baton, *ret, *py_rev_props;
388         PyGILState_STATE state = PyGILState_Ensure();
389
390         py_rev_props = prop_hash_to_dict(rev_props);
391         CB_CHECK_PYRETVAL(py_rev_props);
392
393         ret = PyObject_CallFunction(fn, "slOb", path, rev, py_rev_props, result_of_merge);
394         Py_DECREF(py_rev_props);
395         CB_CHECK_PYRETVAL(ret);
396
397         if (delta_baton != NULL && delta_handler != NULL) {
398                 *delta_baton = (void *)ret;
399                 *delta_handler = py_txdelta_window_handler;
400         } else {
401                 Py_DECREF(ret);
402         }
403         PyGILState_Release(state);
404         return NULL;
405 }
406 #else
407 static svn_error_t *py_ra_file_rev_handler(void *baton, const char *path, svn_revnum_t rev, apr_hash_t *rev_props, svn_txdelta_window_handler_t *delta_handler, void **delta_baton, apr_array_header_t *prop_diffs, apr_pool_t *pool)
408 {
409         PyObject *fn = (PyObject *)baton, *ret, *py_rev_props;
410         PyGILState_STATE state = PyGILState_Ensure();
411
412         py_rev_props = prop_hash_to_dict(rev_props);
413         CB_CHECK_PYRETVAL(py_rev_props);
414
415         ret = PyObject_CallFunction(fn, "slO", path, rev, py_rev_props);
416         Py_DECREF(py_rev_props);
417         CB_CHECK_PYRETVAL(ret);
418
419         if (delta_baton != NULL && delta_handler != NULL) {
420                 *delta_baton = (void *)ret;
421                 *delta_handler = py_txdelta_window_handler;
422         } else {
423                 Py_DECREF(ret);
424         }
425         PyGILState_Release(state);
426         return NULL;
427 }
428
429
430 #endif
431
432 static void ra_done_handler(void *_ra)
433 {
434         RemoteAccessObject *ra = (RemoteAccessObject *)_ra;
435
436         ra->busy = false;
437
438         Py_DECREF(ra);
439 }
440
441 #define RUN_RA_WITH_POOL(pool, ra, cmd) { \
442         svn_error_t *err; \
443         PyThreadState *_save; \
444         _save = PyEval_SaveThread(); \
445         err = (cmd); \
446         PyEval_RestoreThread(_save); \
447         if (err != NULL) { \
448                 handle_svn_error(err); \
449                 svn_error_clear(err); \
450                 apr_pool_destroy(pool); \
451                 ra->busy = false; \
452                 return NULL; \
453         } \
454         ra->busy = false; \
455 }
456
457 static bool ra_check_busy(RemoteAccessObject *raobj)
458 {
459         if (raobj->busy) {
460                 PyErr_SetString(busy_exc, "Remote access object already in use");
461                 return true;
462         }
463         raobj->busy = true;
464         return false;
465 }
466
467 #if ONLY_SINCE_SVN(1, 5)
468 static svn_error_t *py_get_client_string(void *baton, const char **name, apr_pool_t *pool)
469 {
470         RemoteAccessObject *self = (RemoteAccessObject *)baton;
471         PyObject *ret;
472         PyGILState_STATE state;
473
474         if (self->client_string_func == Py_None) {
475                 *name = NULL;
476                 return NULL;
477         }
478
479         state = PyGILState_Ensure();
480
481         ret = PyObject_CallFunction(self->client_string_func, "");
482
483         CB_CHECK_PYRETVAL(ret);
484
485         *name = py_object_to_svn_string(ret, pool);
486         Py_DECREF(ret);
487
488         PyGILState_Release(state);
489         return NULL;
490 }
491 #endif
492
493 /* Based on svn_swig_py_make_file() from Subversion */
494 static svn_error_t *py_open_tmp_file(apr_file_t **fp, void *callback,
495                                                                          apr_pool_t *pool)
496 {
497         RemoteAccessObject *self = (RemoteAccessObject *)callback;
498         PyObject *ret;
499         apr_status_t status;
500         PyGILState_STATE state;
501
502         if (self->open_tmp_file_func == Py_None) {
503                 const char *path;
504
505                 SVN_ERR (svn_io_temp_dir (&path, pool));
506                 path = svn_dirent_join (path, "subvertpy", pool);
507 #if ONLY_SINCE_SVN(1, 6)
508                 SVN_ERR (svn_io_open_unique_file3(fp, NULL, path, svn_io_file_del_on_pool_cleanup, pool, pool));
509 #else
510                 SVN_ERR (svn_io_open_unique_file (fp, NULL, path, ".tmp", TRUE, pool));
511 #endif
512
513                 return NULL;
514         }
515
516         state = PyGILState_Ensure();
517
518         ret = PyObject_CallFunction(self->open_tmp_file_func, "");
519
520         CB_CHECK_PYRETVAL(ret);
521
522         if (PyString_Check(ret)) {
523                 char* fname = PyString_AsString(ret);
524                 status = apr_file_open(fp, fname, APR_CREATE | APR_READ | APR_WRITE, APR_OS_DEFAULT, 
525                                                                 pool);
526                 if (status) {
527                         PyErr_SetAprStatus(status);
528                         goto fail_file;
529                 }
530                 Py_DECREF(ret);
531         } else if (PyFile_Check(ret)) {
532                 *fp = apr_file_from_object(ret, pool);
533                 Py_DECREF(ret);
534                 if (!*fp) {
535                         goto fail;
536                 }
537         } else {
538                 PyErr_SetString(PyExc_TypeError, "Unknown type for file variable");
539                 Py_DECREF(ret);
540                 PyGILState_Release(state);
541                 return py_svn_error();
542         }
543
544         PyGILState_Release(state);
545         return NULL;
546         
547 fail_file:
548         Py_DECREF(ret);
549 fail:
550         PyGILState_Release(state);
551         return py_svn_error();
552 }
553
554 static void py_progress_func(apr_off_t progress, apr_off_t total, void *baton, apr_pool_t *pool)
555 {
556         PyGILState_STATE state = PyGILState_Ensure();
557         RemoteAccessObject *ra = (RemoteAccessObject *)baton;
558         PyObject *fn = (PyObject *)ra->progress_func, *ret;
559         if (fn != Py_None) {
560                 ret = PyObject_CallFunction(fn, "LL", progress, total);
561                 Py_XDECREF(ret);
562         }
563         PyGILState_Release(state);
564 }
565
566 static PyObject *ra_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
567 {
568         char *kwnames[] = { "url", "progress_cb", "auth", "config",
569                                 "client_string_func", "open_tmp_file_func", "uuid", 
570                                                 NULL };
571         char *uuid = NULL;
572         PyObject *py_url;
573         PyObject *progress_cb = Py_None;
574         AuthObject *auth = (AuthObject *)Py_None;
575         PyObject *config = Py_None;
576         PyObject *client_string_func = Py_None, *open_tmp_file_func = Py_None;
577         RemoteAccessObject *ret;
578         apr_hash_t *config_hash;
579         svn_ra_callbacks2_t *callbacks2;
580         svn_auth_baton_t *auth_baton;
581         svn_error_t *err;
582
583         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOOOOz", kwnames, &py_url, 
584                                                                          &progress_cb, (PyObject **)&auth, &config, 
585                                                                          &client_string_func, &open_tmp_file_func, 
586                                                                          &uuid))
587                 return NULL;
588
589         ret = PyObject_New(RemoteAccessObject, &RemoteAccess_Type);
590         if (ret == NULL)
591                 return NULL;
592
593         ret->client_string_func = client_string_func;
594         ret->open_tmp_file_func = open_tmp_file_func;
595         Py_INCREF(client_string_func);
596
597         Py_INCREF(progress_cb);
598         ret->progress_func = progress_cb;
599
600         ret->auth = NULL;
601         ret->corrected_url = NULL;
602
603         ret->root = NULL;
604         ret->pool = Pool(NULL);
605         if (ret->pool == NULL) {
606                 Py_DECREF(ret);
607                 return NULL;
608         }
609
610         ret->url = py_object_to_svn_uri(py_url, ret->pool);
611         if (ret->url == NULL) {
612                 Py_DECREF(ret);
613                 return NULL;
614         }
615
616         if ((PyObject *)auth == Py_None) {
617                 ret->auth = NULL;
618                 svn_auth_open(&auth_baton, apr_array_make(ret->pool, 0, sizeof(svn_auth_provider_object_t *)), ret->pool);
619         } else if (PyObject_TypeCheck(auth, &Auth_Type)) {
620                 Py_INCREF(auth);
621                 ret->auth = auth;
622                 auth_baton = ret->auth->auth_baton;
623         } else {
624                 PyErr_SetString(PyExc_TypeError, "auth argument is not an Auth object");
625                 Py_DECREF(ret);
626                 return NULL;
627         }
628
629         err = svn_ra_create_callbacks(&callbacks2, ret->pool);
630         if (err != NULL) {
631                 handle_svn_error(err);
632                 svn_error_clear(err);
633                 Py_DECREF(ret);
634                 return NULL;
635         }
636
637         callbacks2->progress_baton = (void *)ret;
638         callbacks2->progress_func = py_progress_func;
639         callbacks2->auth_baton = auth_baton;
640         callbacks2->open_tmp_file = py_open_tmp_file;
641         callbacks2->cancel_func = py_cancel_check;
642 #if ONLY_SINCE_SVN(1, 5)
643         callbacks2->get_client_string = py_get_client_string;
644 #endif
645         config_hash = config_hash_from_object(config, ret->pool);
646         if (config_hash == NULL) {
647                 Py_DECREF(ret);
648                 return NULL;
649         }
650         Py_BEGIN_ALLOW_THREADS
651 #if ONLY_SINCE_SVN(1, 7)
652         err = svn_ra_open4(&ret->ra, &ret->corrected_url, ret->url, uuid,
653                            callbacks2, ret, config_hash, ret->pool);
654 #elif ONLY_SINCE_SVN(1, 5)
655         err = svn_ra_open3(&ret->ra, ret->url, uuid,
656                            callbacks2, ret, config_hash, ret->pool);
657 #else
658         if (uuid != NULL) {
659                 PyErr_SetString(PyExc_TypeError, 
660                         "uuid argument not supported with svn 1.4");
661                 Py_DECREF(ret);
662                 return NULL;
663         }
664         err = svn_ra_open2(&ret->ra, ret->url,
665                            callbacks2, ret, config_hash, ret->pool);
666 #endif
667         Py_END_ALLOW_THREADS
668         if (err != NULL) {
669                 handle_svn_error(err);
670                 svn_error_clear(err);
671                 Py_DECREF(ret);
672                 return NULL;
673         }
674         ret->busy = false;
675         return (PyObject *)ret;
676 }
677
678  /**
679   * Obtain the globally unique identifier for this repository.
680   */
681 static PyObject *ra_get_uuid(PyObject *self)
682 {
683         const char *uuid;
684         RemoteAccessObject *ra = (RemoteAccessObject *)self;
685         PyObject *ret;
686         apr_pool_t *temp_pool;
687
688         if (ra_check_busy(ra))
689                 return NULL;
690
691         temp_pool = Pool(NULL);
692         if (temp_pool == NULL)
693                 return NULL;
694 #if ONLY_SINCE_SVN(1, 5)
695         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_uuid2(ra->ra, &uuid, temp_pool));
696 #else
697         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_uuid(ra->ra, &uuid, temp_pool));
698 #endif
699         ret = PyUnicode_FromString(uuid);
700         apr_pool_destroy(temp_pool);
701         return ret;
702 }
703
704 /** Switch to a different url. */
705 static PyObject *ra_reparent(PyObject *self, PyObject *args)
706 {
707         PyObject *py_url;
708         apr_pool_t *temp_pool;
709         RemoteAccessObject *ra = (RemoteAccessObject *)self;
710
711         if (!PyArg_ParseTuple(args, "O:reparent", &py_url))
712                 return NULL;
713
714         if (ra_check_busy(ra))
715                 return NULL;
716
717         temp_pool = Pool(NULL);
718         if (temp_pool == NULL)
719                 return NULL;
720         ra->url = py_object_to_svn_uri(py_url, ra->pool);
721         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_reparent(ra->ra, ra->url, temp_pool));
722         apr_pool_destroy(temp_pool);
723         Py_RETURN_NONE;
724 }
725
726 /**
727  * Obtain the number of the latest committed revision in the 
728  * connected repository.
729  */
730 static PyObject *ra_get_latest_revnum(PyObject *self)
731 {
732         RemoteAccessObject *ra = (RemoteAccessObject *)self;
733         svn_revnum_t latest_revnum;
734         apr_pool_t *temp_pool;
735         if (ra_check_busy(ra))
736                 return NULL;
737
738         temp_pool = Pool(NULL);
739         if (temp_pool == NULL)
740                 return NULL;
741         RUN_RA_WITH_POOL(temp_pool, ra,
742                                   svn_ra_get_latest_revnum(ra->ra, &latest_revnum, temp_pool));
743         apr_pool_destroy(temp_pool);
744         return PyInt_FromLong(latest_revnum);
745 }
746
747
748 static bool ra_get_log_prepare(RemoteAccessObject *ra, PyObject *paths,
749 bool include_merged_revisions, PyObject *revprops, apr_pool_t **pool,
750 apr_array_header_t **apr_paths, apr_array_header_t **apr_revprops)
751 {
752         if (ra_check_busy(ra))
753                 goto fail_busy;
754
755         *pool = Pool(NULL);
756         if (*pool == NULL)
757                 goto fail_pool;
758         if (paths == Py_None) {
759                 /* The subversion libraries don't behave as expected, 
760                  * so tweak our own parameters a bit. */
761                 *apr_paths = apr_array_make(*pool, 1, sizeof(char *));
762                 APR_ARRAY_PUSH(*apr_paths, char *) = apr_pstrdup(*pool, "");
763         } else if (!path_list_to_apr_array(*pool, paths, apr_paths)) {
764                 goto fail_prep;
765         }
766
767 #if ONLY_BEFORE_SVN(1, 5)
768         if (revprops == Py_None) {
769                 PyErr_SetString(PyExc_NotImplementedError,
770                         "fetching all revision properties not supported");
771                 goto fail_prep;
772         } else if (!PySequence_Check(revprops)) {
773                 PyErr_SetString(PyExc_TypeError, "revprops should be a sequence");
774                 goto fail_prep;
775         } else {
776                 int i;
777                 for (i = 0; i < PySequence_Size(revprops); i++) {
778                         PyObject *n = PySequence_GetItem(revprops, i);
779                         char *ns;
780
781                         ns = py_object_to_svn_string(n, *pool);
782                         if (ns == NULL) {
783                                 goto fail_prep;
784                         }
785
786                         if (strcmp(SVN_PROP_REVISION_LOG, ns) && 
787                                 strcmp(SVN_PROP_REVISION_AUTHOR, ns) &&
788                                 strcmp(SVN_PROP_REVISION_DATE, ns)) {
789                                 PyErr_SetString(PyExc_NotImplementedError, 
790                                                                 "fetching custom revision properties not supported");
791                                 goto fail_prep;
792                         }
793                 }
794         }
795
796         if (include_merged_revisions) {
797                 PyErr_SetString(PyExc_NotImplementedError, 
798                         "include_merged_revisions not supported in Subversion 1.4");
799                 goto fail_prep;
800         }
801 #endif
802
803         if (!string_list_to_apr_array(*pool, revprops, apr_revprops)) {
804                 goto fail_prep;
805         }
806         
807         return true;
808         
809 fail_prep:
810         apr_pool_destroy(*pool);
811 fail_pool:
812         ra->busy = false;
813 fail_busy:
814         return false;
815 }
816
817 static PyObject *ra_get_log(PyObject *self, PyObject *args, PyObject *kwargs)
818 {
819         char *kwnames[] = { "callback", "paths", "start", "end", "limit",
820                 "discover_changed_paths", "strict_node_history", "include_merged_revisions", "revprops", NULL };
821         PyObject *callback, *paths;
822         svn_revnum_t start = 0, end = 0;
823         int limit=0; 
824         bool discover_changed_paths=false, strict_node_history=true,include_merged_revisions=false;
825         RemoteAccessObject *ra = (RemoteAccessObject *)self;
826         PyObject *revprops = Py_None;
827         apr_pool_t *temp_pool;
828         apr_array_header_t *apr_paths;
829         apr_array_header_t *apr_revprops;
830
831         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOll|ibbbO:get_log", kwnames, 
832                                                  &callback, &paths, &start, &end, &limit,
833                                                  &discover_changed_paths, &strict_node_history,
834                                                  &include_merged_revisions, &revprops))
835                 return NULL;
836
837         if (!ra_get_log_prepare(ra, paths, include_merged_revisions,
838         revprops, &temp_pool, &apr_paths, &apr_revprops)) {
839                 return NULL;
840         }
841
842 #if ONLY_SINCE_SVN(1, 5)
843         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_log2(ra->ra, 
844                         apr_paths, start, end, limit,
845                         discover_changed_paths, strict_node_history, 
846                         include_merged_revisions,
847                         apr_revprops,
848                         py_svn_log_entry_receiver, 
849                         callback, temp_pool));
850 #else
851         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_log(ra->ra, 
852                         apr_paths, start, end, limit,
853                         discover_changed_paths, strict_node_history, py_svn_log_wrapper, 
854                         callback, temp_pool));
855 #endif
856         apr_pool_destroy(temp_pool);
857         Py_RETURN_NONE;
858 }
859
860 /**
861  * Obtain the URL of the root of this repository.
862  */
863 static PyObject *ra_get_repos_root(PyObject *self)
864 {
865         RemoteAccessObject *ra = (RemoteAccessObject *)self;
866         const char *root;
867         apr_pool_t *temp_pool;
868
869         if (ra->root == NULL) {
870                 if (ra_check_busy(ra))
871                         return NULL;
872
873                 temp_pool = Pool(NULL);
874                 if (temp_pool == NULL)
875                         return NULL;
876 #if ONLY_SINCE_SVN(1, 5)
877                 RUN_RA_WITH_POOL(temp_pool, ra,
878                                                   svn_ra_get_repos_root2(ra->ra, &root, temp_pool));
879 #else
880                 RUN_RA_WITH_POOL(temp_pool, ra,
881                                                   svn_ra_get_repos_root(ra->ra, &root, temp_pool));
882 #endif
883                 ra->root = svn_uri_canonicalize(root, ra->pool);
884                 apr_pool_destroy(temp_pool);
885         }
886
887         return PyString_FromString(ra->root);
888 }
889
890 /**
891  * Obtain the URL of this repository.
892  */
893 static PyObject *ra_get_url(PyObject *self, void *closure)
894 {
895         const char *url;
896         apr_pool_t *temp_pool;
897         PyObject *r;
898         RemoteAccessObject *ra = (RemoteAccessObject *)self;
899
900         if (ra_check_busy(ra))
901                 return NULL;
902
903 #if ONLY_SINCE_SVN(1, 5)
904         temp_pool = Pool(NULL);
905
906         RUN_RA_WITH_POOL(temp_pool, ra,
907                                                 svn_ra_get_session_url(ra->ra, &url, temp_pool));
908
909         r = PyString_FromString(url);
910
911         apr_pool_destroy(temp_pool);
912
913         return r;
914 #else
915         PyErr_SetString(PyExc_NotImplementedError, 
916                                         "svn_ra_get_session_url only supported for svn >= 1.5");
917         return NULL;
918 #endif
919 }
920
921 static PyObject *ra_do_update(PyObject *self, PyObject *args)
922 {
923         svn_revnum_t revision_to_update_to;
924         char *update_target;
925         bool recurse;
926         PyObject *update_editor;
927         const REPORTER_T *reporter;
928         void *report_baton;
929         svn_error_t *err;
930         apr_pool_t *temp_pool;
931         ReporterObject *ret;
932         RemoteAccessObject *ra = (RemoteAccessObject *)self;
933         svn_boolean_t send_copyfrom_args = FALSE;
934
935         if (!PyArg_ParseTuple(args, "lsbO|b:do_update", &revision_to_update_to, &update_target, &recurse, &update_editor, 
936                                                   &send_copyfrom_args))
937                 return NULL;
938
939         if (ra_check_busy(ra))
940                 return NULL;
941
942         temp_pool = Pool(NULL);
943         if (temp_pool == NULL)
944                 return NULL;
945
946         Py_INCREF(update_editor);
947 #if ONLY_SINCE_SVN(1, 5)
948         Py_BEGIN_ALLOW_THREADS
949         err = svn_ra_do_update2(ra->ra, &reporter, 
950                                                                                                   &report_baton, 
951                                                                                                   revision_to_update_to, 
952                                                                                                   update_target, recurse?svn_depth_infinity:svn_depth_files, 
953                                                                                                   send_copyfrom_args,
954                                                                                                   &py_editor, update_editor, 
955                                                                                                   temp_pool);
956 #else
957         if (send_copyfrom_args) {
958                 PyErr_SetString(PyExc_NotImplementedError, "send_copyfrom_args only supported for svn >= 1.5");
959                 apr_pool_destroy(temp_pool);
960                 return NULL;
961         }
962         Py_BEGIN_ALLOW_THREADS
963         err = svn_ra_do_update(ra->ra, &reporter,
964                 &report_baton, revision_to_update_to,
965                 update_target, recurse,
966                 &py_editor, update_editor,
967                 temp_pool);
968
969 #endif
970         Py_END_ALLOW_THREADS
971         if (err != NULL) {
972                 handle_svn_error(err);
973                 svn_error_clear(err);
974                 apr_pool_destroy(temp_pool);
975                 ra->busy = false;
976                 return NULL;
977         }
978
979         ret = PyObject_New(ReporterObject, &Reporter_Type);
980         if (ret == NULL)
981                 return NULL;
982         ret->reporter = reporter;
983         ret->report_baton = report_baton;
984         ret->pool = temp_pool;
985         Py_INCREF(ra);
986         ret->ra = ra;
987         return (PyObject *)ret;
988 }
989
990 static PyObject *ra_do_switch(PyObject *self, PyObject *args)
991 {
992         RemoteAccessObject *ra = (RemoteAccessObject *)self;
993         svn_revnum_t revision_to_update_to;
994         char *update_target;
995         bool recurse;
996         char *switch_url;
997         PyObject *update_editor;
998         const REPORTER_T *reporter;
999         void *report_baton;
1000         apr_pool_t *temp_pool;
1001         ReporterObject *ret;
1002         svn_error_t *err;
1003
1004         if (!PyArg_ParseTuple(args, "lsbsO:do_switch", &revision_to_update_to, &update_target, 
1005                                                   &recurse, &switch_url, &update_editor))
1006                 return NULL;
1007         if (ra_check_busy(ra))
1008                 return NULL;
1009
1010         temp_pool = Pool(NULL);
1011         if (temp_pool == NULL) {
1012                 ra->busy = false;
1013                 return NULL;
1014         }
1015
1016         Py_INCREF(update_editor);
1017         Py_BEGIN_ALLOW_THREADS
1018
1019 #if ONLY_SINCE_SVN(1, 5)
1020         err = svn_ra_do_switch2(
1021                                                 ra->ra, &reporter, &report_baton, 
1022                                                 revision_to_update_to, update_target, 
1023                                                 recurse?svn_depth_infinity:svn_depth_files, switch_url, &py_editor, 
1024                                                 update_editor, temp_pool);
1025 #else
1026         err = svn_ra_do_switch(
1027                                                 ra->ra, &reporter, &report_baton, 
1028                                                 revision_to_update_to, update_target, 
1029                                                 recurse, switch_url, &py_editor, 
1030                                                 update_editor, temp_pool);
1031 #endif
1032
1033         Py_END_ALLOW_THREADS
1034
1035         if (err != NULL) {
1036                 handle_svn_error(err);
1037                 svn_error_clear(err);
1038                 apr_pool_destroy(temp_pool);
1039                 ra->busy = false;
1040                 return NULL;
1041         }
1042         ret = PyObject_New(ReporterObject, &Reporter_Type);
1043         if (ret == NULL) {
1044                 apr_pool_destroy(temp_pool);
1045                 ra->busy = false;
1046                 return NULL;
1047         }
1048         ret->reporter = reporter;
1049         ret->report_baton = report_baton;
1050         ret->pool = temp_pool;
1051         Py_INCREF(ra);
1052         ret->ra = ra;
1053         return (PyObject *)ret;
1054 }
1055
1056 static PyObject *ra_do_diff(PyObject *self, PyObject *args)
1057 {
1058         svn_revnum_t revision_to_update_to;
1059         char *diff_target, *versus_url; 
1060         PyObject *diff_editor;
1061         const REPORTER_T *reporter;
1062         void *report_baton;
1063         svn_error_t *err;
1064         apr_pool_t *temp_pool;
1065         bool ignore_ancestry = false, text_deltas = false, recurse=true;
1066         ReporterObject *ret;
1067         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1068
1069         if (!PyArg_ParseTuple(args, "lssO|bbb:do_diff", &revision_to_update_to, &diff_target, &versus_url, &diff_editor, &recurse, &ignore_ancestry, &text_deltas))
1070                 return NULL;
1071
1072         if (ra_check_busy(ra))
1073                 return NULL;
1074
1075         temp_pool = Pool(NULL);
1076         if (temp_pool == NULL)
1077                 return NULL;
1078
1079         Py_INCREF(diff_editor);
1080         Py_BEGIN_ALLOW_THREADS
1081 #if ONLY_SINCE_SVN(1, 5)
1082         err = svn_ra_do_diff3(ra->ra, &reporter, &report_baton, 
1083                                                                                                   revision_to_update_to, 
1084                                                                                                   diff_target, recurse?svn_depth_infinity:svn_depth_files,
1085                                                                                                   ignore_ancestry, 
1086                                                                                                   text_deltas,
1087                                                                                                   versus_url,
1088                                                                                                   &py_editor, diff_editor, 
1089                                                                                                   temp_pool);
1090 #else
1091         err = svn_ra_do_diff2(ra->ra, &reporter, &report_baton, 
1092                                                                                                   revision_to_update_to, 
1093                                                                                                   diff_target, recurse,
1094                                                                                                   ignore_ancestry, 
1095                                                                                                   text_deltas,
1096                                                                                                   versus_url,
1097                                                                                                   &py_editor, diff_editor, 
1098                                                                                                   temp_pool);
1099 #endif
1100         Py_END_ALLOW_THREADS
1101         if (err != NULL) {
1102                 handle_svn_error(err);
1103                 svn_error_clear(err);
1104                 apr_pool_destroy(temp_pool);
1105                 ra->busy = false;
1106                 return NULL;
1107         }
1108
1109         ret = PyObject_New(ReporterObject, &Reporter_Type);
1110         if (ret == NULL)
1111                 return NULL;
1112         ret->reporter = reporter;
1113         ret->report_baton = report_baton;
1114         ret->pool = temp_pool;
1115         Py_INCREF(ra);
1116         ret->ra = ra;
1117         return (PyObject *)ret;
1118 }
1119
1120 static PyObject *ra_replay(PyObject *self, PyObject *args)
1121 {
1122         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1123         apr_pool_t *temp_pool;
1124         svn_revnum_t revision, low_water_mark;
1125         PyObject *update_editor;
1126         bool send_deltas = true;
1127
1128         if (!PyArg_ParseTuple(args, "llO|b:replay", &revision, &low_water_mark, &update_editor, &send_deltas))
1129                 return NULL;
1130
1131         if (ra_check_busy(ra))
1132                 return NULL;
1133
1134         temp_pool = Pool(NULL);
1135         if (temp_pool == NULL)
1136                 return NULL;
1137         /* Only INCREF here, py_editor takes care of the DECREF */
1138         Py_INCREF(update_editor); 
1139         RUN_RA_WITH_POOL(temp_pool, ra,
1140                                           svn_ra_replay(ra->ra, revision, low_water_mark,
1141                                                                         send_deltas, &py_editor, update_editor, 
1142                                                                         temp_pool));
1143         apr_pool_destroy(temp_pool);
1144
1145         Py_RETURN_NONE;
1146 }
1147
1148 #if ONLY_SINCE_SVN(1, 5)
1149 static svn_error_t *py_revstart_cb(svn_revnum_t revision, void *replay_baton,
1150    const svn_delta_editor_t **editor, void **edit_baton, apr_hash_t *rev_props, apr_pool_t *pool)
1151 {
1152         PyObject *cbs = (PyObject *)replay_baton;
1153         PyObject *py_start_fn = PyTuple_GetItem(cbs, 0);
1154         PyObject *py_revprops = prop_hash_to_dict(rev_props);
1155         PyObject *ret;
1156         PyGILState_STATE state = PyGILState_Ensure();
1157
1158         ret = PyObject_CallFunction(py_start_fn, "lO", revision, py_revprops);
1159         CB_CHECK_PYRETVAL(ret);
1160
1161         *editor = &py_editor;
1162         *edit_baton = ret;
1163
1164         PyGILState_Release(state);
1165         return NULL;
1166 }
1167
1168 static svn_error_t *py_revfinish_cb(svn_revnum_t revision, void *replay_baton, 
1169                                                                         const svn_delta_editor_t *editor, void *edit_baton, 
1170                                                                         apr_hash_t *rev_props, apr_pool_t *pool)
1171 {
1172         PyObject *cbs = (PyObject *)replay_baton;
1173         PyObject *py_finish_fn = PyTuple_GetItem(cbs, 1);
1174         PyObject *py_revprops = prop_hash_to_dict(rev_props);
1175         PyObject *ret;
1176         PyGILState_STATE state = PyGILState_Ensure();
1177
1178         ret = PyObject_CallFunction(py_finish_fn, "lOO", revision, py_revprops, edit_baton);
1179         CB_CHECK_PYRETVAL(ret);
1180
1181         Py_DECREF((PyObject *)edit_baton);
1182         Py_DECREF(ret);
1183
1184         PyGILState_Release(state);
1185         return NULL;
1186 }
1187 #endif
1188
1189 static PyObject *ra_replay_range(PyObject *self, PyObject *args)
1190 {
1191 #if ONLY_SINCE_SVN(1, 5)
1192         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1193         apr_pool_t *temp_pool;
1194         svn_revnum_t start_revision, end_revision, low_water_mark;
1195         PyObject *cbs;
1196         bool send_deltas = true;
1197
1198         if (!PyArg_ParseTuple(args, "lllO|b:replay_range", &start_revision, &end_revision, &low_water_mark, &cbs, &send_deltas))
1199                 return NULL;
1200
1201         if (!PyTuple_Check(cbs)) {
1202                 PyErr_SetString(PyExc_TypeError, "Expected tuple with callbacks");
1203                 return NULL;
1204         }
1205
1206         if (ra_check_busy(ra))
1207                 return NULL;
1208
1209         temp_pool = Pool(NULL);
1210         if (temp_pool == NULL)
1211                 return NULL;
1212
1213         Py_INCREF(cbs);
1214         RUN_RA_WITH_POOL(temp_pool, ra,
1215                                           svn_ra_replay_range(ra->ra, start_revision, end_revision, low_water_mark,
1216                                                                         send_deltas, py_revstart_cb, py_revfinish_cb, cbs, 
1217                                                                         temp_pool));
1218         apr_pool_destroy(temp_pool);
1219
1220         Py_RETURN_NONE;
1221 #else
1222         PyErr_SetString(PyExc_NotImplementedError, 
1223                 "svn_ra_replay not available with Subversion 1.4");
1224         return NULL;
1225 #endif
1226 }
1227
1228
1229
1230 static PyObject *ra_rev_proplist(PyObject *self, PyObject *args)
1231 {
1232         apr_pool_t *temp_pool;
1233         apr_hash_t *props;
1234         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1235         svn_revnum_t rev;
1236         PyObject *py_props;
1237         if (!PyArg_ParseTuple(args, "l:rev_proplist", &rev))
1238                 return NULL;
1239
1240         if (ra_check_busy(ra))
1241                 return NULL;
1242
1243         temp_pool = Pool(NULL);
1244         if (temp_pool == NULL)
1245                 return NULL;
1246         RUN_RA_WITH_POOL(temp_pool, ra,
1247                                           svn_ra_rev_proplist(ra->ra, rev, &props, temp_pool));
1248         py_props = prop_hash_to_dict(props);
1249         apr_pool_destroy(temp_pool);
1250         return py_props;
1251 }
1252
1253 static PyObject *get_commit_editor(PyObject *self, PyObject *args, PyObject *kwargs)
1254 {
1255         char *kwnames[] = { "revprops", "callback", "lock_tokens", "keep_locks", 
1256                 NULL };
1257         PyObject *revprops, *commit_callback = Py_None, *lock_tokens = Py_None;
1258         bool keep_locks = false;
1259         apr_pool_t *pool;
1260         const svn_delta_editor_t *editor;
1261         void *edit_baton;
1262         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1263         apr_hash_t *hash_lock_tokens;
1264 #if ONLY_SINCE_SVN(1, 5)
1265         apr_hash_t *hash_revprops;
1266 #else
1267         char *log_msg;
1268         PyObject *py_log_msg;
1269 #endif
1270         svn_error_t *err;
1271
1272         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOb:get_commit_editor",
1273                 kwnames, &revprops, &commit_callback, &lock_tokens, &keep_locks))
1274                 return NULL;
1275
1276         pool = Pool(NULL);
1277         if (pool == NULL)
1278                 goto fail_pool;
1279         if (lock_tokens == Py_None) {
1280                 hash_lock_tokens = NULL;
1281         } else {
1282                 Py_ssize_t idx = 0;
1283                 PyObject *k, *v;
1284                 hash_lock_tokens = apr_hash_make(pool);
1285                 while (PyDict_Next(lock_tokens, &idx, &k, &v)) {
1286                         if (!PyBytes_Check(k)) {
1287                                 PyErr_SetString(PyExc_TypeError, "token not bytes");
1288                                 goto fail_prep;
1289                         }
1290                         apr_hash_set(hash_lock_tokens, PyBytes_AsString(k),
1291                                                  PyString_Size(k), PyBytes_AsString(v));
1292                 }
1293         }
1294
1295         if (!PyDict_Check(revprops)) {
1296                 PyErr_SetString(PyExc_TypeError, "Expected dictionary with revision properties");
1297                 goto fail_prep;
1298         }
1299
1300         if (ra_check_busy(ra))
1301                 goto fail_prep;
1302
1303         Py_INCREF(commit_callback);
1304
1305 #if ONLY_SINCE_SVN(1, 5)
1306         hash_revprops = prop_dict_to_hash(pool, revprops);
1307         if (hash_revprops == NULL) {
1308                 goto fail_prep2;
1309         }
1310         Py_BEGIN_ALLOW_THREADS
1311         err = svn_ra_get_commit_editor3(ra->ra, &editor, 
1312                 &edit_baton, 
1313                 hash_revprops, py_commit_callback, 
1314                 commit_callback, hash_lock_tokens, keep_locks, pool);
1315 #else
1316         /* Check that revprops has only one member named SVN_PROP_REVISION_LOG */
1317         if (PyDict_Size(revprops) != 1) {
1318                 PyErr_SetString(PyExc_ValueError, "Only svn:log can be set with Subversion 1.4");
1319                 goto fail_prep2;
1320         }
1321
1322         py_log_msg = PyDict_GetItemString(revprops, SVN_PROP_REVISION_LOG);
1323         if (py_log_msg == NULL) {
1324                 PyErr_SetString(PyExc_ValueError, "Only svn:log can be set with Subversion 1.4.");
1325                 goto fail_prep2;
1326         }
1327
1328         log_msg = py_object_to_svn_string(py_log_msg, pool);
1329         if (log_msg == NULL) {
1330                 goto fail_prep2;
1331         }
1332
1333         Py_BEGIN_ALLOW_THREADS
1334         err = svn_ra_get_commit_editor2(ra->ra, &editor, 
1335                 &edit_baton, 
1336                 log_msg, py_commit_callback, 
1337                 commit_callback, hash_lock_tokens, keep_locks, pool);
1338 #endif
1339         Py_END_ALLOW_THREADS
1340
1341         if (err != NULL) {
1342                 handle_svn_error(err);
1343                 svn_error_clear(err);
1344                 goto fail_prep2;
1345         }
1346
1347         Py_INCREF(ra);
1348         return new_editor_object(NULL, editor, edit_baton, pool, 
1349                           &Editor_Type, ra_done_handler, ra, commit_callback);
1350
1351 fail_prep2:
1352         Py_DECREF(commit_callback);
1353         ra->busy = false;
1354 fail_prep:
1355         apr_pool_destroy(pool);
1356 fail_pool:
1357         return NULL;
1358 }
1359
1360 static PyObject *ra_change_rev_prop(PyObject *self, PyObject *args)
1361 {
1362         svn_revnum_t rev;
1363         char *name;
1364         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1365         char *value;
1366         int vallen;
1367         apr_pool_t *temp_pool;
1368         svn_string_t *val_string;
1369
1370         if (!PyArg_ParseTuple(args, "lss#:change_rev_prop", &rev, &name, &value,
1371                         &vallen))
1372                 return NULL;
1373         if (ra_check_busy(ra))
1374                 return NULL;
1375
1376         temp_pool = Pool(NULL);
1377         if (temp_pool == NULL)
1378                 return NULL;
1379         val_string = svn_string_ncreate(value, vallen, temp_pool);
1380         RUN_RA_WITH_POOL(temp_pool, ra,
1381                                           svn_ra_change_rev_prop(ra->ra, rev, name, val_string, 
1382                                                                                          temp_pool));
1383         apr_pool_destroy(temp_pool);
1384         Py_RETURN_NONE;
1385 }
1386
1387
1388 static PyObject *ra_get_dir(PyObject *self, PyObject *args, PyObject *kwargs)
1389 {
1390         apr_pool_t *temp_pool;
1391         apr_hash_t *dirents;
1392         apr_hash_index_t *idx;
1393         apr_hash_t *props;
1394         svn_revnum_t fetch_rev;
1395         const char *key;
1396         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1397         svn_dirent_t *dirent;
1398         apr_ssize_t klen;
1399         const char *path;
1400         PyObject *py_path;
1401         svn_revnum_t revision = -1;
1402         unsigned int dirent_fields = 0;
1403         PyObject *py_dirents, *py_props;
1404         char *kwnames[] = { "path", "revision", "fields", NULL };
1405
1406         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|lI:get_dir", kwnames,
1407                                      &py_path, &revision, &dirent_fields))
1408                 return NULL;
1409
1410         if (ra_check_busy(ra))
1411                 return NULL;
1412
1413         temp_pool = Pool(NULL);
1414         if (temp_pool == NULL)
1415                 return NULL;
1416
1417         if (revision != SVN_INVALID_REVNUM)
1418                 fetch_rev = revision;
1419
1420         path = py_object_to_svn_relpath(py_path, temp_pool);
1421         if (path == NULL)
1422                 return NULL;
1423
1424         /* Yuck. Subversion doesn't like leading slashes.. */
1425         while (*path == '/') path++;
1426
1427         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_dir2(ra->ra, &dirents, &fetch_rev, &props,
1428                                          path, revision, dirent_fields, temp_pool));
1429
1430         if (dirents == NULL) {
1431                 py_dirents = Py_None;
1432                 Py_INCREF(py_dirents);
1433         } else {
1434                 py_dirents = PyDict_New();
1435                 if (py_dirents == NULL) {
1436                         goto fail;
1437                 }
1438                 idx = apr_hash_first(temp_pool, dirents);
1439                 while (idx != NULL) {
1440                         PyObject *item, *pykey;
1441                         apr_hash_this(idx, (const void **)&key, &klen, (void **)&dirent);
1442                         item = py_dirent(dirent, dirent_fields);
1443                         if (item == NULL) {
1444                                 goto fail_dirents;
1445                         }
1446                         if (key == NULL) {
1447                                 pykey = Py_None;
1448                                 Py_INCREF(pykey);
1449                         } else {
1450                                 pykey = PyString_FromString((char *)key);
1451                         }
1452                         if (PyDict_SetItem(py_dirents, pykey, item) != 0) {
1453                                 Py_DECREF(item);
1454                                 Py_DECREF(pykey);
1455                                 goto fail_dirents;
1456                         }
1457                         Py_DECREF(pykey);
1458                         Py_DECREF(item);
1459                         idx = apr_hash_next(idx);
1460                 }
1461         }
1462
1463         py_props = prop_hash_to_dict(props);
1464         if (py_props == NULL) {
1465                 goto fail_dirents;
1466         }
1467         apr_pool_destroy(temp_pool);
1468         return Py_BuildValue("(NlN)", py_dirents, fetch_rev, py_props);
1469         
1470 fail_dirents:
1471         Py_DECREF(py_dirents);
1472 fail:
1473         apr_pool_destroy(temp_pool);
1474         return NULL;
1475 }
1476
1477 static PyObject *ra_get_file(PyObject *self, PyObject *args)
1478 {
1479         const char *path;
1480         PyObject *py_path;
1481         svn_revnum_t revision = -1;
1482         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1483         apr_hash_t *props;
1484         svn_revnum_t fetch_rev;
1485         PyObject *py_stream, *py_props;
1486         apr_pool_t *temp_pool;
1487
1488         if (!PyArg_ParseTuple(args, "OO|l:get_file", &py_path, &py_stream, &revision))
1489                 return NULL;
1490
1491         if (ra_check_busy(ra))
1492                 return NULL;
1493
1494         temp_pool = Pool(NULL);
1495         if (temp_pool == NULL)
1496                 return NULL;
1497
1498         if (revision != SVN_INVALID_REVNUM)
1499                 fetch_rev = revision;
1500
1501         path = py_object_to_svn_relpath(py_path, temp_pool);
1502         if (path == NULL)
1503                 return NULL;
1504
1505         /* Yuck. Subversion doesn't like leading slashes.. */
1506         while (*path == '/') path++;
1507
1508         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_file(ra->ra, path, revision,
1509                                                                                                         new_py_stream(temp_pool, py_stream),
1510                                                                                                         &fetch_rev, &props, temp_pool));
1511
1512         py_props = prop_hash_to_dict(props);
1513         if (py_props == NULL) {
1514                 apr_pool_destroy(temp_pool);
1515                 return NULL;
1516         }
1517
1518         apr_pool_destroy(temp_pool);
1519
1520         return Py_BuildValue("(lN)", fetch_rev, py_props);
1521 }
1522
1523 static PyObject *ra_get_lock(PyObject *self, PyObject *args)
1524 {
1525         char *path;
1526         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1527         svn_lock_t *lock;
1528         apr_pool_t *temp_pool;
1529
1530         if (!PyArg_ParseTuple(args, "s:get_lock", &path))
1531                 return NULL;
1532
1533         if (ra_check_busy(ra))
1534                 return NULL;
1535
1536         temp_pool = Pool(NULL);
1537         if (temp_pool == NULL)
1538                 return NULL;
1539         RUN_RA_WITH_POOL(temp_pool, ra,
1540                                   svn_ra_get_lock(ra->ra, &lock, path, temp_pool));
1541         apr_pool_destroy(temp_pool);
1542         return wrap_lock(lock);
1543 }
1544
1545 static PyObject *ra_check_path(PyObject *self, PyObject *args)
1546 {
1547         const char *path;
1548         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1549         svn_revnum_t revision;
1550         svn_node_kind_t kind;
1551         PyObject *py_path;
1552         apr_pool_t *temp_pool;
1553
1554         if (!PyArg_ParseTuple(args, "Ol:check_path", &py_path, &revision))
1555                 return NULL;
1556         if (ra_check_busy(ra))
1557                 return NULL;
1558
1559         temp_pool = Pool(NULL);
1560         if (temp_pool == NULL)
1561                 return NULL;
1562
1563         path = py_object_to_svn_relpath(py_path, temp_pool);
1564         if (path == NULL)
1565                 return NULL;
1566
1567         if (ra_check_svn_path(path))
1568                 return NULL;
1569
1570         RUN_RA_WITH_POOL(temp_pool, ra,
1571                                           svn_ra_check_path(ra->ra, path, revision, &kind, 
1572                                          temp_pool));
1573         apr_pool_destroy(temp_pool);
1574         return PyInt_FromLong(kind);
1575 }
1576
1577 static PyObject *ra_stat(PyObject *self, PyObject *args)
1578 {
1579         const char *path;
1580         PyObject *py_path;
1581         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1582         PyObject *ret;
1583         svn_revnum_t revision;
1584         svn_dirent_t *dirent;
1585         apr_pool_t *temp_pool;
1586
1587         if (!PyArg_ParseTuple(args, "Ol:stat", &py_path, &revision))
1588                 return NULL;
1589         if (ra_check_busy(ra))
1590                 return NULL;
1591
1592         temp_pool = Pool(NULL);
1593         if (temp_pool == NULL)
1594                 return NULL;
1595
1596         path = py_object_to_svn_relpath(py_path, temp_pool);
1597         if (path == NULL)
1598                 return NULL;
1599
1600         if (ra_check_svn_path(path))
1601                 return NULL;
1602
1603         RUN_RA_WITH_POOL(temp_pool, ra,
1604                                           svn_ra_stat(ra->ra, path, revision, &dirent,
1605                                          temp_pool));
1606         ret = py_dirent(dirent, SVN_DIRENT_ALL);
1607         apr_pool_destroy(temp_pool);
1608         return ret;
1609 }
1610
1611 static PyObject *ra_has_capability(PyObject *self, PyObject *args)
1612 {
1613 #if ONLY_SINCE_SVN(1, 5)
1614         char *capability;
1615         apr_pool_t *temp_pool;
1616         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1617         int has = 0;
1618
1619         if (!PyArg_ParseTuple(args, "s:has_capability", &capability))
1620                 return NULL;
1621
1622         if (ra_check_busy(ra))
1623                 return NULL;
1624
1625         temp_pool = Pool(NULL);
1626         if (temp_pool == NULL)
1627                 return NULL;
1628         RUN_RA_WITH_POOL(temp_pool, ra,
1629                                           svn_ra_has_capability(ra->ra, &has, capability, temp_pool));
1630         apr_pool_destroy(temp_pool);
1631         return PyBool_FromLong(has);
1632 #else
1633         PyErr_SetString(PyExc_NotImplementedError, "has_capability is only supported in Subversion >= 1.5");
1634         return NULL;
1635 #endif
1636 }
1637
1638 static PyObject *ra_unlock(PyObject *self, PyObject *args)
1639 {
1640         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1641         PyObject *path_tokens, *lock_func, *k, *v;
1642         bool break_lock;
1643         Py_ssize_t idx;
1644         apr_pool_t *temp_pool;
1645         apr_hash_t *hash_path_tokens;
1646
1647         if (!PyArg_ParseTuple(args, "ObO:unlock", &path_tokens, &break_lock, &lock_func))
1648                 goto fail_busy;
1649
1650         if (ra_check_busy(ra))
1651                 goto fail_busy;
1652
1653         temp_pool = Pool(NULL);
1654         if (temp_pool == NULL)
1655                 goto fail_pool;
1656         hash_path_tokens = apr_hash_make(temp_pool);
1657         while (PyDict_Next(path_tokens, &idx, &k, &v)) {
1658                 if (!PyBytes_Check(k)) {
1659                         PyErr_SetString(PyExc_TypeError, "token not bytes");
1660                         goto fail_dict;
1661                 }
1662                 apr_hash_set(hash_path_tokens, PyBytes_AsString(k), PyBytes_Size(k), (char *)PyString_AsString(v));
1663         }
1664         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_unlock(ra->ra, hash_path_tokens, break_lock,
1665                                          py_lock_func, lock_func, temp_pool));
1666
1667         apr_pool_destroy(temp_pool);
1668         Py_RETURN_NONE;
1669
1670 fail_dict:
1671         apr_pool_destroy(temp_pool);
1672 fail_pool:
1673         ra->busy = false;
1674 fail_busy:
1675         return NULL;
1676 }
1677
1678 static PyObject *ra_lock(PyObject *self, PyObject *args)
1679 {
1680         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1681         PyObject *path_revs;
1682         char *comment;
1683         int steal_lock;
1684         PyObject *lock_func, *k, *v;
1685         apr_pool_t *temp_pool;
1686         apr_hash_t *hash_path_revs;
1687         svn_revnum_t *rev;
1688         Py_ssize_t idx = 0;
1689
1690         if (!PyArg_ParseTuple(args, "OsbO:lock", &path_revs, &comment, &steal_lock,
1691                                                   &lock_func))
1692                 goto fail_busy;
1693
1694         if (ra_check_busy(ra))
1695                 goto fail_busy;
1696
1697         temp_pool = Pool(NULL);
1698         if (temp_pool == NULL)
1699                 goto fail_pool;
1700         if (path_revs == Py_None) {
1701                 hash_path_revs = NULL;
1702         } else {
1703                 hash_path_revs = apr_hash_make(temp_pool);
1704         }
1705
1706         while (PyDict_Next(path_revs, &idx, &k, &v)) {
1707                 rev = (svn_revnum_t *)apr_palloc(temp_pool, sizeof(svn_revnum_t));
1708                 *rev = PyInt_AsLong(v);
1709                 if (*rev == -1 && PyErr_Occurred()) {
1710                         goto fail_prep;
1711                 }
1712                 if (!PyBytes_Check(k)) {
1713                         PyErr_SetString(PyExc_TypeError, "token not bytes");
1714                         goto fail_prep;
1715                 }
1716                 apr_hash_set(hash_path_revs, PyBytes_AsString(k), PyBytes_Size(k), rev);
1717         }
1718         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_lock(ra->ra, hash_path_revs, comment, steal_lock,
1719                                          py_lock_func, lock_func, temp_pool));
1720         apr_pool_destroy(temp_pool);
1721         Py_RETURN_NONE;
1722
1723 fail_prep:
1724         apr_pool_destroy(temp_pool);
1725 fail_pool:
1726         ra->busy = false;
1727 fail_busy:
1728         return NULL;
1729 }
1730
1731 static PyObject *ra_get_locks(PyObject *self, PyObject *args)
1732 {
1733         const char *path;
1734         PyObject *py_path;
1735         apr_pool_t *temp_pool;
1736         apr_hash_t *hash_locks;
1737         apr_hash_index_t *idx;
1738         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1739         svn_depth_t depth = svn_depth_infinity;
1740         char *key;
1741         apr_ssize_t klen;
1742         svn_lock_t *lock;
1743         PyObject *ret;
1744
1745         if (!PyArg_ParseTuple(args, "O|i:get_locks", &py_path, &depth))
1746                 return NULL;
1747
1748         if (ra_check_busy(ra))
1749                 return NULL;
1750
1751         temp_pool = Pool(NULL);
1752         if (temp_pool == NULL)
1753                 return NULL;
1754
1755         path = py_object_to_svn_relpath(py_path, temp_pool);
1756         if (path == NULL)
1757                 return NULL;
1758
1759         if (ra_check_svn_path(path))
1760                 return NULL;
1761
1762 #if ONLY_SINCE_SVN(1, 7)
1763         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_locks2(ra->ra, &hash_locks, path, depth, temp_pool));
1764 #else
1765         if (depth != svn_depth_infinity) {
1766                 PyErr_SetString(PyExc_NotImplementedError,
1767                                                 "depth != infinity only supported for svn >= 1.7");
1768                 return NULL;
1769         }
1770
1771         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_locks(ra->ra, &hash_locks, path, temp_pool));
1772 #endif
1773
1774         ret = PyDict_New();
1775         if (ret == NULL) {
1776                 apr_pool_destroy(temp_pool);
1777                 return NULL;
1778         }
1779         for (idx = apr_hash_first(temp_pool, hash_locks); idx != NULL;
1780                  idx = apr_hash_next(idx)) {
1781                 PyObject *pyval;
1782                 apr_hash_this(idx, (const void **)&key, &klen, (void **)&lock);
1783                 pyval = pyify_lock(lock);
1784                 if (pyval == NULL) {
1785                         Py_DECREF(ret);
1786                         apr_pool_destroy(temp_pool);
1787                         return NULL;
1788                 }
1789                 if (PyDict_SetItemString(ret, key, pyval) != 0) {
1790                         apr_pool_destroy(temp_pool);
1791                         Py_DECREF(pyval);
1792                         Py_DECREF(ret);
1793                         return NULL;
1794                 }
1795                 Py_DECREF(pyval);
1796         }
1797
1798         apr_pool_destroy(temp_pool);
1799         return ret;
1800 }
1801
1802 static PyObject *ra_get_locations(PyObject *self, PyObject *args)
1803 {
1804         const char *path;
1805         PyObject *py_path;
1806         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1807         svn_revnum_t peg_revision;
1808         PyObject *location_revisions;
1809         apr_pool_t *temp_pool;
1810         apr_hash_t *hash_locations;
1811         apr_hash_index_t *idx;
1812         svn_revnum_t *key;
1813         PyObject *ret;
1814         apr_ssize_t klen;
1815         char *val;
1816
1817         if (!PyArg_ParseTuple(args, "OlO:get_locations", &py_path, &peg_revision, &location_revisions))
1818                 goto fail_busy;
1819
1820         if (ra_check_busy(ra))
1821                 goto fail_busy;
1822
1823         temp_pool = Pool(NULL);
1824         if (temp_pool == NULL)
1825                 goto fail_pool;
1826
1827         path = py_object_to_svn_relpath(py_path, temp_pool);
1828         if (path == NULL)
1829                 goto fail_dict;
1830
1831         if (ra_check_svn_path(path))
1832                 goto fail_dict;
1833
1834         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_locations(ra->ra, &hash_locations,
1835                                         path, peg_revision,
1836                                         revnum_list_to_apr_array(temp_pool, location_revisions),
1837                                         temp_pool));
1838         ret = PyDict_New();
1839         if (ret == NULL) {
1840                 goto fail_dict;
1841         }
1842
1843         for (idx = apr_hash_first(temp_pool, hash_locations); idx != NULL;
1844                 idx = apr_hash_next(idx)) {
1845                 apr_hash_this(idx, (const void **)&key, &klen, (void **)&val);
1846                 if (PyDict_SetItem(ret, PyInt_FromLong(*key), PyString_FromString(val)) != 0) {
1847                         goto fail_conv;
1848                 }
1849         }
1850         apr_pool_destroy(temp_pool);
1851         return ret;
1852         
1853 fail_conv:
1854         Py_DECREF(ret);
1855 fail_dict:
1856         apr_pool_destroy(temp_pool);
1857 fail_pool:
1858         ra->busy = false;
1859 fail_busy:
1860         return NULL;
1861 }
1862
1863 #if ONLY_SINCE_SVN(1, 5)
1864 static PyObject *range_to_tuple(svn_merge_range_t *range)
1865 {
1866         return Py_BuildValue("(llb)", range->start, range->end, range->inheritable);
1867 }
1868
1869 static PyObject *merge_rangelist_to_list(apr_array_header_t *rangelist)
1870 {
1871         PyObject *ret;
1872         int i;
1873
1874         ret = PyList_New(rangelist->nelts);
1875         if (ret == NULL)
1876                 return NULL;
1877
1878         for (i = 0; i < rangelist->nelts; i++) {
1879                 PyObject *pyval;
1880                 pyval = range_to_tuple(APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *));
1881                 if (pyval == NULL) {
1882                         Py_DECREF(ret);
1883                         return NULL;
1884                 }
1885                 if (PyList_SetItem(ret, i, pyval) != 0) {
1886                         Py_DECREF(ret);
1887                         Py_DECREF(pyval);
1888                         return NULL;
1889                 }
1890         }
1891
1892         return ret;
1893 }
1894
1895 static PyObject *mergeinfo_to_dict(svn_mergeinfo_t mergeinfo, apr_pool_t *temp_pool)
1896 {
1897         PyObject *ret;
1898         char *key;
1899         apr_ssize_t klen;
1900         apr_hash_index_t *idx;
1901         apr_array_header_t *range;
1902
1903         ret = PyDict_New();
1904         if (ret == NULL) {
1905                 return NULL;
1906         }
1907
1908         for (idx = apr_hash_first(temp_pool, mergeinfo); idx != NULL; 
1909                 idx = apr_hash_next(idx)) {
1910                 PyObject *pyval;
1911                 apr_hash_this(idx, (const void **)&key, &klen, (void **)&range);
1912                 pyval = merge_rangelist_to_list(range);
1913                 if (pyval == NULL) {
1914                         Py_DECREF(ret);
1915                         return NULL;
1916                 }
1917                 if (PyDict_SetItemString(ret, key, pyval) != 0) {
1918                         Py_DECREF(ret);
1919                         Py_DECREF(pyval);
1920                         return NULL;
1921                 }
1922                 Py_DECREF(pyval);
1923         }
1924
1925         return ret;
1926 }
1927 #endif
1928
1929 static PyObject *ra_mergeinfo(PyObject *self, PyObject *args)
1930 {
1931 #if ONLY_SINCE_SVN(1, 5)
1932         RemoteAccessObject *ra = (RemoteAccessObject *)self;
1933         apr_array_header_t *apr_paths;
1934         apr_pool_t *temp_pool;
1935         svn_mergeinfo_catalog_t catalog;
1936         apr_ssize_t klen;
1937         apr_hash_index_t *idx;
1938         svn_mergeinfo_t val;
1939         char *key;
1940         PyObject *ret;
1941         svn_revnum_t revision = -1;
1942         PyObject *paths;
1943         svn_mergeinfo_inheritance_t inherit = svn_mergeinfo_explicit;
1944         svn_boolean_t include_descendants;
1945
1946         if (!PyArg_ParseTuple(args, "O|lib:mergeinfo", &paths, &revision, &inherit, &include_descendants))
1947                 return NULL;
1948
1949         temp_pool = Pool(NULL);
1950         if (temp_pool == NULL)
1951                 return NULL;
1952
1953         if (!path_list_to_apr_array(temp_pool, paths, &apr_paths)) {
1954                 apr_pool_destroy(temp_pool);
1955                 return NULL;
1956         }
1957
1958         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_mergeinfo(ra->ra, 
1959                      &catalog, apr_paths, revision, inherit, 
1960                                          include_descendants,
1961                      temp_pool));
1962
1963         ret = PyDict_New();
1964         if (ret == NULL) {
1965                 apr_pool_destroy(temp_pool);
1966                 return NULL;
1967         }
1968
1969         if (catalog != NULL) {
1970                 for (idx = apr_hash_first(temp_pool, catalog); idx != NULL; 
1971                         idx = apr_hash_next(idx)) {
1972                         PyObject *pyval;
1973                         apr_hash_this(idx, (const void **)&key, &klen, (void **)&val);
1974                         pyval = mergeinfo_to_dict(val, temp_pool);
1975                         if (pyval == NULL) {
1976                                 apr_pool_destroy(temp_pool);
1977                                 Py_DECREF(ret);
1978                                 return NULL;
1979                         }
1980                         if (PyDict_SetItemString(ret, key, pyval) != 0) {
1981                                 apr_pool_destroy(temp_pool);
1982                                 Py_DECREF(pyval);
1983                                 Py_DECREF(ret);
1984                                 return NULL;
1985                         }
1986
1987                         Py_DECREF(pyval);
1988                 }
1989         }
1990
1991         apr_pool_destroy(temp_pool);
1992
1993         return ret;
1994 #else
1995         PyErr_SetString(PyExc_NotImplementedError, "mergeinfo is only supported in Subversion >= 1.5");
1996         return NULL;
1997 #endif
1998 }
1999
2000 #if ONLY_SINCE_SVN(1, 5)
2001 static svn_error_t *py_location_segment_receiver(svn_location_segment_t *segment, void *baton, apr_pool_t *pool)
2002 {
2003         PyObject *fn = baton, *ret;
2004         PyGILState_STATE state = PyGILState_Ensure();
2005
2006         ret = PyObject_CallFunction(fn, "llz", segment->range_start, segment->range_end, segment->path);
2007         CB_CHECK_PYRETVAL(ret);
2008         Py_XDECREF(ret);
2009         PyGILState_Release(state);
2010         return NULL;
2011 }
2012 #endif
2013
2014 static PyObject *ra_get_location_segments(PyObject *self, PyObject *args)
2015 {
2016 #if ONLY_SINCE_SVN(1, 5)
2017         RemoteAccessObject *ra = (RemoteAccessObject *)self;
2018         svn_revnum_t peg_revision, start_revision, end_revision;
2019         const char *path;
2020         PyObject *py_path;
2021         PyObject *py_rcvr;
2022         apr_pool_t *temp_pool;
2023
2024         if (!PyArg_ParseTuple(args, "OlllO:get_location_segments", &py_path,
2025                         &peg_revision, &start_revision, &end_revision, &py_rcvr))
2026                 return NULL;
2027
2028         temp_pool = Pool(NULL);
2029         if (temp_pool == NULL)
2030                 return NULL;
2031
2032         path = py_object_to_svn_relpath(py_path, temp_pool);
2033         if (path == NULL)
2034                 return NULL;
2035
2036         if (ra_check_svn_path(path))
2037                 return NULL;
2038
2039         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_location_segments(ra->ra, 
2040                                          path, peg_revision, start_revision, end_revision,
2041                                          py_location_segment_receiver, 
2042                                          py_rcvr, temp_pool));
2043
2044         apr_pool_destroy(temp_pool);
2045         Py_RETURN_NONE;
2046 #else
2047         PyErr_SetString(PyExc_NotImplementedError, "mergeinfo is only supported in Subversion >= 1.5");
2048         return NULL;
2049 #endif
2050 }
2051
2052
2053 static PyObject *ra_get_file_revs(PyObject *self, PyObject *args)
2054 {
2055         char *path;
2056         svn_revnum_t start, end;
2057         PyObject *file_rev_handler;
2058         apr_pool_t *temp_pool;
2059         RemoteAccessObject *ra = (RemoteAccessObject *)self;
2060         svn_boolean_t include_merged_revisions = FALSE;
2061
2062         if (!PyArg_ParseTuple(args, "sllO|b:get_file_revs", &path, &start,
2063                         &end, &file_rev_handler, &include_merged_revisions))
2064                 return NULL;
2065
2066         if (ra_check_svn_path(path))
2067                 return NULL;
2068
2069         if (ra_check_busy(ra))
2070                 return NULL;
2071
2072         temp_pool = Pool(NULL);
2073         if (temp_pool == NULL)
2074                 return NULL;
2075
2076 #if ONLY_SINCE_SVN(1, 5)
2077         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_file_revs2(ra->ra, path, start, end, 
2078                                 include_merged_revisions, 
2079                                 py_file_rev_handler, (void *)file_rev_handler, 
2080                                         temp_pool));
2081 #else
2082         if (include_merged_revisions) {
2083                 PyErr_SetString(PyExc_NotImplementedError, 
2084                                                 "include_merged_revisions only supported with svn >= 1.5");
2085                 apr_pool_destroy(temp_pool);
2086                 return NULL;
2087         }
2088         RUN_RA_WITH_POOL(temp_pool, ra, svn_ra_get_file_revs(ra->ra, path, start, end, 
2089                                 py_ra_file_rev_handler, (void *)file_rev_handler, temp_pool));
2090 #endif
2091
2092         apr_pool_destroy(temp_pool);
2093
2094         Py_RETURN_NONE;
2095 }
2096
2097 static void ra_dealloc(PyObject *self)
2098 {
2099         RemoteAccessObject *ra = (RemoteAccessObject *)self;
2100         Py_XDECREF(ra->client_string_func);
2101         Py_XDECREF(ra->progress_func);
2102         Py_XDECREF(ra->auth);
2103         apr_pool_destroy(ra->pool);
2104         PyObject_Del(self);
2105 }
2106
2107 static PyObject *ra_repr(PyObject *self)
2108 {
2109         RemoteAccessObject *ra = (RemoteAccessObject *)self;
2110         return PyRepr_FromFormat("RemoteAccess(\"%s\")", ra->url);
2111 }
2112
2113 static int ra_set_progress_func(PyObject *self, PyObject *value, void *closure)
2114 {
2115         RemoteAccessObject *ra = (RemoteAccessObject *)self;
2116         Py_XDECREF(ra->progress_func);
2117         ra->progress_func = value;
2118         Py_INCREF(ra->progress_func);
2119         return 0;
2120 }
2121
2122 static PyGetSetDef ra_getsetters[] = { 
2123         { "progress_func", NULL, ra_set_progress_func, NULL },
2124         { NULL }
2125 };
2126
2127 #include "_ra_iter_log.c"
2128
2129 static PyMethodDef ra_methods[] = {
2130         { "get_file_revs", ra_get_file_revs, METH_VARARGS, 
2131                 "S.get_file_revs(path, start_rev, end_revs, handler)" },
2132         { "get_locations", ra_get_locations, METH_VARARGS, 
2133                 "S.get_locations(path, peg_revision, location_revisions)" },
2134         { "get_locks", ra_get_locks, METH_VARARGS, 
2135                 "S.get_locks(path, depth=DEPTH_INFINITY)" },
2136         { "lock", ra_lock, METH_VARARGS, 
2137                 "S.lock(path_revs, comment, steal_lock, lock_func)\n" },
2138         { "unlock", ra_unlock, METH_VARARGS, 
2139                 "S.unlock(path_tokens, break_lock, lock_func)\n" },
2140         { "mergeinfo", ra_mergeinfo, METH_VARARGS, 
2141                 "S.mergeinfo(paths, revision, inherit, include_descendants)\n" },
2142         { "get_location_segments", ra_get_location_segments, METH_VARARGS, 
2143                 "S.get_location_segments(path, peg_revision, start_revision, "
2144                         "end_revision, rcvr)\n"
2145                 "The receiver is called as rcvr(range_start, range_end, path)\n"
2146         },
2147         { "has_capability", ra_has_capability, METH_VARARGS, 
2148                 "S.has_capability(name) -> bool\n"
2149                 "Check whether the specified capability is supported by the client and server" },
2150         { "check_path", ra_check_path, METH_VARARGS, 
2151                 "S.check_path(path, revnum) -> node_kind\n"
2152                 "Check the type of a path (one of NODE_DIR, NODE_FILE, NODE_UNKNOWN)" },
2153         { "stat", ra_stat, METH_VARARGS,
2154                 "S.stat(path, revnum) -> dirent\n" },
2155         { "get_lock", ra_get_lock, METH_VARARGS, 
2156                 "S.get_lock(path) -> lock\n"
2157         },
2158         { "get_dir", (PyCFunction)ra_get_dir, METH_VARARGS|METH_KEYWORDS, 
2159                 "S.get_dir(path, revision, dirent_fields=-1) -> (dirents, fetched_rev, properties)\n"
2160                 "Get the contents of a directory. "},
2161         { "get_file", ra_get_file, METH_VARARGS, 
2162                 "S.get_file(path, stream, revnum=-1) -> (fetched_rev, properties)\n"
2163                 "Fetch a file. The contents will be written to stream." },
2164         { "change_rev_prop", ra_change_rev_prop, METH_VARARGS, 
2165                 "S.change_rev_prop(revnum, name, value)\n"
2166                 "Change a revision property" },
2167         { "get_commit_editor", (PyCFunction)get_commit_editor, METH_VARARGS|METH_KEYWORDS, 
2168                 "S.get_commit_editor(revprops, commit_callback, lock_tokens, keep_locks) -> editor\n"
2169         },
2170         { "rev_proplist", ra_rev_proplist, METH_VARARGS, 
2171                 "S.rev_proplist(revnum) -> properties\n"
2172                 "Return a dictionary with the properties set on the specified revision" },
2173         { "replay", ra_replay, METH_VARARGS, 
2174                 "S.replay(revision, low_water_mark, update_editor, send_deltas=True)\n" 
2175                 "Replay a revision, reporting changes to update_editor." },
2176         { "replay_range", ra_replay_range, METH_VARARGS, 
2177                 "S.replay_range(start_rev, end_rev, low_water_mark, cbs, send_deltas=True)\n"
2178                 "Replay a range of revisions, reporting them to an update editor.\n"
2179                 "cbs is a two-tuple with two callbacks:\n"
2180                 "- start_rev_cb(revision, revprops) -> editor\n"
2181                 "- finish_rev_cb(revision, revprops, editor)\n"
2182         },
2183         { "do_switch", ra_do_switch, METH_VARARGS, 
2184                 "S.do_switch(revision_to_update_to, update_target, recurse, switch_url, update_editor)\n" },
2185         { "do_update", ra_do_update, METH_VARARGS, 
2186                 "S.do_update(revision_to_update_to, update_target, recurse, update_editor)\n" },
2187         { "do_diff", ra_do_diff, METH_VARARGS, 
2188                 "S.do_diff(revision_to_update_to, diff_target, versus_url, diff_editor, recurse, ignore_ancestry, text_deltas) -> Reporter object\n"
2189         },
2190         { "get_repos_root", (PyCFunction)ra_get_repos_root, METH_NOARGS, 
2191                 "S.get_repos_root() -> url\n"
2192                 "Return the URL to the root of the repository." },
2193         { "get_url", (PyCFunction)ra_get_url, METH_NOARGS,
2194                 "S.get_url() -> url\n"
2195                 "Return the URL of the repository." },
2196         { "get_log", (PyCFunction)ra_get_log, METH_VARARGS|METH_KEYWORDS, 
2197                 "S.get_log(callback, paths, start, end, limit=0, "
2198                 "discover_changed_paths=False, strict_node_history=True, "
2199                 "include_merged_revisions=False, revprops=None)\n"
2200                 "The callback is passed three or four arguments:\n"
2201                 "callback(changed_paths, revision, revprops[, has_children])\n"
2202                 "The changed_paths argument may be None, or a dictionary mapping each\n"
2203                 "path to a tuple:\n"
2204                 "(action, from_path, from_rev)\n"
2205         },
2206         { "iter_log", (PyCFunction)ra_iter_log, METH_VARARGS|METH_KEYWORDS, 
2207                 "S.iter_log(paths, start, end, limit=0, "
2208                 "discover_changed_paths=False, strict_node_history=True, "
2209                 "include_merged_revisions=False, revprops=None)\n"
2210                 "Yields tuples of three or four elements:\n"
2211                 "(changed_paths, revision, revprops[, has_children])\n"
2212                 "The changed_paths element may be None, or a dictionary mapping each\n"
2213                 "path to a tuple:\n"
2214                 "(action, from_path, from_rev, node_kind)\n"
2215                 "This method collects the log entries in another thread. Before calling\n"
2216                 "any further methods, make sure the thread has completed by running the\n"
2217                 "iterator to exhaustion (i.e. until StopIteration is raised, the \"for\"\n"
2218                 "loop finishes, etc).\n"
2219         },
2220         { "get_latest_revnum", (PyCFunction)ra_get_latest_revnum, METH_NOARGS, 
2221                 "S.get_latest_revnum() -> int\n"
2222                 "Return the last revision committed in the repository." },
2223         { "reparent", ra_reparent, METH_VARARGS, 
2224                 "S.reparent(url)\n"
2225                 "Reparent to a new URL" },
2226         { "get_uuid", (PyCFunction)ra_get_uuid, METH_NOARGS, 
2227                 "S.get_uuid() -> uuid\n"
2228                 "Return the UUID of the repository." },
2229         { NULL, }
2230 };
2231
2232 static PyMemberDef ra_members[] = {
2233         { "busy", T_BYTE, offsetof(RemoteAccessObject, busy), READONLY, 
2234                 "Whether this connection is in use at the moment" },
2235         { "url", T_STRING, offsetof(RemoteAccessObject, url), READONLY, 
2236                 "URL this connection is to" },
2237         { "corrected_url", T_STRING, offsetof(RemoteAccessObject, corrected_url), READONLY,
2238                 "Corrected URL" },
2239         { NULL, }
2240 };
2241
2242 static PyTypeObject RemoteAccess_Type = {
2243         PyVarObject_HEAD_INIT(NULL, 0)
2244         "_ra.RemoteAccess", /*  const char *tp_name;  For printing, in format "<module>.<name>" */
2245         sizeof(RemoteAccessObject), 
2246         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
2247
2248         /* Methods to implement standard operations */
2249
2250         ra_dealloc, /*  destructor tp_dealloc;  */
2251         NULL, /*        printfunc tp_print;     */
2252         NULL, /*        getattrfunc tp_getattr; */
2253         NULL, /*        setattrfunc tp_setattr; */
2254         NULL, /*        cmpfunc tp_compare;     */
2255         ra_repr, /*     reprfunc tp_repr;       */
2256
2257         /* Method suites for standard classes */
2258
2259         NULL, /*        PyNumberMethods *tp_as_number;  */
2260         NULL, /*        PySequenceMethods *tp_as_sequence;      */
2261         NULL, /*        PyMappingMethods *tp_as_mapping;        */
2262
2263         /* More standard operations (here for binary compatibility) */
2264
2265         NULL, /*        hashfunc tp_hash;       */
2266         NULL, /*        ternaryfunc tp_call;    */
2267         NULL, /*        reprfunc tp_str;        */
2268         NULL, /*        getattrofunc tp_getattro;       */
2269         NULL, /*        setattrofunc tp_setattro;       */
2270
2271         /* Functions to access object as input/output buffer */
2272         NULL, /*        PyBufferProcs *tp_as_buffer;    */
2273
2274         /* Flags to define presence of optional/expanded features */
2275         Py_TPFLAGS_BASETYPE, /* long tp_flags;  */
2276
2277         NULL, /*        const char *tp_doc;  Documentation string */
2278
2279         /* Assigned meaning in release 2.0 */
2280         /* call function for all accessible objects */
2281         NULL, /*        traverseproc tp_traverse;       */
2282
2283         /* delete references to contained objects */
2284         NULL, /*        inquiry tp_clear;       */
2285
2286         /* Assigned meaning in release 2.1 */
2287         /* rich comparisons */
2288         NULL, /*        richcmpfunc tp_richcompare;     */
2289
2290         /* weak reference enabler */
2291         0, /*   Py_ssize_t tp_weaklistoffset;   */
2292
2293         /* Added in release 2.2 */
2294         /* Iterators */
2295         NULL, /*        getiterfunc tp_iter;    */
2296         NULL, /*        iternextfunc tp_iternext;       */
2297
2298         /* Attribute descriptor and subclassing stuff */
2299         ra_methods, /*  struct PyMethodDef *tp_methods; */
2300         ra_members, /*  struct PyMemberDef *tp_members; */
2301         ra_getsetters, /*       struct PyGetSetDef *tp_getset;  */
2302         NULL, /*        struct _typeobject *tp_base;    */
2303         NULL, /*        PyObject *tp_dict;      */
2304         NULL, /*        descrgetfunc tp_descr_get;      */
2305         NULL, /*        descrsetfunc tp_descr_set;      */
2306         0, /*   Py_ssize_t tp_dictoffset;       */
2307         NULL, /*        initproc tp_init;       */
2308         NULL, /*        allocfunc tp_alloc;     */
2309         ra_new, /*      newfunc tp_new; */
2310
2311 };
2312
2313 typedef struct {
2314         PyObject_VAR_HEAD
2315         apr_pool_t *pool;
2316         svn_auth_provider_object_t *provider;
2317         PyObject *callback;
2318 } AuthProviderObject;
2319
2320 static void auth_provider_dealloc(PyObject *self)
2321 {
2322         AuthProviderObject *auth_provider = (AuthProviderObject *)self;
2323         Py_XDECREF(auth_provider->callback);
2324         auth_provider->callback = NULL;
2325         apr_pool_destroy(auth_provider->pool);
2326         PyObject_Del(self);
2327 }
2328
2329 static PyTypeObject AuthProvider_Type = {
2330         PyVarObject_HEAD_INIT(NULL, 0)
2331         "_ra.AuthProvider", /*  const char *tp_name;  For printing, in format "<module>.<name>" */
2332         sizeof(AuthProviderObject),
2333         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
2334
2335         /* Methods to implement standard operations */
2336
2337         auth_provider_dealloc, /*       destructor tp_dealloc;  */
2338
2339 };
2340
2341 static PyObject *auth_init(PyTypeObject *type, PyObject *args, PyObject *kwargs)
2342 {
2343         char *kwnames[] = { "providers", NULL };
2344         apr_array_header_t *c_providers;
2345         svn_auth_provider_object_t **el;
2346         PyObject *providers;
2347         AuthObject *ret;
2348         int i;
2349
2350         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwnames, &providers))
2351                 return NULL;
2352
2353         ret = PyObject_New(AuthObject, &Auth_Type);
2354         if (ret == NULL)
2355                 return NULL;
2356
2357         ret->providers = NULL;
2358
2359         ret->pool = Pool(NULL);
2360         if (ret->pool == NULL) {
2361                 PyErr_NoMemory();
2362                 Py_DECREF(ret);
2363                 return NULL;
2364         }
2365
2366         if (!PySequence_Check(providers)) {
2367                 PyErr_SetString(PyExc_TypeError, "Auth providers should be a sequence");
2368                 Py_DECREF(ret);
2369                 return NULL;
2370         }
2371
2372         Py_INCREF(providers);
2373         ret->providers = providers;
2374
2375         c_providers = apr_array_make(ret->pool, PySequence_Size(providers),
2376                                                                  sizeof(svn_auth_provider_object_t *));
2377         if (c_providers == NULL) {
2378                 PyErr_NoMemory();
2379                 Py_DECREF(ret);
2380                 return NULL;
2381         }
2382         for (i = 0; i < PySequence_Size(providers); i++) {
2383                 AuthProviderObject *provider;
2384                 el = (svn_auth_provider_object_t **)apr_array_push(c_providers);
2385                 provider = (AuthProviderObject *)PySequence_GetItem(providers, i);
2386                 if (!PyObject_TypeCheck(provider, &AuthProvider_Type)) {
2387                         PyErr_SetString(PyExc_TypeError, "Invalid auth provider");
2388                         Py_DECREF(ret);
2389                         return NULL;
2390                 }
2391                 *el = provider->provider;
2392         }
2393         svn_auth_open(&ret->auth_baton, c_providers, ret->pool);
2394         return (PyObject *)ret;
2395 }
2396
2397 static PyObject *auth_set_parameter(PyObject *self, PyObject *args)
2398 {
2399         AuthObject *auth = (AuthObject *)self;
2400         char *name;
2401         PyObject *value;
2402         void *vvalue;
2403         if (!PyArg_ParseTuple(args, "sO:set_parameter", &name, &value))
2404                 return NULL;
2405
2406         if (!strcmp(name, SVN_AUTH_PARAM_SSL_SERVER_FAILURES)) {
2407                 long ret = PyInt_AsLong(value);
2408                 if (ret == -1 && PyErr_Occurred())
2409                         return NULL;
2410                 vvalue = apr_pcalloc(auth->pool, sizeof(apr_uint32_t));
2411                 *((apr_uint32_t *)vvalue) = ret;
2412         } else if (!strcmp(name, SVN_AUTH_PARAM_DEFAULT_USERNAME) ||
2413                            !strcmp(name, SVN_AUTH_PARAM_DEFAULT_PASSWORD)) {
2414                 vvalue = py_object_to_svn_string(value, auth->pool);
2415                 if (vvalue == NULL) {
2416                         return NULL;
2417                 }
2418         } else {
2419                 PyErr_Format(PyExc_TypeError, "Unsupported auth parameter %s", name);
2420                 return NULL;
2421         }
2422
2423         svn_auth_set_parameter(auth->auth_baton, name, (char *)vvalue);
2424
2425         Py_RETURN_NONE;
2426 }
2427
2428 static PyObject *auth_get_parameter(PyObject *self, PyObject *args)
2429 {
2430         char *name;
2431         const void *value;
2432         AuthObject *auth = (AuthObject *)self;
2433
2434         if (!PyArg_ParseTuple(args, "s:get_parameter", &name))
2435                 return NULL;
2436
2437         value = svn_auth_get_parameter(auth->auth_baton, name);
2438
2439         if (!strcmp(name, SVN_AUTH_PARAM_SSL_SERVER_FAILURES)) {
2440                 return PyInt_FromLong(*((apr_uint32_t *)value));
2441         } else if (!strcmp(name, SVN_AUTH_PARAM_DEFAULT_USERNAME) ||
2442                            !strcmp(name, SVN_AUTH_PARAM_DEFAULT_PASSWORD)) {
2443                 return PyString_FromString((const char *)value);
2444         } else {
2445                 PyErr_Format(PyExc_TypeError, "Unsupported auth parameter %s", name);
2446                 return NULL;
2447         }
2448 }
2449
2450 typedef struct {
2451         PyObject_VAR_HEAD
2452         apr_pool_t *pool;
2453         char *cred_kind;
2454         svn_auth_iterstate_t *state;
2455         void *credentials;
2456 } CredentialsIterObject;
2457
2458 static PyObject *auth_first_credentials(PyObject *self, PyObject *args)
2459 {
2460         char *cred_kind;
2461         char *realmstring;
2462         AuthObject *auth = (AuthObject *)self;
2463         void *creds;
2464         apr_pool_t *pool;
2465         CredentialsIterObject *ret;
2466         svn_auth_iterstate_t *state;
2467
2468         if (!PyArg_ParseTuple(args, "ss:credentials", &cred_kind, &realmstring))
2469                 return NULL;
2470
2471         pool = Pool(NULL);
2472         if (pool == NULL)
2473                 return NULL;
2474
2475         RUN_SVN_WITH_POOL(pool,
2476                                           svn_auth_first_credentials(&creds, &state, cred_kind, realmstring, auth->auth_baton, pool));
2477
2478         ret = PyObject_New(CredentialsIterObject, &CredentialsIter_Type);
2479         if (ret == NULL)
2480                 return NULL;
2481
2482         ret->pool = pool;
2483         ret->cred_kind = apr_pstrdup(pool, cred_kind);
2484         ret->state = state;
2485         ret->credentials = creds;
2486
2487         return (PyObject *)ret;
2488 }
2489
2490 static void credentials_iter_dealloc(PyObject *self)
2491 {
2492         CredentialsIterObject *credsiter = (CredentialsIterObject *)self;
2493         apr_pool_destroy(credsiter->pool);
2494         PyObject_Del(self);
2495 }
2496
2497 static PyObject *credentials_iter_next(CredentialsIterObject *iterator)
2498 {
2499         PyObject *ret;
2500
2501         if (iterator->credentials == NULL) {
2502                 PyErr_SetString(PyExc_StopIteration, "No more credentials available");
2503                 return NULL;
2504         }
2505
2506         if (!strcmp(iterator->cred_kind, SVN_AUTH_CRED_SIMPLE)) {
2507                 svn_auth_cred_simple_t *simple = iterator->credentials;
2508                 ret = Py_BuildValue("(zzb)", simple->username, simple->password, simple->may_save);
2509         } else if (!strcmp(iterator->cred_kind, SVN_AUTH_CRED_USERNAME)) {
2510                 svn_auth_cred_username_t *uname = iterator->credentials;
2511                 ret = Py_BuildValue("(zb)", uname->username, uname->may_save);
2512         } else if (!strcmp(iterator->cred_kind, SVN_AUTH_CRED_SSL_CLIENT_CERT)) {
2513                 svn_auth_cred_ssl_client_cert_t *ccert = iterator->credentials;
2514                 ret = Py_BuildValue("(zb)", ccert->cert_file, ccert->may_save);
2515         } else if (!strcmp(iterator->cred_kind, SVN_AUTH_CRED_SSL_CLIENT_CERT_PW)) {
2516                 svn_auth_cred_ssl_client_cert_pw_t *ccert = iterator->credentials;
2517                 ret = Py_BuildValue("(zb)", ccert->password, ccert->may_save);
2518         } else if (!strcmp(iterator->cred_kind, SVN_AUTH_CRED_SSL_SERVER_TRUST)) {
2519                 svn_auth_cred_ssl_server_trust_t *ccert = iterator->credentials;
2520                 ret = Py_BuildValue("(ib)", ccert->accepted_failures, ccert->may_save);
2521         } else {
2522                 PyErr_Format(PyExc_RuntimeError, "Unknown cred kind %s", iterator->cred_kind);
2523                 return NULL;
2524         }
2525
2526         RUN_SVN_WITH_POOL(iterator->pool,
2527                                           svn_auth_next_credentials(&iterator->credentials, iterator->state, iterator->pool));
2528
2529         return ret;
2530 }
2531
2532 static PyTypeObject CredentialsIter_Type = {
2533         PyVarObject_HEAD_INIT(NULL, 0)
2534         "_ra.CredentialsIter", /*       const char *tp_name;  For printing, in format "<module>.<name>" */
2535         sizeof(CredentialsIterObject),
2536         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
2537
2538         /* Methods to implement standard operations */
2539
2540         (destructor)credentials_iter_dealloc, /*        destructor tp_dealloc;  */
2541         NULL, /*        printfunc tp_print;     */
2542         NULL, /*        getattrfunc tp_getattr; */
2543         NULL, /*        setattrfunc tp_setattr; */
2544         NULL, /*        cmpfunc tp_compare;     */
2545         NULL, /*        reprfunc tp_repr;       */
2546
2547         /* Method suites for standard classes */
2548
2549         NULL, /*        PyNumberMethods *tp_as_number;  */
2550         NULL, /*        PySequenceMethods *tp_as_sequence;      */
2551         NULL, /*        PyMappingMethods *tp_as_mapping;        */
2552
2553         /* More standard operations (here for binary compatibility) */
2554
2555         NULL, /*        hashfunc tp_hash;       */
2556         NULL, /*        ternaryfunc tp_call;    */
2557         NULL, /*        reprfunc tp_str;        */
2558         NULL, /*        getattrofunc tp_getattro;       */
2559         NULL, /*        setattrofunc tp_setattro;       */
2560
2561         /* Functions to access object as input/output buffer */
2562         NULL, /*        PyBufferProcs *tp_as_buffer;    */
2563
2564         /* Flags to define presence of optional/expanded features */
2565         Py_TPFLAGS_HAVE_ITER, /*        long tp_flags;  */
2566
2567         NULL, /*        const char *tp_doc;  Documentation string */
2568
2569         /* Assigned meaning in release 2.0 */
2570         /* call function for all accessible objects */
2571         NULL, /*        traverseproc tp_traverse;       */
2572
2573         /* delete references to contained objects */
2574         NULL, /*        inquiry tp_clear;       */
2575
2576         /* Assigned meaning in release 2.1 */
2577         /* rich comparisons */
2578         NULL, /*        richcmpfunc tp_richcompare;     */
2579
2580         /* weak reference enabler */
2581         0, /*   Py_ssize_t tp_weaklistoffset;   */
2582
2583         /* Added in release 2.2 */
2584         /* Iterators */
2585         NULL, /*        getiterfunc tp_iter;    */
2586         (iternextfunc)credentials_iter_next, /* iternextfunc tp_iternext;       */
2587
2588 };
2589
2590 static PyMethodDef auth_methods[] = {
2591         { "set_parameter", auth_set_parameter, METH_VARARGS,
2592                 "S.set_parameter(key, value)\n"
2593                 "Set a parameter" },
2594         { "get_parameter", auth_get_parameter, METH_VARARGS,
2595                 "S.get_parameter(key) -> value\n"
2596                 "Get a parameter" },
2597         { "credentials", auth_first_credentials, METH_VARARGS,
2598                 "Credentials" },
2599         { NULL, }
2600 };
2601
2602 static void auth_dealloc(PyObject *self)
2603 {
2604         AuthObject *auth = (AuthObject *)self;
2605         apr_pool_destroy(auth->pool);
2606         Py_XDECREF(auth->providers);
2607         PyObject_Del(auth);
2608 }
2609
2610 static PyTypeObject Auth_Type = {
2611         PyVarObject_HEAD_INIT(NULL, 0)
2612         "_ra.Auth", /*  const char *tp_name;  For printing, in format "<module>.<name>" */
2613         sizeof(AuthObject),
2614         0,/*    Py_ssize_t tp_basicsize, tp_itemsize;  For allocation */
2615
2616         /* Methods to implement standard operations */
2617
2618         auth_dealloc, /*        destructor tp_dealloc;  */
2619         NULL, /*        printfunc tp_print;     */
2620         NULL, /*        getattrfunc tp_getattr; */
2621         NULL, /*        setattrfunc tp_setattr; */
2622         NULL, /*        cmpfunc tp_compare;     */
2623         NULL, /*        reprfunc tp_repr;       */
2624
2625         /* Method suites for standard classes */
2626
2627         NULL, /*        PyNumberMethods *tp_as_number;  */
2628         NULL, /*        PySequenceMethods *tp_as_sequence;      */
2629         NULL, /*        PyMappingMethods *tp_as_mapping;        */
2630
2631         /* More standard operations (here for binary compatibility) */
2632
2633         NULL, /*        hashfunc tp_hash;       */
2634         NULL, /*        ternaryfunc tp_call;    */
2635         NULL, /*        reprfunc tp_str;        */
2636         NULL, /*        getattrofunc tp_getattro;       */
2637         NULL, /*        setattrofunc tp_setattro;       */
2638
2639         /* Functions to access object as input/output buffer */
2640         NULL, /*        PyBufferProcs *tp_as_buffer;    */
2641
2642         /* Flags to define presence of optional/expanded features */
2643         0, /*   long tp_flags;  */
2644
2645         NULL, /*        const char *tp_doc;  Documentation string */
2646
2647         /* Assigned meaning in release 2.0 */
2648         /* call function for all accessible objects */
2649         NULL, /*        traverseproc tp_traverse;       */
2650
2651         /* delete references to contained objects */
2652         NULL, /*        inquiry tp_clear;       */
2653
2654         /* Assigned meaning in release 2.1 */
2655         /* rich comparisons */
2656         NULL, /*        richcmpfunc tp_richcompare;     */
2657
2658         /* weak reference enabler */
2659         0, /*   Py_ssize_t tp_weaklistoffset;   */
2660
2661         /* Added in release 2.2 */
2662         /* Iterators */
2663         NULL, /*        getiterfunc tp_iter;    */
2664         NULL, /*        iternextfunc tp_iternext;       */
2665
2666         /* Attribute descriptor and subclassing stuff */
2667         auth_methods, /*        struct PyMethodDef *tp_methods; */
2668         NULL, /*        struct PyMemberDef *tp_members; */
2669         NULL, /*        struct PyGetSetDef *tp_getset;  */
2670         NULL, /*        struct _typeobject *tp_base;    */
2671         NULL, /*        PyObject *tp_dict;      */
2672         NULL, /*        descrgetfunc tp_descr_get;      */
2673         NULL, /*        descrsetfunc tp_descr_set;      */
2674         0, /*   Py_ssize_t tp_dictoffset;       */
2675         NULL, /*        initproc tp_init;       */
2676         NULL, /*        allocfunc tp_alloc;     */
2677         auth_init, /*   newfunc tp_new; */
2678
2679 };
2680
2681 static svn_error_t *py_username_prompt(svn_auth_cred_username_t **cred, void *baton, const char *realm, int may_save, apr_pool_t *pool)
2682 {
2683         PyObject *fn = (PyObject *)baton, *ret;
2684         PyObject *py_username, *py_may_save;
2685         char *username;
2686         PyGILState_STATE state = PyGILState_Ensure();
2687         ret = PyObject_CallFunction(fn, "sb", realm, may_save);
2688         CB_CHECK_PYRETVAL(ret);
2689
2690         if (ret == Py_None) {
2691                 Py_DECREF(ret);
2692                 PyGILState_Release(state);
2693                 return NULL;
2694         }
2695
2696         if (!PyTuple_Check(ret)) {
2697                 PyErr_SetString(PyExc_TypeError, "expected tuple with username credentials");
2698                 goto fail;
2699         }
2700
2701         if (PyTuple_Size(ret) != 2) {
2702                 PyErr_SetString(PyExc_TypeError, "expected tuple with username credentials to be size 2");
2703                 goto fail;
2704         }
2705
2706         py_may_save = PyTuple_GetItem(ret, 1);
2707         CB_CHECK_PYRETVAL(py_may_save);
2708         if (!PyBool_Check(py_may_save)) {
2709                 PyErr_SetString(PyExc_TypeError, "may_save should be boolean");
2710                 goto fail;
2711         }
2712         py_username = PyTuple_GetItem(ret, 0);
2713         CB_CHECK_PYRETVAL(py_username);
2714         username = py_object_to_svn_string(py_username, pool);
2715         if (username == NULL) {
2716                 goto fail;
2717         }
2718
2719         *cred = apr_pcalloc(pool, sizeof(**cred));
2720         (*cred)->username = username;
2721         (*cred)->may_save = (py_may_save == Py_True);
2722         Py_DECREF(ret);
2723         PyGILState_Release(state);
2724         return NULL;
2725
2726 fail:
2727         Py_DECREF(ret);
2728         PyGILState_Release(state);
2729         return py_svn_error();
2730 }
2731
2732 static PyObject *get_username_prompt_provider(PyObject *self, PyObject *args)
2733 {
2734         AuthProviderObject *auth;
2735         PyObject *prompt_func;
2736         int retry_limit;
2737         if (!PyArg_ParseTuple(args, "Oi:get_username_prompt_provider",
2738                           &prompt_func, &retry_limit))
2739                 return NULL;
2740         auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
2741         if (auth == NULL)
2742                 return NULL;
2743         auth->pool = Pool(NULL);
2744         if (auth->pool == NULL)
2745                 return NULL;
2746         Py_INCREF(prompt_func);
2747         auth->callback = prompt_func;
2748         svn_auth_get_username_prompt_provider(&auth->provider, py_username_prompt,
2749                  (void *)prompt_func, retry_limit, auth->pool);
2750         return (PyObject *)auth;
2751 }
2752
2753 static svn_error_t *py_simple_prompt(svn_auth_cred_simple_t **cred, void *baton, const char *realm, const char *username, int may_save, apr_pool_t *pool)
2754 {
2755         PyObject *fn = (PyObject *)baton, *ret;
2756         PyObject *py_may_save, *py_username, *py_password;
2757         char *ret_username, *password;
2758         PyGILState_STATE state = PyGILState_Ensure();
2759         ret = PyObject_CallFunction(fn, "ssb", realm, username, may_save);
2760         CB_CHECK_PYRETVAL(ret);
2761         if (!PyTuple_Check(ret)) {
2762                 PyErr_SetString(PyExc_TypeError, "expected tuple with simple credentials");
2763                 goto fail;
2764         }
2765         if (PyTuple_Size(ret) != 3) {
2766                 PyErr_SetString(PyExc_TypeError, "expected tuple of size 3");
2767                 goto fail;
2768         }
2769
2770         py_may_save = PyTuple_GetItem(ret, 2);
2771         CB_CHECK_PYRETVAL(py_may_save);
2772
2773         if (!PyBool_Check(py_may_save)) {
2774                 PyErr_SetString(PyExc_TypeError, "may_save should be boolean");
2775                 goto fail;
2776         }
2777
2778         py_username = PyTuple_GetItem(ret, 0);
2779         CB_CHECK_PYRETVAL(py_username);
2780         ret_username = py_object_to_svn_string(py_username, pool);
2781         if (ret_username == NULL) {
2782                 goto fail;
2783         }
2784
2785         py_password = PyTuple_GetItem(ret, 1);
2786         CB_CHECK_PYRETVAL(py_password);
2787         password = py_object_to_svn_string(py_password, pool);
2788         if (password == NULL) {
2789                 goto fail;
2790         }
2791
2792         *cred = apr_pcalloc(pool, sizeof(**cred));
2793         (*cred)->username = ret_username;
2794         (*cred)->password = password;
2795         (*cred)->may_save = (py_may_save == Py_True);
2796         Py_DECREF(ret);
2797         PyGILState_Release(state);
2798         return NULL;
2799
2800 fail:
2801         Py_DECREF(ret);
2802         PyGILState_Release(state);
2803         return py_svn_error();
2804 }
2805
2806 static PyObject *get_simple_prompt_provider(PyObject *self, PyObject *args)
2807 {
2808         PyObject *prompt_func;
2809         int retry_limit;
2810         AuthProviderObject *auth;
2811
2812         if (!PyArg_ParseTuple(args, "Oi", &prompt_func, &retry_limit))
2813                 return NULL;
2814
2815         auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
2816         auth->pool = Pool(NULL);
2817         if (auth->pool == NULL)
2818                 return NULL;
2819         Py_INCREF(prompt_func);
2820         auth->callback = prompt_func;
2821         svn_auth_get_simple_prompt_provider (&auth->provider, py_simple_prompt, (void *)prompt_func, retry_limit, auth->pool);
2822         return (PyObject *)auth;
2823 }
2824
2825 static svn_error_t *py_ssl_server_trust_prompt(svn_auth_cred_ssl_server_trust_t **cred, void *baton, const char *realm, apr_uint32_t failures, const svn_auth_ssl_server_cert_info_t *cert_info, svn_boolean_t may_save, apr_pool_t *pool)
2826 {
2827         PyObject *fn = (PyObject *)baton;
2828         PyObject *ret;
2829         PyObject *py_cert, *py_may_save, *py_accepted_failures;
2830         PyGILState_STATE state = PyGILState_Ensure();
2831         long accepted_failures;
2832
2833         if (cert_info == NULL) {
2834                 py_cert = Py_None;
2835                 Py_INCREF(py_cert);
2836         } else {
2837                 py_cert = Py_BuildValue("(sssss)", cert_info->hostname, cert_info->fingerprint,
2838                                                   cert_info->valid_from, cert_info->valid_until,
2839                                                   cert_info->issuer_dname, cert_info->ascii_cert);
2840         }
2841
2842         CB_CHECK_PYRETVAL(py_cert);
2843
2844         ret = PyObject_CallFunction(fn, "slOb", realm, failures, py_cert, may_save);
2845         Py_DECREF(py_cert);
2846         CB_CHECK_PYRETVAL(ret);
2847
2848         if (ret == Py_None) {
2849                 Py_DECREF(ret);
2850                 PyGILState_Release(state);
2851                 return NULL;
2852         }
2853         if (!PyTuple_Check(ret)) {
2854                 Py_DECREF(ret);
2855                 PyErr_SetString(PyExc_TypeError, "expected tuple with server trust credentials");
2856                 PyGILState_Release(state);
2857                 return py_svn_error();
2858         }
2859         if (PyTuple_Size(ret) != 2) {
2860                 Py_DECREF(ret);
2861                 PyErr_SetString(PyExc_TypeError, "expected tuple of size 2");
2862                 PyGILState_Release(state);
2863                 return py_svn_error();
2864         }
2865
2866         py_accepted_failures = PyTuple_GetItem(ret, 0);
2867         if (!PyInt_Check(py_accepted_failures)) {
2868                 Py_DECREF(ret);
2869                 PyErr_SetString(PyExc_TypeError, "accepted_failures should be integer");
2870                 PyGILState_Release(state);
2871                 return py_svn_error();
2872         }
2873
2874         py_may_save = PyTuple_GetItem(ret, 1);
2875         if (!PyBool_Check(py_may_save)) {
2876                 Py_DECREF(ret);
2877                 PyErr_SetString(PyExc_TypeError, "may_save should be boolean");
2878                 PyGILState_Release(state);
2879                 return py_svn_error();
2880         }
2881
2882         accepted_failures = PyInt_AsLong(py_accepted_failures);
2883         if (accepted_failures == -1 && PyErr_Occurred()) {
2884                 Py_DECREF(ret);
2885                 PyGILState_Release(state);
2886                 return py_svn_error();
2887         }
2888         *cred = apr_pcalloc(pool, sizeof(**cred));
2889         (*cred)->accepted_failures = accepted_failures;
2890         (*cred)->may_save = (py_may_save == Py_True);
2891
2892         Py_DECREF(ret);
2893         PyGILState_Release(state);
2894         return NULL;
2895 }
2896
2897 static PyObject *get_ssl_server_trust_prompt_provider(PyObject *self, PyObject *args)
2898 {
2899         AuthProviderObject *auth;
2900         PyObject *prompt_func;
2901
2902         if (!PyArg_ParseTuple(args, "O", &prompt_func))
2903                 return NULL;
2904
2905         auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
2906         if (auth == NULL)
2907                 return NULL;
2908         auth->pool = Pool(NULL);
2909         if (auth->pool == NULL)
2910                 return NULL;
2911         Py_INCREF(prompt_func);
2912         auth->callback = prompt_func;
2913         svn_auth_get_ssl_server_trust_prompt_provider (&auth->provider, py_ssl_server_trust_prompt, (void *)prompt_func, auth->pool);
2914         return (PyObject *)auth;
2915 }
2916
2917 static svn_error_t *py_ssl_client_cert_pw_prompt(svn_auth_cred_ssl_client_cert_pw_t **cred, void *baton, const char *realm, svn_boolean_t may_save, apr_pool_t *pool)
2918 {
2919         PyObject *fn = (PyObject *)baton, *ret, *py_may_save, *py_password;
2920         PyGILState_STATE state = PyGILState_Ensure();
2921         ret = PyObject_CallFunction(fn, "sb", realm, may_save);
2922         CB_CHECK_PYRETVAL(ret);
2923         if (!PyTuple_Check(ret)) {
2924                 PyErr_SetString(PyExc_TypeError, "expected tuple with client cert pw credentials");
2925                 goto fail;
2926         }
2927
2928         if (PyTuple_Size(ret) != 2) {
2929                 PyErr_SetString(PyExc_TypeError, "expected tuple of size 2");
2930                 goto fail;
2931         }
2932         py_may_save = PyTuple_GetItem(ret, 1);
2933         if (!PyBool_Check(py_may_save)) {
2934                 PyErr_SetString(PyExc_TypeError, "may_save should be boolean");
2935                 goto fail;
2936         }
2937         py_password = PyTuple_GetItem(ret, 0);
2938         if (!PyString_Check(py_password)) {
2939                 PyErr_SetString(PyExc_TypeError, "password should be string");
2940                 goto fail;
2941         }
2942         *cred = apr_pcalloc(pool, sizeof(**cred));
2943         (*cred)->password = py_object_to_svn_string(py_password, pool);
2944         (*cred)->may_save = (py_may_save == Py_True);
2945         Py_DECREF(ret);
2946         PyGILState_Release(state);
2947         return NULL;
2948         
2949 fail:
2950         Py_DECREF(ret);
2951         PyGILState_Release(state);
2952         return py_svn_error();
2953 }
2954
2955 static svn_error_t *py_ssl_client_cert_prompt(svn_auth_cred_ssl_client_cert_t **cred, void *baton, const char *realm, svn_boolean_t may_save, apr_pool_t *pool)
2956 {
2957         PyObject *fn = (PyObject *)baton, *ret, *py_may_save, *py_cert_file;
2958         PyGILState_STATE state = PyGILState_Ensure();
2959         char *cert_file;
2960         ret = PyObject_CallFunction(fn, "sb", realm, may_save);
2961         CB_CHECK_PYRETVAL(ret);
2962
2963         if (!PyTuple_Check(ret)) {
2964                 PyErr_SetString(PyExc_TypeError, "expected tuple with client cert credentials");
2965                 goto fail;
2966         }
2967
2968         if (PyTuple_Size(ret) != 2) {
2969                 PyErr_SetString(PyExc_TypeError, "expected tuple of size 2");
2970                 goto fail;
2971         }
2972         py_may_save = PyTuple_GetItem(ret, 1);
2973         if (!PyBool_Check(py_may_save)) {
2974                 PyErr_SetString(PyExc_TypeError, "may_save should be boolean");
2975                 goto fail;
2976         }
2977
2978         py_cert_file = PyTuple_GetItem(ret, 0);
2979         cert_file = py_object_to_svn_string(py_cert_file, pool);
2980         if (!cert_file) {
2981                 goto fail;
2982         }
2983
2984         *cred = apr_pcalloc(pool, sizeof(**cred));
2985         (*cred)->cert_file = cert_file;
2986         (*cred)->may_save = (py_may_save == Py_True);
2987         Py_DECREF(ret);
2988         PyGILState_Release(state);
2989         return NULL;
2990
2991 fail:
2992         Py_DECREF(ret);
2993         PyGILState_Release(state);
2994         return py_svn_error();
2995 }
2996
2997 static PyObject *get_ssl_client_cert_pw_prompt_provider(PyObject *self, PyObject *args)
2998 {
2999         PyObject *prompt_func;
3000         int retry_limit;
3001         AuthProviderObject *auth;
3002
3003         if (!PyArg_ParseTuple(args, "Oi", &prompt_func, &retry_limit))
3004                 return NULL;
3005
3006         auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
3007         if (auth == NULL)
3008                 return NULL;
3009         auth->pool = Pool(NULL);
3010         if (auth->pool == NULL)
3011                 return NULL;
3012         Py_INCREF(prompt_func);
3013         auth->callback = prompt_func;
3014         svn_auth_get_ssl_client_cert_pw_prompt_provider (&auth->provider, py_ssl_client_cert_pw_prompt, (void *)prompt_func, retry_limit, auth->pool);
3015         return (PyObject *)auth;
3016 }
3017
3018 static PyObject *get_ssl_client_cert_prompt_provider(PyObject *self, PyObject *args)
3019 {
3020         PyObject *prompt_func;
3021         int retry_limit;
3022         AuthProviderObject *auth;
3023
3024         if (!PyArg_ParseTuple(args, "Oi", &prompt_func, &retry_limit))
3025                 return NULL;
3026
3027         auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
3028         if (auth == NULL)
3029                 return NULL;
3030         auth->pool = Pool(NULL);
3031         if (auth->pool == NULL)
3032                 return NULL;
3033         Py_INCREF(prompt_func);
3034         auth->callback = prompt_func;
3035         svn_auth_get_ssl_client_cert_prompt_provider (&auth->provider, py_ssl_client_cert_prompt, (void *)prompt_func, retry_limit, auth->pool);
3036         return (PyObject *)auth;
3037 }
3038
3039 static PyObject *get_username_provider(PyObject *self)
3040 {
3041         AuthProviderObject *auth;
3042         auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
3043         if (auth == NULL)
3044                 return NULL;
3045         auth->pool = Pool(NULL);
3046         auth->callback = NULL;
3047         if (auth->pool == NULL) {
3048                 PyObject_Del(auth);
3049                 return NULL;
3050         }
3051         svn_auth_get_username_provider(&auth->provider, auth->pool);
3052         return (PyObject *)auth;
3053 }
3054
3055 #if ONLY_SINCE_SVN(1, 6)
3056 static svn_error_t *py_cb_get_simple_provider_prompt(svn_boolean_t *may_save_plaintext,
3057                                                      const char *realmstring,
3058                                                      void *baton,
3059                                                      apr_pool_t *pool)
3060 {
3061         if (baton == Py_None) {
3062                 /* just disallow saving plaintext passwords on 1.6 and later */
3063                 *may_save_plaintext = FALSE;
3064         } else {
3065                 PyObject *ret;
3066                 PyGILState_STATE state = PyGILState_Ensure();
3067                 ret = PyObject_CallFunction(baton, "s", realmstring);
3068                 CB_CHECK_PYRETVAL(ret);
3069                 if (ret == NULL) {
3070                         PyGILState_Release(state);
3071                         return py_svn_error();
3072                 }
3073                 *may_save_plaintext = PyObject_IsTrue(ret);
3074                 Py_DECREF(ret);
3075                 PyGILState_Release(state);
3076         }
3077
3078     return NULL;
3079 }
3080 #endif
3081
3082 static PyObject *get_simple_provider(PyObject *self, PyObject *args)
3083 {
3084         AuthProviderObject *auth;
3085         PyObject *callback = Py_None;
3086         apr_pool_t *pool;
3087
3088         if (!PyArg_ParseTuple(args, "|O:get_simple_provider", &callback))
3089                 return NULL;
3090
3091         pool = Pool(NULL);
3092         if (pool == NULL)
3093                 return NULL;
3094         auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
3095         if (auth == NULL) {
3096                 apr_pool_destroy(pool);
3097                 return NULL;
3098         }
3099         auth->pool = pool;
3100 #if ONLY_SINCE_SVN(1, 6)
3101         Py_INCREF(callback);
3102         auth->callback = callback;
3103         svn_auth_get_simple_provider2(&auth->provider,
3104                   py_cb_get_simple_provider_prompt, auth->callback, auth->pool);
3105 #else
3106         auth->callback = NULL;
3107         auth->provider = NULL;
3108         if (callback != Py_None) {
3109                 PyErr_SetString(PyExc_NotImplementedError,
3110                         "callback not supported with svn < 1.6");
3111                 Py_DECREF(auth);
3112                 return NULL;
3113         }
3114         svn_auth_get_simple_provider(&auth->provider, auth->pool);
3115 #endif
3116         return (PyObject *)auth;
3117 }
3118
3119 static PyObject *get_ssl_server_trust_file_provider(PyObject *self)
3120 {
3121         AuthProviderObject *auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
3122         if (auth == NULL)
3123                 return NULL;
3124         auth->callback = NULL;
3125         auth->pool = Pool(NULL);
3126         if (auth->pool == NULL)
3127                 return NULL;
3128         svn_auth_get_ssl_server_trust_file_provider(&auth->provider, auth->pool);
3129         return (PyObject *)auth;
3130 }
3131
3132 static PyObject *get_ssl_client_cert_file_provider(PyObject *self)
3133 {
3134         AuthProviderObject *auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
3135         if (auth == NULL)
3136                 return NULL;
3137         auth->callback = NULL;
3138         auth->pool = Pool(NULL);
3139         if (auth->pool == NULL)
3140                 return NULL;
3141         svn_auth_get_ssl_client_cert_file_provider(&auth->provider, auth->pool);
3142         return (PyObject *)auth;
3143 }
3144
3145 static PyObject *get_ssl_client_cert_pw_file_provider(PyObject *self)
3146 {
3147         AuthProviderObject *auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
3148         if (auth == NULL)
3149                 return NULL;
3150         auth->callback = NULL;
3151         auth->pool = Pool(NULL);
3152         if (auth->pool == NULL)
3153                 return NULL;
3154
3155 #if ONLY_SINCE_SVN(1, 6)
3156         svn_auth_get_ssl_client_cert_pw_file_provider2(&auth->provider, NULL, NULL, auth->pool);
3157 #else
3158         svn_auth_get_ssl_client_cert_pw_file_provider(&auth->provider, auth->pool);
3159 #endif
3160         return (PyObject *)auth;
3161 }
3162
3163 static PyObject *print_modules(PyObject *self)
3164 {
3165         svn_stringbuf_t *stringbuf;
3166         svn_string_t *string;
3167         PyObject *ret;
3168         apr_pool_t *pool = Pool(NULL);
3169         if (pool == NULL)
3170                 return NULL;
3171         stringbuf = svn_stringbuf_create("", pool);
3172         if (stringbuf == NULL) {
3173                 apr_pool_destroy(pool);
3174                 return NULL;
3175         }
3176         RUN_SVN_WITH_POOL(pool, svn_ra_print_modules(stringbuf, pool));
3177         string = svn_string_create_from_buf(stringbuf, pool);
3178         if (string == NULL) {
3179                 apr_pool_destroy(pool);
3180                 return NULL;
3181         }
3182         ret = PyBytes_FromStringAndSize(string->data, string->len);
3183         apr_pool_destroy(pool);
3184         return ret;
3185 }
3186
3187 #if defined(WIN32) || defined(__CYGWIN__)
3188 static PyObject *get_windows_simple_provider(PyObject* self)
3189 {
3190         AuthProviderObject *auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
3191         if (auth == NULL)
3192                 return NULL;
3193         auth->callback = NULL;
3194         auth->pool = Pool(NULL);
3195         if (auth->pool == NULL)
3196                 return NULL;
3197         svn_auth_get_windows_simple_provider(&auth->provider, auth->pool);
3198         return (PyObject *)auth;
3199 }
3200
3201 #if ONLY_SINCE_SVN(1, 5)
3202 static PyObject *get_windows_ssl_server_trust_provider(PyObject *self)
3203 {
3204         AuthProviderObject *auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
3205         if (auth == NULL)
3206                 return NULL;
3207         auth->callback = NULL;
3208         auth->pool = Pool(NULL);
3209         if (auth->pool == NULL)
3210                 return NULL;
3211         svn_auth_get_windows_ssl_server_trust_provider(&auth->provider, auth->pool);
3212         return (PyObject *)auth;
3213 }
3214 #endif
3215 #endif
3216
3217 #if defined(SVN_KEYCHAIN_PROVIDER_AVAILABLE)
3218 static PyObject *get_keychain_simple_provider(PyObject* self)
3219 {
3220         AuthProviderObject *auth = PyObject_New(AuthProviderObject, &AuthProvider_Type);
3221         if (auth == NULL)
3222                 return NULL;
3223         auth->callback = NULL;
3224         auth->pool = Pool(NULL);
3225         if (auth->pool == NULL)
3226                 return NULL;
3227         svn_auth_get_keychain_simple_provider(&auth->provider, auth->pool);
3228         return (PyObject *)auth;
3229 }
3230 #endif
3231
3232 static PyObject *get_platform_specific_client_providers(PyObject *self)
3233 {
3234 #if ONLY_SINCE_SVN(1, 6)
3235         /* svn_auth_get_platform_specific_client_providers() allocates all the
3236          * providers in a single pool, so we can't use it :/ */
3237         const char *provider_names[] = {
3238                 "gnome_keyring", "keychain", "kwallet", "windows", NULL,
3239         };
3240         const char *provider_types[] = {
3241                 "simple", "ssl_client_cert_pw", "ssl_server_trust", NULL,
3242         };
3243         PyObject *pylist;
3244         int i, j;
3245
3246         pylist = PyList_New(0);
3247         if (pylist == NULL) {
3248                 return NULL;
3249         }
3250
3251         for (i = 0; provider_names[i] != NULL; i++) {
3252                 for (j = 0; provider_types[j] != NULL; j++) {
3253                         svn_auth_provider_object_t *c_provider = NULL;
3254                         apr_pool_t *pool = Pool(NULL);
3255                         AuthProviderObject *auth;
3256
3257                         if (pool == NULL)
3258                                 continue;
3259
3260                         RUN_SVN(svn_auth_get_platform_specific_provider(&c_provider,
3261                                                                                                                         provider_names[i],
3262                                                                                                                         provider_types[j],
3263                                                                                                                         pool));
3264
3265                         auth = PyObject_New(AuthProviderObject,
3266                                                                 &AuthProvider_Type);
3267
3268                         if (c_provider == NULL || auth == NULL) {
3269                                 apr_pool_destroy(pool);
3270                                 continue;
3271                         }
3272
3273                         auth->pool = pool;
3274                         auth->callback = NULL;
3275                         auth->provider = c_provider;
3276
3277                         PyList_Append(pylist, (PyObject *)auth);
3278
3279                         Py_DECREF(auth);
3280                 }
3281         }
3282
3283         return pylist;
3284 #else
3285         PyObject *pylist = PyList_New(0);
3286         PyObject *provider = NULL;
3287
3288         if (pylist == NULL) {
3289                 Py_DECREF(pylist);
3290                 return NULL;
3291         }
3292
3293 #if defined(WIN32) || defined(__CYGWIN__)
3294         provider = get_windows_simple_provider(self);
3295         if (provider == NULL)
3296                 return NULL;
3297         PyList_Append(pylist, provider);
3298         Py_DECREF(provider);
3299
3300 #if ONLY_SINCE_SVN(1, 5)
3301         provider = get_windows_ssl_server_trust_provider(self);
3302         if (provider == NULL)
3303                 return NULL;
3304         PyList_Append(pylist, provider);
3305         Py_DECREF(provider);
3306 #endif /* 1.5 */
3307 #endif /* WIN32 || __CYGWIN__ */
3308
3309 #if defined(SVN_KEYCHAIN_PROVIDER_AVAILABLE)
3310         provider = get_keychain_simple_provider(self);
3311         if (provider == NULL)
3312                 return NULL;
3313         PyList_Append(pylist, provider);
3314         Py_DECREF(provider);
3315 #endif
3316
3317         return pylist;
3318 #endif
3319 }
3320
3321 static PyMethodDef ra_module_methods[] = {
3322         { "version", (PyCFunction)version, METH_NOARGS,
3323                 "version() -> (major, minor, micro, tag)\n"
3324                 "Version of libsvn_ra currently used." },
3325         { "api_version", (PyCFunction)api_version, METH_NOARGS,
3326                 "api_version() -> (major, minor, patch, tag)\n\n"
3327                 "Version of libsvn_ra Subvertpy was compiled against."
3328         },
3329         { "get_ssl_client_cert_pw_file_provider", (PyCFunction)get_ssl_client_cert_pw_file_provider, METH_NOARGS, NULL },
3330         { "get_ssl_client_cert_file_provider", (PyCFunction)get_ssl_client_cert_file_provider, METH_NOARGS, NULL },
3331         { "get_ssl_server_trust_file_provider", (PyCFunction)get_ssl_server_trust_file_provider, METH_NOARGS, NULL },
3332         { "get_simple_provider", (PyCFunction)get_simple_provider, METH_VARARGS, NULL },
3333 #if defined(WIN32) || defined(__CYGWIN__)
3334         { "get_windows_simple_provider", (PyCFunction)get_windows_simple_provider, METH_NOARGS, NULL },
3335 #if ONLY_SINCE_SVN(1, 5)
3336         { "get_windows_ssl_server_trust_provider", (PyCFunction)get_windows_ssl_server_trust_provider, METH_NOARGS, NULL },
3337 #endif
3338 #endif
3339 #if defined(SVN_KEYCHAIN_PROVIDER_AVAILABLE)
3340         { "get_keychain_simple_provider", (PyCFunction)get_keychain_simple_provider, METH_NOARGS, NULL },
3341 #endif
3342         { "get_username_prompt_provider", (PyCFunction)get_username_prompt_provider, METH_VARARGS, NULL },
3343         { "get_simple_prompt_provider", (PyCFunction)get_simple_prompt_provider, METH_VARARGS, NULL },
3344         { "get_ssl_server_trust_prompt_provider", (PyCFunction)get_ssl_server_trust_prompt_provider, METH_VARARGS, NULL },
3345         { "get_ssl_client_cert_prompt_provider", (PyCFunction)get_ssl_client_cert_prompt_provider, METH_VARARGS, NULL },
3346         { "get_ssl_client_cert_pw_prompt_provider", (PyCFunction)get_ssl_client_cert_pw_prompt_provider, METH_VARARGS, NULL },
3347         { "get_username_provider", (PyCFunction)get_username_provider, METH_NOARGS, NULL },
3348         { "get_platform_specific_client_providers",
3349                 (PyCFunction)get_platform_specific_client_providers,
3350                 METH_NOARGS,
3351                 "Get a list of all available platform client providers.",
3352         },
3353         { "print_modules", (PyCFunction)print_modules, METH_NOARGS, NULL },
3354         { NULL, }
3355 };
3356
3357 static PyObject *
3358 moduleinit(void)
3359 {
3360         static apr_pool_t *pool;
3361         PyObject *mod;
3362
3363         if (PyType_Ready(&RemoteAccess_Type) < 0)
3364                 return NULL;
3365
3366         if (PyType_Ready(&Editor_Type) < 0)
3367                 return NULL;
3368
3369         if (PyType_Ready(&FileEditor_Type) < 0)
3370                 return NULL;
3371
3372         if (PyType_Ready(&DirectoryEditor_Type) < 0)
3373                 return NULL;
3374
3375         if (PyType_Ready(&Reporter_Type) < 0)
3376                 return NULL;
3377
3378         if (PyType_Ready(&TxDeltaWindowHandler_Type) < 0)
3379                 return NULL;
3380
3381         if (PyType_Ready(&Auth_Type) < 0)
3382                 return NULL;
3383
3384         if (PyType_Ready(&CredentialsIter_Type) < 0)
3385                 return NULL;
3386
3387         if (PyType_Ready(&AuthProvider_Type) < 0)
3388                 return NULL;
3389
3390         if (PyType_Ready(&LogIterator_Type) < 0)
3391                 return NULL;
3392
3393         apr_initialize();
3394         pool = Pool(NULL);
3395         if (pool == NULL)
3396                 return NULL;
3397         svn_ra_initialize(pool);
3398         PyEval_InitThreads();
3399
3400
3401 #if PY_MAJOR_VERSION >= 3
3402         static struct PyModuleDef moduledef = {
3403           PyModuleDef_HEAD_INIT,
3404           "_ra",         /* m_name */
3405           "Remote Access",            /* m_doc */
3406           -1,              /* m_size */
3407           ra_module_methods, /* m_methods */
3408           NULL,            /* m_reload */
3409           NULL,            /* m_traverse */
3410           NULL,            /* m_clear*/
3411           NULL,            /* m_free */
3412         };
3413         mod = PyModule_Create(&moduledef);
3414 #else
3415         mod = Py_InitModule3("_ra", ra_module_methods, "Remote Access");
3416 #endif
3417         if (mod == NULL)
3418                 return NULL;
3419
3420         PyModule_AddObject(mod, "RemoteAccess", (PyObject *)&RemoteAccess_Type);
3421         Py_INCREF(&RemoteAccess_Type);
3422
3423         PyModule_AddObject(mod, "Auth", (PyObject *)&Auth_Type);
3424         Py_INCREF(&Auth_Type);
3425
3426         PyModule_AddObject(mod, "Editor", (PyObject *)&Editor_Type);
3427         Py_INCREF(&Editor_Type);
3428
3429         busy_exc = PyErr_NewException("_ra.BusyException", NULL, NULL);
3430         PyModule_AddObject(mod, "BusyException", busy_exc);
3431
3432 #if ONLY_SINCE_SVN(1, 5)
3433         PyModule_AddIntConstant(mod, "DEPTH_UNKNOWN", svn_depth_unknown);
3434         PyModule_AddIntConstant(mod, "DEPTH_EXCLUDE", svn_depth_exclude);
3435         PyModule_AddIntConstant(mod, "DEPTH_EMPTY", svn_depth_empty);
3436         PyModule_AddIntConstant(mod, "DEPTH_FILES", svn_depth_files);
3437         PyModule_AddIntConstant(mod, "DEPTH_IMMEDIATES", svn_depth_immediates);
3438         PyModule_AddIntConstant(mod, "DEPTH_INFINITY", svn_depth_infinity);
3439 #endif
3440
3441         PyModule_AddIntConstant(mod, "DIRENT_KIND", SVN_DIRENT_KIND);
3442         PyModule_AddIntConstant(mod, "DIRENT_SIZE", SVN_DIRENT_SIZE);
3443         PyModule_AddIntConstant(mod, "DIRENT_HAS_PROPS", SVN_DIRENT_HAS_PROPS);
3444         PyModule_AddIntConstant(mod, "DIRENT_CREATED_REV", SVN_DIRENT_CREATED_REV);
3445         PyModule_AddIntConstant(mod, "DIRENT_TIME", SVN_DIRENT_TIME);
3446         PyModule_AddIntConstant(mod, "DIRENT_LAST_AUTHOR", SVN_DIRENT_LAST_AUTHOR);
3447         PyModule_AddIntConstant(mod, "DIRENT_ALL", SVN_DIRENT_ALL);
3448
3449 #if ONLY_SINCE_SVN(1, 5)
3450         PyModule_AddIntConstant(mod, "MERGEINFO_EXPLICIT", svn_mergeinfo_explicit);
3451         PyModule_AddIntConstant(mod, "MERGEINFO_INHERITED", svn_mergeinfo_inherited);
3452         PyModule_AddIntConstant(mod, "MERGEINFO_NEAREST_ANCESTOR", svn_mergeinfo_nearest_ancestor);
3453 #endif
3454
3455 #ifdef SVN_VER_REVISION
3456         PyModule_AddIntConstant(mod, "SVN_REVISION", SVN_VER_REVISION);
3457 #endif
3458
3459         return mod;
3460 }
3461
3462 #if PY_MAJOR_VERSION >= 3
3463 PyMODINIT_FUNC
3464 PyInit__ra(void)
3465 {
3466         return moduleinit();
3467 }
3468 #else
3469 PyMODINIT_FUNC
3470 init_ra(void)
3471 {
3472         moduleinit();
3473 }
3474 #endif