Port BerkeleyDB 4.1 support from the pybsddb project.  bsddb is now at
version 4.1.1 and works with up to BerkeleyDB 4.1.25.
diff --git a/Modules/_bsddb.c b/Modules/_bsddb.c
index 5834d51..9bb339c 100644
--- a/Modules/_bsddb.c
+++ b/Modules/_bsddb.c
@@ -35,16 +35,23 @@
 
 /*
  * Handwritten code to wrap version 3.x of the Berkeley DB library,
- * written to replace a SWIG-generated file.
+ * written to replace a SWIG-generated file.  It has since been updated
+ * to compile with BerkeleyDB versions 3.2 through 4.1.
  *
  * This module was started by Andrew Kuchling to remove the dependency
  * on SWIG in a package by Gregory P. Smith <greg@electricrain.com> who
  * based his work on a similar package by Robin Dunn <robin@alldunn.com>
  * which wrapped Berkeley DB 2.7.x.
  *
- * Development of this module has now returned full circle back to
- * Robin Dunn who is working in behalf of Digital Creations to complete
- * the wrapping of the DB 3.x API and to build a solid unit test suite.
+ * Development of this module then returned full circle back to Robin Dunn
+ * who worked on behalf of Digital Creations to complete the wrapping of
+ * the DB 3.x API and to build a solid unit test suite.  Robin has
+ * since gone onto other projects (wxPython).
+ *
+ * Gregory P. Smith <greg@electricrain.com> is once again the maintainer.
+ *
+ * Use the pybsddb-users@lists.sf.net mailing list for all questions.
+ * Things can change faster than the header of this file is updated.
  *
  * This module contains 5 types:
  *
@@ -75,12 +82,10 @@
 /* --------------------------------------------------------------------- */
 /* Various macro definitions */
 
-#define PY_BSDDB_VERSION "3.4.2"
-
 /* 40 = 4.0, 33 = 3.3; this will break if the second number is > 9 */
 #define DBVER (DB_VERSION_MAJOR * 10 + DB_VERSION_MINOR)
 
-static char *orig_rcs_id = "/Id: _db.c,v 1.48 2002/11/21 19:11:19 greg Exp /";
+#define PY_BSDDB_VERSION "4.1.1"
 static char *rcs_id = "$Id$";
 
 
@@ -166,7 +171,7 @@
 typedef struct {
     PyObject_HEAD
     DB_ENV*     db_env;
-    int         flags;             /* saved flags from open() */
+    u_int32_t   flags;             /* saved flags from open() */
     int         closed;
     int         getReturnsNone;
 } DBEnvObject;
@@ -176,8 +181,8 @@
     PyObject_HEAD
     DB*             db;
     DBEnvObject*    myenvobj;  /* PyObject containing the DB_ENV */
-    int             flags;     /* saved flags from open() */
-    int             setflags;  /* saved flags from set_flags() */
+    u_int32_t       flags;     /* saved flags from open() */
+    u_int32_t       setflags;  /* saved flags from set_flags() */
     int             haveStat;
     int             getReturnsNone;
 #if (DBVER >= 33)
@@ -299,7 +304,8 @@
    what's been given, verifies that it's allowed, and then makes the DBT.
 
    Caller should call FREE_DBT(key) when done. */
-static int make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags)
+static int
+make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags)
 {
     db_recno_t recno;
     int type;
@@ -315,7 +321,9 @@
         if (type == -1)
             return 0;
         if (type == DB_RECNO || type == DB_QUEUE) {
-            PyErr_SetString(PyExc_TypeError, "String keys not allowed for Recno and Queue DB's");
+            PyErr_SetString(
+                PyExc_TypeError,
+                "String keys not allowed for Recno and Queue DB's");
             return 0;
         }
 
@@ -329,16 +337,19 @@
         if (type == -1)
             return 0;
         if (type == DB_BTREE && pflags != NULL) {
-            /* if BTREE then an Integer key is allowed with the DB_SET_RECNO flag */
+            /* if BTREE then an Integer key is allowed with the
+             * DB_SET_RECNO flag */
             *pflags |= DB_SET_RECNO;
         }
         else if (type != DB_RECNO && type != DB_QUEUE) {
-            PyErr_SetString(PyExc_TypeError, "Integer keys only allowed for Recno and Queue DB's");
+            PyErr_SetString(
+                PyExc_TypeError,
+                "Integer keys only allowed for Recno and Queue DB's");
             return 0;
         }
 
-        /* Make a key out of the requested recno, use allocated space so DB will
-           be able to realloc room for the real key if needed. */
+        /* Make a key out of the requested recno, use allocated space so DB
+         * will be able to realloc room for the real key if needed. */
         recno = PyInt_AS_LONG(keyobj);
         key->data = malloc(sizeof(db_recno_t));
         if (key->data == NULL) {
@@ -381,7 +392,8 @@
 }
 
 
-/* Callback used to save away more information about errors from the DB library. */
+/* Callback used to save away more information about errors from the DB
+ * library. */
 static char _db_errmsg[1024];
 static void _db_errorCallback(const char* prefix, char* msg)
 {
@@ -393,12 +405,14 @@
 static int makeDBError(int err)
 {
     char errTxt[2048];  /* really big, just in case... */
-    PyObject* errObj = NULL;
+    PyObject *errObj = NULL;
+    PyObject *errTuple = NULL;
     int exceptionRaised = 0;
 
     switch (err) {
         case 0:                     /* successful, no error */      break;
 
+#if (DBVER < 41)
         case DB_INCOMPLETE:
 #if INCOMPLETE_IS_WARNING
             strcpy(errTxt, db_strerror(err));
@@ -407,7 +421,8 @@
                 strcat(errTxt, _db_errmsg);
                 _db_errmsg[0] = 0;
             }
-#if PYTHON_API_VERSION >= 1010 /* if Python 2.1 or better use warning framework */
+/* if Python 2.1 or better use warning framework */
+#if PYTHON_API_VERSION >= 1010
             exceptionRaised = PyErr_Warn(PyExc_RuntimeWarning, errTxt);
 #else
             fprintf(stderr, errTxt);
@@ -418,6 +433,7 @@
         errObj = DBIncompleteError;
 #endif
         break;
+#endif /* DBVER < 41 */
 
         case DB_KEYEMPTY:           errObj = DBKeyEmptyError;       break;
         case DB_KEYEXIST:           errObj = DBKeyExistError;       break;
@@ -455,7 +471,10 @@
             strcat(errTxt, _db_errmsg);
             _db_errmsg[0] = 0;
         }
-        PyErr_SetObject(errObj, Py_BuildValue("(is)", err, errTxt));
+
+	errTuple = Py_BuildValue("(is)", err, errTxt);
+        PyErr_SetObject(errObj, errTuple);
+	Py_DECREF(errTuple);
     }
 
     return ((errObj != NULL) || exceptionRaised);
@@ -666,13 +685,16 @@
 DB_dealloc(DBObject* self)
 {
     if (self->db != NULL) {
-        /* avoid closing a DB when its DBEnv has been closed out from under it */
+        /* avoid closing a DB when its DBEnv has been closed out from under
+         * it */
         if (!self->myenvobj ||
-            (self->myenvobj && self->myenvobj->db_env)) {
+            (self->myenvobj && self->myenvobj->db_env))
+        {
             MYDB_BEGIN_ALLOW_THREADS;
             self->db->close(self->db, 0);
             MYDB_END_ALLOW_THREADS;
-#if PYTHON_API_VERSION >= 1010 /* if Python 2.1 or better use warning framework */
+ /* if Python 2.1 or better use warning framework */
+#if PYTHON_API_VERSION >= 1010
         } else {
             PyErr_Warn(PyExc_RuntimeWarning,
                 "DB could not be closed in destructor: DBEnv already closed");
@@ -843,7 +865,8 @@
 
     MYDB_BEGIN_ALLOW_THREADS;
 #if (DBVER >= 40)
-    err = myenv->db_env->lock_get(myenv->db_env, locker, flags, obj, lock_mode, &self->lock);
+    err = myenv->db_env->lock_get(myenv->db_env, locker, flags, obj, lock_mode,
+                                  &self->lock);
 #else
     err = lock_get(myenv->db_env, locker, flags, obj, lock_mode, &self->lock);
 #endif
@@ -907,7 +930,8 @@
 #if (DBVER >= 33)
 
 static int
-_db_associateCallback(DB* db, const DBT* priKey, const DBT* priData, DBT* secKey)
+_db_associateCallback(DB* db, const DBT* priKey, const DBT* priData,
+                      DBT* secKey)
 {
     int       retval = DB_DONOTINDEX;
     DBObject* secondaryDB = (DBObject*)db->app_private;
@@ -958,13 +982,21 @@
 #endif
             secKey->flags = DB_DBT_APPMALLOC;   /* DB will free */
             secKey->data = malloc(size);        /* TODO, check this */
-            memcpy(secKey->data, data, size);
-            secKey->size = size;
-            retval = 0;
+	    if (secKey->data) {
+		memcpy(secKey->data, data, size);
+		secKey->size = size;
+		retval = 0;
+	    }
+	    else {
+		PyErr_SetString(PyExc_MemoryError,
+                                "malloc failed in _db_associateCallback");
+		PyErr_Print();
+	    }
         }
         else {
-            PyErr_SetString(PyExc_TypeError,
-                            "DB associate callback should return DB_DONOTINDEX or a string.");
+            PyErr_SetString(
+               PyExc_TypeError,
+               "DB associate callback should return DB_DONOTINDEX or string.");
             PyErr_Print();
         }
 
@@ -985,11 +1017,28 @@
     int err, flags=0;
     DBObject* secondaryDB;
     PyObject* callback;
+#if (DBVER >= 41)
+    PyObject *txnobj = NULL;
+    DB_TXN *txn = NULL;
+    char* kwnames[] = {"secondaryDB", "callback", "flags", "txn", NULL};
+#else
     char* kwnames[] = {"secondaryDB", "callback", "flags", NULL};
+#endif
 
+#if (DBVER >= 41)
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|iO:associate", kwnames,
+                                     &secondaryDB, &callback, &flags,
+                                     &txnobj)) {
+#else
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|i:associate", kwnames,
-                                     &secondaryDB, &callback, &flags))
+                                     &secondaryDB, &callback, &flags)) {
+#endif
         return NULL;
+    }
+
+#if (DBVER >= 41)
+    if (!checkTxnObj(txnobj, &txn)) return NULL;
+#endif
 
     CHECK_DB_NOT_CLOSED(self);
     if (!DBObject_Check(secondaryDB)) {
@@ -1024,10 +1073,18 @@
      */
     PyEval_InitThreads();
     MYDB_BEGIN_ALLOW_THREADS;
+#if (DBVER >= 41)
+    err = self->db->associate(self->db,
+	                      txn,
+                              secondaryDB->db,
+                              _db_associateCallback,
+                              flags);
+#else
     err = self->db->associate(self->db,
                               secondaryDB->db,
                               _db_associateCallback,
                               flags);
+#endif
     MYDB_END_ALLOW_THREADS;
 
     if (err) {
@@ -1083,7 +1140,8 @@
     if (type == -1)
         return NULL;
     if (type != DB_QUEUE) {
-        PyErr_SetString(PyExc_TypeError, "Consume methods only allowed for Queue DB's");
+        PyErr_SetString(PyExc_TypeError,
+                        "Consume methods only allowed for Queue DB's");
         return NULL;
     }
     if (!checkTxnObj(txnobj, &txn))
@@ -1107,7 +1165,8 @@
         retval = Py_None;
     }
     else if (!err) {
-        retval = Py_BuildValue("s#s#", key.data, key.size, data.data, data.size);
+        retval = Py_BuildValue("s#s#", key.data, key.size, data.data,
+                               data.size);
         FREE_DBT(key);
         FREE_DBT(data);
     }
@@ -1123,7 +1182,8 @@
 }
 
 static PyObject*
-DB_consume_wait(DBObject* self, PyObject* args, PyObject* kwargs, int consume_flag)
+DB_consume_wait(DBObject* self, PyObject* args, PyObject* kwargs,
+                int consume_flag)
 {
     return _DB_consume(self, args, kwargs, DB_CONSUME_WAIT);
 }
@@ -1211,10 +1271,11 @@
     int doff = -1;
     DBT key, data;
     DB_TXN *txn = NULL;
-    char* kwnames[] = { "key", "default", "txn", "flags", "dlen", "doff", NULL };
+    char* kwnames[] = {"key", "default", "txn", "flags", "dlen", "doff", NULL};
 
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOiii:get", kwnames,
-                                     &keyobj, &dfltobj, &txnobj, &flags, &dlen, &doff))
+                                     &keyobj, &dfltobj, &txnobj, &flags, &dlen,
+                                     &doff))
         return NULL;
 
     CHECK_DB_NOT_CLOSED(self);
@@ -1247,7 +1308,8 @@
     }
     else if (!err) {
         if (flags & DB_SET_RECNO) /* return both key and data */
-            retval = Py_BuildValue("s#s#", key.data, key.size, data.data, data.size);
+            retval = Py_BuildValue("s#s#", key.data, key.size, data.data,
+                                   data.size);
         else /* return just the data */
             retval = PyString_FromStringAndSize((char*)data.data, data.size);
         FREE_DBT(key);
@@ -1415,7 +1477,8 @@
     CHECK_DB_NOT_CLOSED(self);
 
     if (!PySequence_Check(cursorsObj)) {
-        PyErr_SetString(PyExc_TypeError, "Sequence of DBCursor objects expected");
+        PyErr_SetString(PyExc_TypeError,
+                        "Sequence of DBCursor objects expected");
         return NULL;
     }
 
@@ -1425,7 +1488,8 @@
     for (x=0; x<length; x++) {
         PyObject* item = PySequence_GetItem(cursorsObj, x);
         if (!DBCursorObject_Check(item)) {
-            PyErr_SetString(PyExc_TypeError, "Sequence of DBCursor objects expected");
+            PyErr_SetString(PyExc_TypeError,
+                            "Sequence of DBCursor objects expected");
             free(cursors);
             return NULL;
         }
@@ -1457,7 +1521,8 @@
                                      &keyobj, &txnobj, &flags))
         return NULL;
     CHECK_DB_NOT_CLOSED(self);
-    if (!make_dbt(keyobj, &key))   /* BTree only, don't need to allow for an int key */
+    if (!make_dbt(keyobj, &key))
+        /* BTree only, don't need to allow for an int key */
         return NULL;
     if (!checkTxnObj(txnobj, &txn))
         return NULL;
@@ -1477,27 +1542,82 @@
     int err, type = DB_UNKNOWN, flags=0, mode=0660;
     char* filename = NULL;
     char* dbname = NULL;
-    char* kwnames[] = { "filename", "dbname", "dbtype", "flags", "mode", NULL };
-    char* kwnames2[] = { "filename", "dbtype", "flags", "mode", NULL };
+#if (DBVER >= 41)
+    PyObject *txnobj = NULL;
+    DB_TXN *txn = NULL;
+    /* with dbname */
+    char* kwnames[] = {
+        "filename", "dbname", "dbtype", "flags", "mode", "txn", NULL};
+    /* without dbname */
+    char* kwnames_basic[] = {
+        "filename", "dbtype", "flags", "mode", "txn", NULL};
+#else
+    /* with dbname */
+    char* kwnames[] = {
+        "filename", "dbname", "dbtype", "flags", "mode", NULL};
+    /* without dbname */
+    char* kwnames_basic[] = {
+        "filename", "dbtype", "flags", "mode", NULL};
+#endif
 
+#if (DBVER >= 41)
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|ziiiO:open", kwnames,
+				     &filename, &dbname, &type, &flags, &mode,
+                                     &txnobj))
+#else
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|ziii:open", kwnames,
-                                     &filename, &dbname, &type, &flags, &mode)) {
-        PyErr_Clear();
-        type = DB_UNKNOWN; flags = 0; mode = 0660;
-        filename = NULL; dbname = NULL;
-        if (!PyArg_ParseTupleAndKeywords(args, kwargs,"z|iii:open", kwnames2,
-                                         &filename, &type, &flags, &mode))
-            return NULL;
+				     &filename, &dbname, &type, &flags,
+                                     &mode))
+#endif
+    {
+	PyErr_Clear();
+	type = DB_UNKNOWN; flags = 0; mode = 0660;
+	filename = NULL; dbname = NULL;
+#if (DBVER >= 41)
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs,"z|iiiO:open",
+                                         kwnames_basic,
+					 &filename, &type, &flags, &mode,
+                                         &txnobj))
+	    return NULL;
+#else
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs,"z|iii:open",
+                                         kwnames_basic,
+					 &filename, &type, &flags, &mode))
+	    return NULL;
+#endif
     }
 
+#if (DBVER >= 41)
+    if (!checkTxnObj(txnobj, &txn)) return NULL;
+#endif
+
     if (NULL == self->db) {
         PyErr_SetObject(DBError, Py_BuildValue("(is)", 0,
                                  "Cannot call open() twice for DB object"));
         return NULL;
     }
 
+#if 0 && (DBVER >= 41)
+    if ((!txn) && (txnobj != Py_None) && self->myenvobj
+        && (self->myenvobj->flags & DB_INIT_TXN))
+    {
+	/* If no 'txn' parameter was supplied (no DbTxn object and None was not
+	 * explicitly passed) but we are in a transaction ready environment:
+	 *   add DB_AUTO_COMMIT to allow for older pybsddb apps using transactions
+	 *   to work on BerkeleyDB 4.1 without needing to modify their
+	 *   DBEnv or DB open calls. 
+	 * TODO make this behaviour of the library configurable.
+	 */
+	flags |= DB_AUTO_COMMIT;
+    }
+#endif
+
     MYDB_BEGIN_ALLOW_THREADS;
+#if (DBVER >= 41)
+    err = self->db->open(self->db, txn, filename, dbname, type, flags, mode);
+#else
     err = self->db->open(self->db, filename, dbname, type, flags, mode);
+#endif
     MYDB_END_ALLOW_THREADS;
     if (makeDBError(err)) {
         self->db = NULL;
@@ -1578,7 +1698,8 @@
     char* newname;
     int err, flags=0;
 
-    if (!PyArg_ParseTuple(args, "sss|i:rename", &filename, &database, &newname, &flags))
+    if (!PyArg_ParseTuple(args, "sss|i:rename", &filename, &database, &newname,
+                          &flags))
         return NULL;
     CHECK_DB_NOT_CLOSED(self);
 
@@ -1850,7 +1971,9 @@
         MAKE_HASH_ENTRY(nkeys);
         MAKE_HASH_ENTRY(ndata);
         MAKE_HASH_ENTRY(pagesize);
+#if (DBVER < 41)
         MAKE_HASH_ENTRY(nelem);
+#endif
         MAKE_HASH_ENTRY(ffactor);
         MAKE_HASH_ENTRY(buckets);
         MAKE_HASH_ENTRY(free);
@@ -2021,6 +2144,29 @@
     return PyInt_FromLong(oldValue);
 }
 
+#if (DBVER >= 41)
+static PyObject*
+DB_set_encrypt(DBObject* self, PyObject* args, PyObject* kwargs)
+{
+    int err;
+    u_int32_t flags=0;
+    char *passwd = NULL;
+    char* kwnames[] = { "passwd", "flags", NULL };
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i:set_encrypt", kwnames,
+		&passwd, &flags)) {
+	return NULL;
+    }
+
+    MYDB_BEGIN_ALLOW_THREADS;
+    err = self->db->set_encrypt(self->db, passwd, flags);
+    MYDB_END_ALLOW_THREADS;
+
+    RETURN_IF_ERR();
+    RETURN_NONE();
+}
+#endif /* DBVER >= 41 */
+
 
 /*-------------------------------------------------------------- */
 /* Mapping and Dictionary-like access routines */
@@ -2033,7 +2179,8 @@
     void* sp;
 
     if (self->db == NULL) {
-        PyErr_SetObject(DBError, Py_BuildValue("(is)", 0, "DB object has been closed"));
+        PyErr_SetObject(DBError,
+                        Py_BuildValue("(is)", 0, "DB object has been closed"));
         return -1;
     }
 
@@ -2107,7 +2254,8 @@
     int flags = 0;
 
     if (self->db == NULL) {
-        PyErr_SetObject(DBError, Py_BuildValue("(is)", 0, "DB object has been closed"));
+        PyErr_SetObject(DBError,
+                        Py_BuildValue("(is)", 0, "DB object has been closed"));
         return -1;
     }
 
@@ -2119,11 +2267,13 @@
             retval =  -1;
         else {
             if (self->setflags & (DB_DUP|DB_DUPSORT))
-                flags = DB_NOOVERWRITE;         /* dictionaries shouldn't have duplicate keys */
+                /* dictionaries shouldn't have duplicate keys */
+                flags = DB_NOOVERWRITE;
             retval = _DB_put(self, NULL, &key, &data, flags);
 
             if ((retval == -1) &&  (self->setflags & (DB_DUP|DB_DUPSORT))) {
-                /* try deleting any old record that matches and then PUT it again... */
+                /* try deleting any old record that matches and then PUT it
+                 * again... */
                 _DB_delete(self, NULL, &key, 0);
                 PyErr_Clear();
                 retval = _DB_put(self, NULL, &key, &data, flags);
@@ -2148,7 +2298,7 @@
     PyObject* txnobj = NULL;
     DB_TXN *txn = NULL;
 
-    if (!PyArg_ParseTuple(args,"O|O:has_key", &keyobj, &txnobj ))
+    if (!PyArg_ParseTuple(args,"O|O:has_key", &keyobj, &txnobj))
         return NULL;
     CHECK_DB_NOT_CLOSED(self);
     if (!make_key_dbt(self, keyobj, &key, NULL))
@@ -2244,11 +2394,13 @@
             case DB_BTREE:
             case DB_HASH:
             default:
-                item = Py_BuildValue("s#s#", key.data, key.size, data.data, data.size);
+                item = Py_BuildValue("s#s#", key.data, key.size, data.data,
+                                     data.size);
                 break;
             case DB_RECNO:
             case DB_QUEUE:
-                item = Py_BuildValue("is#", *((db_recno_t*)key.data), data.data, data.size);
+                item = Py_BuildValue("is#", *((db_recno_t*)key.data),
+                                     data.data, data.size);
                 break;
             }
             break;
@@ -2320,7 +2472,6 @@
     return _DB_make_list(self, txn, _VALUES_LIST);
 }
 
-
 /* --------------------------------------------------------------------- */
 /* DBCursor methods */
 
@@ -2433,13 +2584,18 @@
     CLEAR_DBT(key);
     CLEAR_DBT(data);
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|ii:get", &kwnames[2],
-				     &flags, &dlen, &doff)) {
+				     &flags, &dlen, &doff))
+    {
         PyErr_Clear();
-        if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi|ii:get", &kwnames[1], 
-					 &keyobj, &flags, &dlen, &doff)) {
+        if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi|ii:get",
+                                         &kwnames[1], 
+					 &keyobj, &flags, &dlen, &doff))
+        {
             PyErr_Clear();
-            if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOi|ii:get", kwnames,
-				  &keyobj, &dataobj, &flags, &dlen, &doff)) {
+            if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOi|ii:get",
+                                             kwnames, &keyobj, &dataobj,
+                                             &flags, &dlen, &doff))
+            {
                 return NULL;
 	    }
 	}
@@ -2763,7 +2919,8 @@
 
     CLEAR_DBT(key);
     recno = (db_recno_t) irecno;
-    /* use allocated space so DB will be able to realloc room for the real key */
+    /* use allocated space so DB will be able to realloc room for the real
+     * key */
     key.data = malloc(sizeof(db_recno_t));
     if (key.data == NULL) {
         PyErr_SetString(PyExc_MemoryError, "Key memory allocation failed");
@@ -2924,6 +3081,82 @@
     RETURN_NONE();
 }
 
+#if (DBVER >= 41)
+static PyObject*
+DBEnv_dbremove(DBEnvObject* self, PyObject* args, PyObject* kwargs)
+{
+    int err;
+    u_int32_t flags=0;
+    char *file = NULL;
+    char *database = NULL;
+    PyObject *txnobj = NULL;
+    DB_TXN *txn = NULL;
+    char* kwnames[] = { "file", "database", "txn", "flags", NULL };
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss|Oi:dbremove", kwnames,
+		&file, &database, &txnobj, &flags)) {
+	return NULL;
+    }
+    if (!checkTxnObj(txnobj, &txn)) {
+        return NULL;
+    }
+    CHECK_ENV_NOT_CLOSED(self);
+    MYDB_BEGIN_ALLOW_THREADS;
+    err = self->db_env->dbremove(self->db_env, txn, file, database, flags);
+    MYDB_END_ALLOW_THREADS;
+    RETURN_IF_ERR();
+    RETURN_NONE();
+}
+
+static PyObject*
+DBEnv_dbrename(DBEnvObject* self, PyObject* args, PyObject* kwargs)
+{
+    int err;
+    u_int32_t flags=0;
+    char *file = NULL;
+    char *database = NULL;
+    char *newname = NULL;
+    PyObject *txnobj = NULL;
+    DB_TXN *txn = NULL;
+    char* kwnames[] = { "file", "database", "newname", "txn", "flags", NULL };
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sss|Oi:dbrename", kwnames,
+		&file, &database, &newname, &txnobj, &flags)) {
+	return NULL;
+    }
+    if (!checkTxnObj(txnobj, &txn)) {
+        return NULL;
+    }
+    CHECK_ENV_NOT_CLOSED(self);
+    MYDB_BEGIN_ALLOW_THREADS;
+    err = self->db_env->dbrename(self->db_env, txn, file, database, newname,
+                                 flags);
+    MYDB_END_ALLOW_THREADS;
+    RETURN_IF_ERR();
+    RETURN_NONE();
+}
+
+static PyObject*
+DBEnv_set_encrypt(DBEnvObject* self, PyObject* args, PyObject* kwargs)
+{
+    int err;
+    u_int32_t flags=0;
+    char *passwd = NULL;
+    char* kwnames[] = { "passwd", "flags", NULL };
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i:set_encrypt", kwnames,
+		&passwd, &flags)) {
+	return NULL;
+    }
+
+    MYDB_BEGIN_ALLOW_THREADS;
+    err = self->db_env->set_encrypt(self->db_env, passwd, flags);
+    MYDB_END_ALLOW_THREADS;
+
+    RETURN_IF_ERR();
+    RETURN_NONE();
+}
+#endif /* DBVER >= 41 */
 
 static PyObject*
 DBEnv_set_cachesize(DBEnvObject* self, PyObject* args)
@@ -3334,7 +3567,9 @@
 
 #define MAKE_ENTRY(name)  _addIntToDict(d, #name, sp->st_##name)
 
+#if (DBVER < 41)
     MAKE_ENTRY(lastid);
+#endif
     MAKE_ENTRY(nmodes);
 #if (DBVER >= 32)
     MAKE_ENTRY(maxlocks);
@@ -3399,7 +3634,8 @@
             item = PyString_FromString (*log_list);
             if (item == NULL) {
                 Py_DECREF(list);
-                PyErr_SetString(PyExc_MemoryError, "List item creation failed");
+                PyErr_SetString(PyExc_MemoryError,
+                                "List item creation failed");
                 list = NULL;
                 break;
             }
@@ -3612,6 +3848,9 @@
     {"rename",          (PyCFunction)DB_rename,         METH_VARARGS},
     {"set_bt_minkey",   (PyCFunction)DB_set_bt_minkey,  METH_VARARGS},
     {"set_cachesize",   (PyCFunction)DB_set_cachesize,  METH_VARARGS},
+#if (DBVER >= 41)
+    {"set_encrypt",     (PyCFunction)DB_set_encrypt,    METH_VARARGS|METH_KEYWORDS},
+#endif
     {"set_flags",       (PyCFunction)DB_set_flags,      METH_VARARGS},
     {"set_h_ffactor",   (PyCFunction)DB_set_h_ffactor,  METH_VARARGS},
     {"set_h_nelem",     (PyCFunction)DB_set_h_nelem,    METH_VARARGS},
@@ -3676,6 +3915,11 @@
     {"close",           (PyCFunction)DBEnv_close,            METH_VARARGS},
     {"open",            (PyCFunction)DBEnv_open,             METH_VARARGS},
     {"remove",          (PyCFunction)DBEnv_remove,           METH_VARARGS},
+#if (DBVER >= 41)
+    {"dbremove",        (PyCFunction)DBEnv_dbremove,         METH_VARARGS|METH_KEYWORDS},
+    {"dbrename",        (PyCFunction)DBEnv_dbrename,         METH_VARARGS|METH_KEYWORDS},
+    {"set_encrypt",     (PyCFunction)DBEnv_set_encrypt,      METH_VARARGS|METH_KEYWORDS},
+#endif
     {"set_cachesize",   (PyCFunction)DBEnv_set_cachesize,    METH_VARARGS},
     {"set_data_dir",    (PyCFunction)DBEnv_set_data_dir,     METH_VARARGS},
 #if (DBVER >= 32)
@@ -3866,7 +4110,8 @@
     int flags = 0;
     char* kwnames[] = { "dbEnv", "flags", NULL};
 
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:DB", kwnames, &dbenvobj, &flags))
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:DB", kwnames,
+                                     &dbenvobj, &flags))
         return NULL;
     if (dbenvobj == Py_None)
         dbenvobj = NULL;
@@ -4036,7 +4281,8 @@
 
 
 #if (DBVER >= 33)
-    _addIntToDict(d, "DB_LOCK_CONFLICT", 0);   /* docs say to use zero instead */
+    /* docs say to use zero instead */
+    _addIntToDict(d, "DB_LOCK_CONFLICT", 0);
 #else
     ADD_INT(d, DB_LOCK_CONFLICT);
 #endif
@@ -4111,7 +4357,12 @@
     ADD_INT(d, DB_APPEND);
     ADD_INT(d, DB_BEFORE);
     ADD_INT(d, DB_CACHED_COUNTS);
+#if (DBVER >= 41)
+    _addIntToDict(d, "DB_CHECKPOINT", 0);
+#else
     ADD_INT(d, DB_CHECKPOINT);
+    ADD_INT(d, DB_CURLSN);
+#endif
 #if (DBVER >= 33)
     ADD_INT(d, DB_COMMIT);
 #endif
@@ -4119,7 +4370,6 @@
 #if (DBVER >= 32)
     ADD_INT(d, DB_CONSUME_WAIT);
 #endif
-    ADD_INT(d, DB_CURLSN);
     ADD_INT(d, DB_CURRENT);
 #if (DBVER >= 33)
     ADD_INT(d, DB_FAST_STAT);
@@ -4159,7 +4409,11 @@
     ADD_INT(d, DB_DONOTINDEX);
 #endif
 
+#if (DBVER >= 41)
+    _addIntToDict(d, "DB_INCOMPLETE", 0);
+#else
     ADD_INT(d, DB_INCOMPLETE);
+#endif
     ADD_INT(d, DB_KEYEMPTY);
     ADD_INT(d, DB_KEYEXIST);
     ADD_INT(d, DB_LOCK_DEADLOCK);
@@ -4184,6 +4438,14 @@
     ADD_INT(d, DB_NOPANIC);
 #endif
 
+#if (DBVER >= 41)
+    ADD_INT(d, DB_ENCRYPT_AES);
+    ADD_INT(d, DB_AUTO_COMMIT);
+#else
+    /* allow berkeleydb 4.1 aware apps to run on older versions */
+    _addIntToDict(d, "DB_AUTO_COMMIT", 0);
+#endif
+
     ADD_INT(d, EINVAL);
     ADD_INT(d, EACCES);
     ADD_INT(d, ENOSPC);
@@ -4197,7 +4459,7 @@
 
 
     /* The base exception class is DBError */
-    DBError = PyErr_NewException("bsddb3._db.DBError", NULL, NULL);
+    DBError = PyErr_NewException("bsddb._db.DBError", NULL, NULL);
     PyDict_SetItemString(d, "DBError", DBError);
 
     /* Some magic to make DBNotFoundError derive from both DBError and
@@ -4210,7 +4472,7 @@
 
 
     /* All the rest of the exceptions derive only from DBError */
-#define MAKE_EX(name)   name = PyErr_NewException("bsddb3._db." #name, DBError, NULL); \
+#define MAKE_EX(name)   name = PyErr_NewException("bsddb._db." #name, DBError, NULL); \
                         PyDict_SetItemString(d, #name, name)
 
 #if !INCOMPLETE_IS_WARNING
@@ -4246,9 +4508,6 @@
     /* Check for errors */
     if (PyErr_Occurred()) {
         PyErr_Print();
-        Py_FatalError("can't initialize module _db");
+        Py_FatalError("can't initialize module _bsddb");
     }
 }
-
-
-