]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add MongoDB CDR module
authorDaniel Swarbrick <daniel@seventhsignal.de>
Wed, 7 Sep 2011 21:50:25 +0000 (23:50 +0200)
committerDaniel Swarbrick <daniel@seventhsignal.de>
Wed, 7 Sep 2011 21:50:25 +0000 (23:50 +0200)
22 files changed:
src/mod/event_handlers/mod_cdr_mongodb/Makefile [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/APACHE-2.0.txt [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/README.md [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/SConstruct [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.h [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.h [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.c [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.h [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.c [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.h [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/numbers.c [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform.h [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.c [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.h [new file with mode: 0644]
src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c [new file with mode: 0644]

diff --git a/src/mod/event_handlers/mod_cdr_mongodb/Makefile b/src/mod/event_handlers/mod_cdr_mongodb/Makefile
new file mode 100644 (file)
index 0000000..b9e04b9
--- /dev/null
@@ -0,0 +1,9 @@
+include ../../../../build/modmake.rules
+
+MONGODB_DRIVER=./driver/src
+LOCAL_CFLAGS=-I$(MONGODB_DRIVER)
+LOCAL_OBJS=$(MONGODB_DRIVER)/md5.o \
+          $(MONGODB_DRIVER)/mongo.o $(MONGODB_DRIVER)/net.o \
+          $(MONGODB_DRIVER)/bson.o $(MONGODB_DRIVER)/numbers.o $(MONGODB_DRIVER)/encoding.o \
+
+local_depend: $(LOCAL_OBJS)
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/APACHE-2.0.txt b/src/mod/event_handlers/mod_cdr_mongodb/driver/APACHE-2.0.txt
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md b/src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md
new file mode 100644 (file)
index 0000000..f3f0fab
--- /dev/null
@@ -0,0 +1,139 @@
+# MongoDB C Driver History
+
+## 0.4
+
+THIS RELEASE INCLUDES NUMEROUS BACKWARD-BREAKING CHANGES.
+These changes have been made for extensibility, consistency,
+and ease of use. Please read the following release notes
+carefully, and study the updated tutorial.
+
+API Principles:
+
+1. Present a consistent interface for all objects: connections,
+   cursors, bson objects, and bson iterators.
+2. Require no knowledge of an object's implementation to use the API.
+3. Allow users to allocate objects on the stack or on the heap.
+4. Integrate API with new error reporting strategy.
+5. Be concise, except where it impairs clarity.
+
+Changes:
+
+* mongo_replset_init_conn has been renamed to mongo_replset_init.
+* bson_buffer has been removed. All functions for building bson
+  objects now take objects of type bson. The new pattern looks like this:
+
+  Example:
+
+    bson b[1];
+    bson_init( b );
+    bson_append_int( b, "foo", 1 );
+    bson_finish( b );
+    /* The object is ready to use.
+       When finished, destroy it. */
+    bson_destroy( b );
+
+* mongo_connection has been renamed to mongo.
+
+  Example:
+
+    mongo conn[1];
+    mongo_connect( conn, '127.0.0.1', 27017 );
+    /* Connection is ready. Destroy when down. */
+    mongo_destroy( conn );
+
+* New cursor builder API for clearer code:
+
+  Example:
+
+    mongo_cursor cursor[1];
+    mongo_cursor_init( cursor, conn, "test.foo" );
+
+    bson query[1];
+
+    bson_init( query );
+    bson_append_int( query, "bar", 1 );
+    bson_finish( query );
+
+    bson fields[1];
+
+    bson_init( fields );
+    bson_append_int( fields, "baz", 1 );
+    bson_finish( fields );
+
+    mongo_cursor_set_query( cursor, query );
+    mongo_cursor_set_fields( cursor, fields );
+    mongo_cursor_set_limit( cursor, 10 );
+    mongo_cursor_set_skip( cursor, 10 );
+
+    while( mongo_cursor_next( cursor ) == MONGO_OK )
+        bson_print( mongo_cursor_bson( cursor ) );
+
+* bson_iterator_init now takes a (bson*) instead of a (const char*). This is consistent
+  with bson_find, which also takes a (bson*). If you want to initiate a bson iterator
+  with a buffer, use the new function bson_iterator_from_buffer.
+* With the addition of the mongo_cursor_bson function, it's now no
+  longer necessary to know how bson and mongo_cursor objects are implemented.
+
+  Example:
+
+    bson b[1];
+    bson_iterator i[1];
+
+    bson_iterator_init( i, b );
+
+    /* With a cursor */
+    bson_iterator_init( i, mongo_cursor_bson( cursor ) );
+
+* Added mongo_cursor_data and bson_data functions, which return the
+  raw bson buffer as a (const char *).
+* All constants that were once lower case are now
+  upper case. These include: MONGO_OP_MSG, MONGO_OP_UPDATE, MONGO_OP_INSERT,
+  MONGO_OP_QUERY, MONGO_OP_GET_MORE, MONGO_OP_DELETE, MONGO_OP_KILL_CURSORS
+  BSON_EOO, BSON_DOUBLE, BSON_STRING, BSON_OBJECT, BSON_ARRAY, BSON_BINDATA,
+  BSON_UNDEFINED, BSON_OID, BSON_BOOL, BSON_DATE, BSON_NULL, BSON_REGEX, BSON_DBREF,
+  BSON_CODE, BSON_SYMBOL, BSON_CODEWSCOPE, BSON_INT, BSON_TIMESTAMP, BSON_LONG,
+  MONGO_CONN_SUCCESS, MONGO_CONN_BAD_ARG, MONGO_CONN_NO_SOCKET, MONGO_CONN_FAIL,
+  MONGO_CONN_NOT_MASTER, MONGO_CONN_BAD_SET_NAME, MONGO_CONN_CANNOT_FIND_PRIMARY 
+  If your programs use any of these constants, you must convert them to their
+  upper case forms, or you will see compile errors.
+* The error handling strategy has been changed. Exceptions are not longer being used.
+* Functions taking a mongo_connection object now return either MONGO_OK or MONGO_ERROR.
+  In case of an error, an error code of type mongo_error_t will be indicated on the
+  mongo_connection->err field.
+* Functions taking a bson object now return either BSON_OK or BSON_ERROR.
+  In case of an error, an error code of type bson_validity_t will be indicated on the
+  bson->err or bson_buffer->err field.
+* Calls to mongo_cmd_get_last_error store the error status on the
+  mongo->lasterrcode and mongo->lasterrstr fields.
+* bson_print now prints all types.
+* Users may now set custom malloc, realloc, free, printf, sprintf, and fprintf fields.
+* Groundwork for modules for supporting platform-specific features (e.g., socket timeouts).
+* Added mongo_set_op_timeout for setting socket timeout. To take advantage of this, you must
+  compile with --use-platform=LINUX. The compiles with platform/linux/net.h instead of the
+  top-level net.h.
+* Fixed tailable cursors.
+* GridFS API is now in-line with the new driver API. In particular, all of the
+  following functions now return MONGO_OK or MONGO_ERROR: gridfs_init,
+  gridfile_init, gridfile_writer_done, gridfs_store_buffer, gridfs_store_file,
+  and gridfs_find_query.
+* Fixed a few memory leaks.
+
+## 0.3
+2011-4-14
+
+* Support replica sets.
+* Better standard connection API.
+* GridFS write buffers iteratively.
+* Fixes for working with large GridFS files (> 3GB)
+* bson_append_string_n and family (Gergely Nagy)
+
+## 0.2
+2011-2-11
+
+* GridFS support (Chris Triolo).
+* BSON Timestamp type support.
+
+## 0.1
+2009-11-30
+
+* Initial release.
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/README.md b/src/mod/event_handlers/mod_cdr_mongodb/driver/README.md
new file mode 100644 (file)
index 0000000..1afe39e
--- /dev/null
@@ -0,0 +1,60 @@
+# MongoDB C Driver
+
+This is then 10gen-supported MongoDB C driver. There are two goals for this driver.
+The first is to provide a strict, default compilation option for ultimate portability,
+no dependencies, and generic embeddability.
+
+The second is to support more advanced, platform-specific features, like socket timeout,
+by providing an interface for platform-specific modules.
+
+Until the 1.0 release, this driver should be considered alpha. Keep in mind that the API will be in flux until then.
+
+# Building
+
+First check out the version you want to build. *Always build from a particular tag, since HEAD may be
+a work in progress.* For example, to build version 0.4, run:
+
+    git checkout v0.4
+
+You can then build the driver with scons:
+
+    scons
+
+## Running the tests
+Make sure that you're running mongod on 127.0.0.1 on the default port (27017). The replica set
+test assumes a replica set with at least three nodes running at 127.0.0.1 and starting at port
+30000. Note that the driver does not recognize 'localhost' as a valid host name.
+
+To compile and run the tests:
+
+    scons test
+
+# Error Handling
+Most functions return MONGO_OK or BSON_OK on success and MONGO_ERROR or BSON_ERROR on failure.
+Specific error codes and error strings are then stored in the `err` and `errstr` fields of the
+`mongo` and `bson` objects. It is the client's responsibility to check for errors and handle
+them appropriately.
+
+# Docs
+The docs are built using Sphinx and Doxygen. If you have these tools installed, then
+you can build the docs with scons:
+
+    scons docs
+
+The html docs will appear in docs/html.
+
+# ISSUES
+
+You can report bugs, request new features, and view this driver's roadmap
+using [JIRA](http://jira.mongodb.org/browse/CDRIVER).
+
+# CREDITS
+
+* Gergely Nagy - Non-null-terminated string support.
+* Josh Rotenberg - Initial Doxygen setup and a significant chunk of documentation.
+
+# LICENSE
+
+Unless otherwise specified in a source file, sources in this
+repository are published under the terms of the Apache License version
+2.0, a copy of which is in this repository as APACHE-2.0.txt.
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/SConstruct b/src/mod/event_handlers/mod_cdr_mongodb/driver/SConstruct
new file mode 100644 (file)
index 0000000..d3be2d8
--- /dev/null
@@ -0,0 +1,178 @@
+# -*- mode: python; -*-
+
+VERSION = "0.4"
+
+# --- options ----
+AddOption('--test-server',
+          dest='test_server',
+          default='127.0.0.1',
+          type='string',
+          nargs=1,
+          action='store',
+          help='IP address of server to use for testing')
+
+AddOption('--seed-start-port',
+          dest='seed_start_port',
+          default=30000,
+          type='int',
+          nargs=1,
+          action='store',
+          help='IP address of server to use for testing')
+
+AddOption('--c99',
+          dest='use_c99',
+          default=False,
+          action='store_true',
+          help='Compile with c99 (recommended for gcc)')
+
+AddOption('--d',
+          dest='optimize',
+          default=True,
+          action='store_false',
+          help='disable optimizations')
+
+AddOption('--use-platform',
+          dest='compile_platform',
+          default='GENERIC',
+          type='string',
+          nargs=1,
+          action='store',
+          help='Compile for a specific platform to take advantage '
+               ' of particular system features. For the moment, this include timeouts only.'
+               ' Current options include LINUX, '
+               ' GENERIC, and CUSTOM. If you specific CUSTOM, you must place a'
+               ' system-specific implementation of net.h and net.c in src/platform/custom/')
+
+import os, sys
+
+env = Environment( ENV=os.environ )
+
+#  ---- Docs ----
+def build_docs(env, target, source):
+    buildscript_path = os.path.join(os.path.abspath("docs"))
+    sys.path.insert(0, buildscript_path)
+    import buildscripts
+    from buildscripts import docs
+    docs.main()
+
+env.Alias("docs", [], [build_docs])
+env.AlwaysBuild("docs")
+
+# ---- Platforms ----
+PLATFORM_TEST_DIR = None
+if "LINUX" == GetOption('compile_platform'):
+    env.Append( CPPFLAGS=" -D_MONGO_USE_LINUX_SYSTEM" )
+    NET_LIB = "src/platform/linux/net.c"
+    PLATFORM_TEST_DIR = "test/platform/linux/"
+    PLATFORM_TESTS = [ "timeouts" ]
+elif "CUSTOM" == GetOption('compile_platform'):
+    env.Append( CPPFLAGS=" -D_MONGO_USE_CUSTOM_SYSTEM" )
+    NET_LIB = "src/platform/custom/net.c"
+else:
+    NET_LIB = "src/net.c"
+
+# ---- Libraries ----
+if os.sys.platform in ["darwin", "linux2"]:
+    env.Append( CPPFLAGS=" -pedantic -Wall -ggdb -DMONGO_HAVE_STDINT" )
+    env.Append( CPPPATH=["/opt/local/include/"] )
+    env.Append( LIBPATH=["/opt/local/lib/"] )
+
+    if GetOption('use_c99'):
+        env.Append( CFLAGS=" -std=c99 " )
+        env.Append( CXXDEFINES="MONGO_HAVE_STDINT" )
+    else:
+        env.Append( CFLAGS=" -ansi " )
+
+    if GetOption('optimize'):
+        env.Append( CPPFLAGS=" -O3 " )
+        # -O3 benchmarks *significantly* faster than -O2 when disabling networking
+elif 'win32' == os.sys.platform:
+    env.Append( LIBS='ws2_32' )
+
+#we shouldn't need these options in c99 mode
+if not GetOption('use_c99'):
+    conf = Configure(env)
+
+    if not conf.CheckType('int64_t'):
+        if conf.CheckType('int64_t', '#include <stdint.h>\n'):
+            conf.env.Append( CPPDEFINES="MONGO_HAVE_STDINT" )
+        elif conf.CheckType('int64_t', '#include <unistd.h>\n'):
+            conf.env.Append( CPPDEFINES="MONGO_HAVE_UNISTD" )
+        elif conf.CheckType('__int64'):
+            conf.env.Append( CPPDEFINES="MONGO_USE__INT64" )
+        elif conf.CheckType('long long int'):
+            conf.env.Append( CPPDEFINES="MONGO_USE_LONG_LONG_INT" )
+        else:
+            print "*** what is your 64 bit int type? ****"
+            Exit(1)
+
+    env = conf.Finish()
+
+have_libjson = False
+conf = Configure(env)
+if conf.CheckLib('json'):
+    have_libjson = True
+env = conf.Finish()
+
+if sys.byteorder == 'big':
+    env.Append( CPPDEFINES="MONGO_BIG_ENDIAN" )
+
+env.Append( CPPPATH=["src/"] )
+
+coreFiles = ["src/md5.c" ]
+mFiles = [ "src/mongo.c", NET_LIB, "src/gridfs.c"]
+bFiles = [ "src/bson.c", "src/numbers.c", "src/encoding.c"]
+mLibFiles = coreFiles + mFiles + bFiles
+bLibFiles = coreFiles + bFiles
+m = env.Library( "mongoc" ,  mLibFiles )
+b = env.Library( "bson" , bLibFiles  )
+env.Default( env.Alias( "lib" , [ m[0] , b[0] ] ) )
+
+if os.sys.platform == "linux2":
+    env.Append( SHLINKFLAGS="-shared -Wl,-soname,libmongoc.so." + VERSION )
+    env.Append( SHLINKFLAGS = "-shared -Wl,-soname,libbson.so." + VERSION )
+
+dynm = env.SharedLibrary( "mongoc" , mLibFiles )
+dynb = env.SharedLibrary( "bson" , bLibFiles )
+env.Default( env.Alias( "sharedlib" , [ dynm[0] , dynb[0] ] ) )
+
+
+
+# ---- Benchmarking ----
+benchmarkEnv = env.Clone()
+benchmarkEnv.Append( CPPDEFINES=[('TEST_SERVER', r'\"%s\"'%GetOption('test_server')),
+('SEED_START_PORT', r'%d'%GetOption('seed_start_port'))] )
+benchmarkEnv.Append( LIBS=[m, b] )
+benchmarkEnv.Prepend( LIBPATH=["."] )
+benchmarkEnv.Program( "benchmark" ,  [ "test/benchmark.c"] )
+
+# ---- Tests ----
+testEnv = benchmarkEnv.Clone()
+testCoreFiles = [ ]
+
+def run_tests( root, tests ):
+    for name in tests:
+        filename = "%s/%s.c" % (root, name)
+        exe = "test_" + name
+        test = testEnv.Program( exe , testCoreFiles + [filename]  )
+        test_alias = testEnv.Alias('test', [test], test[0].abspath + ' 2> ' + os.path.devnull)
+        AlwaysBuild(test_alias)
+
+tests = Split("sizes resize endian_swap bson bson_subobject simple update errors "
+"count_delete auth gridfs validate examples helpers oid functions cursors replica_set")
+
+# Run standard tests
+run_tests("test", tests)
+
+# Run platform tests
+if not PLATFORM_TEST_DIR is None:
+    run_tests( PLATFORM_TEST_DIR, PLATFORM_TESTS )
+
+if have_libjson:
+    tests.append('json')
+    testEnv.Append( LIBS=["json"] )
+
+# special case for cpptest
+test = testEnv.Program( 'test_cpp' , testCoreFiles + ['test/cpptest.cpp']  )
+test_alias = testEnv.Alias('test', [test], test[0].abspath + ' 2> '+ os.path.devnull)
+AlwaysBuild(test_alias)
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c
new file mode 100644 (file)
index 0000000..349f422
--- /dev/null
@@ -0,0 +1,978 @@
+/* bson.c */
+
+/*    Copyright 2009, 2010 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <limits.h>
+
+#include "bson.h"
+#include "encoding.h"
+
+const int initialBufferSize = 128;
+
+/* only need one of these */
+static const int zero = 0;
+
+/* Custom standard function pointers. */
+void *( *bson_malloc_func )( size_t ) = malloc;
+void *( *bson_realloc_func )( void *, size_t ) = realloc;
+void  ( *bson_free )( void * ) = free;
+bson_printf_func bson_printf = printf;
+bson_fprintf_func bson_fprintf = fprintf;
+bson_sprintf_func bson_sprintf = sprintf;
+
+static int _bson_errprintf( const char *, ... );
+bson_printf_func bson_errprintf = _bson_errprintf;
+
+/* ObjectId fuzz functions. */
+static int ( *oid_fuzz_func )( void ) = NULL;
+static int ( *oid_inc_func )( void )  = NULL;
+
+/* ----------------------------
+   READING
+   ------------------------------ */
+
+bson *bson_empty( bson *obj ) {
+    static char *data = "\005\0\0\0\0";
+    bson_init_data( obj, data );
+    obj->finished = 1;
+    obj->err = 0;
+    obj->stackPos = 0;
+    return obj;
+}
+
+void bson_copy_basic( bson *out, const bson *in ) {
+    if ( !out ) return;
+    bson_init_size( out, bson_size( in ) );
+    memcpy( out->data, in->data, bson_size( in ) );
+}
+
+void bson_copy( bson *out, const bson *in ) {
+    int i;
+
+    if ( !out ) return;
+    bson_copy_basic( out, in );
+    out->cur = out->data + ( in->cur - in->data );
+    out->dataSize = in->dataSize;
+    out->finished = in->finished;
+    out->stackPos = in->stackPos;
+    out->err = in->err;
+    for( i=0; i<out->stackPos; i++ )
+        out->stack[i] = in->stack[i];
+}
+
+int bson_init_data( bson *b, char *data ) {
+    b->data = data;
+    return BSON_OK;
+}
+
+static void _bson_reset( bson *b ) {
+    b->finished = 0;
+    b->stackPos = 0;
+    b->err = 0;
+    b->errstr = NULL;
+}
+
+int bson_size( const bson *b ) {
+    int i;
+    if ( ! b || ! b->data )
+        return 0;
+    bson_little_endian32( &i, b->data );
+    return i;
+}
+
+const char *bson_data( bson *b ) {
+    return (const char *)b->data;
+}
+
+static char hexbyte( char hex ) {
+    switch ( hex ) {
+    case '0':
+        return 0x0;
+    case '1':
+        return 0x1;
+    case '2':
+        return 0x2;
+    case '3':
+        return 0x3;
+    case '4':
+        return 0x4;
+    case '5':
+        return 0x5;
+    case '6':
+        return 0x6;
+    case '7':
+        return 0x7;
+    case '8':
+        return 0x8;
+    case '9':
+        return 0x9;
+    case 'a':
+    case 'A':
+        return 0xa;
+    case 'b':
+    case 'B':
+        return 0xb;
+    case 'c':
+    case 'C':
+        return 0xc;
+    case 'd':
+    case 'D':
+        return 0xd;
+    case 'e':
+    case 'E':
+        return 0xe;
+    case 'f':
+    case 'F':
+        return 0xf;
+    default:
+        return 0x0; /* something smarter? */
+    }
+}
+
+void bson_oid_from_string( bson_oid_t *oid, const char *str ) {
+    int i;
+    for ( i=0; i<12; i++ ) {
+        oid->bytes[i] = ( hexbyte( str[2*i] ) << 4 ) | hexbyte( str[2*i + 1] );
+    }
+}
+
+void bson_oid_to_string( const bson_oid_t *oid, char *str ) {
+    static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+    int i;
+    for ( i=0; i<12; i++ ) {
+        str[2*i]     = hex[( oid->bytes[i] & 0xf0 ) >> 4];
+        str[2*i + 1] = hex[ oid->bytes[i] & 0x0f      ];
+    }
+    str[24] = '\0';
+}
+
+void bson_set_oid_fuzz( int ( *func )( void ) ) {
+    oid_fuzz_func = func;
+}
+
+void bson_set_oid_inc( int ( *func )( void ) ) {
+    oid_inc_func = func;
+}
+
+void bson_oid_gen( bson_oid_t *oid ) {
+    static int incr = 0;
+    static int fuzz = 0;
+    int i;
+    int t = time( NULL );
+
+    if( oid_inc_func )
+        i = oid_inc_func();
+    else
+        i = incr++;
+
+    if ( !fuzz ) {
+        if ( oid_fuzz_func )
+            fuzz = oid_fuzz_func();
+        else {
+            srand( t );
+            fuzz = rand();
+        }
+    }
+
+    bson_big_endian32( &oid->ints[0], &t );
+    oid->ints[1] = fuzz;
+    bson_big_endian32( &oid->ints[2], &i );
+}
+
+time_t bson_oid_generated_time( bson_oid_t *oid ) {
+    time_t out;
+    bson_big_endian32( &out, &oid->ints[0] );
+
+    return out;
+}
+
+void bson_print( bson *b ) {
+    bson_print_raw( b->data , 0 );
+}
+
+void bson_print_raw( const char *data , int depth ) {
+    bson_iterator i;
+    const char *key;
+    int temp;
+    bson_timestamp_t ts;
+    char oidhex[25];
+    bson scope;
+    bson_iterator_from_buffer( &i, data );
+
+    while ( bson_iterator_next( &i ) ) {
+        bson_type t = bson_iterator_type( &i );
+        if ( t == 0 )
+            break;
+        key = bson_iterator_key( &i );
+
+        for ( temp=0; temp<=depth; temp++ )
+            printf( "\t" );
+        bson_printf( "%s : %d \t " , key , t );
+        switch ( t ) {
+        case BSON_DOUBLE:
+            printf( "%f" , bson_iterator_double( &i ) );
+            break;
+        case BSON_STRING:
+            printf( "%s" , bson_iterator_string( &i ) );
+            break;
+        case BSON_SYMBOL:
+            printf( "SYMBOL: %s" , bson_iterator_string( &i ) );
+            break;
+        case BSON_OID:
+            bson_oid_to_string( bson_iterator_oid( &i ), oidhex );
+            printf( "%s" , oidhex );
+            break;
+        case BSON_BOOL:
+            printf( "%s" , bson_iterator_bool( &i ) ? "true" : "false" );
+            break;
+        case BSON_DATE:
+            printf( "%ld" , ( long int )bson_iterator_date( &i ) );
+            break;
+        case BSON_BINDATA:
+            printf( "BSON_BINDATA" );
+            break;
+        case BSON_UNDEFINED:
+            printf( "BSON_UNDEFINED" );
+            break;
+        case BSON_NULL:
+            printf( "BSON_NULL" );
+            break;
+        case BSON_REGEX:
+            printf( "BSON_REGEX: %s", bson_iterator_regex( &i ) );
+            break;
+        case BSON_CODE:
+            printf( "BSON_CODE: %s", bson_iterator_code( &i ) );
+            break;
+        case BSON_CODEWSCOPE:
+            printf( "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) );
+            bson_init( &scope );
+            bson_iterator_code_scope( &i, &scope );
+            printf( "\n\t SCOPE: " );
+            bson_print( &scope );
+            break;
+        case BSON_INT:
+            printf( "%d" , bson_iterator_int( &i ) );
+            break;
+        case BSON_LONG:
+            printf( "%lld" , ( long long int )bson_iterator_long( &i ) );
+            break;
+        case BSON_TIMESTAMP:
+            ts = bson_iterator_timestamp( &i );
+            printf( "i: %d, t: %d", ts.i, ts.t );
+            break;
+        case BSON_OBJECT:
+        case BSON_ARRAY:
+            printf( "\n" );
+            bson_print_raw( bson_iterator_value( &i ) , depth + 1 );
+            break;
+        default:
+            bson_errprintf( "can't print type : %d\n" , t );
+        }
+        printf( "\n" );
+    }
+}
+
+/* ----------------------------
+   ITERATOR
+   ------------------------------ */
+
+void bson_iterator_init( bson_iterator *i, const bson *b ) {
+    i->cur = b->data + 4;
+    i->first = 1;
+}
+
+void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ) {
+    i->cur = buffer + 4;
+    i->first = 1;
+}
+
+bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ) {
+    bson_iterator_init( it, (bson *)obj );
+    while( bson_iterator_next( it ) ) {
+        if ( strcmp( name, bson_iterator_key( it ) ) == 0 )
+            break;
+    }
+    return bson_iterator_type( it );
+}
+
+bson_bool_t bson_iterator_more( const bson_iterator *i ) {
+    return *( i->cur );
+}
+
+bson_type bson_iterator_next( bson_iterator *i ) {
+    int ds;
+
+    if ( i->first ) {
+        i->first = 0;
+        return ( bson_type )( *i->cur );
+    }
+
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_EOO:
+        return BSON_EOO; /* don't advance */
+    case BSON_UNDEFINED:
+    case BSON_NULL:
+        ds = 0;
+        break;
+    case BSON_BOOL:
+        ds = 1;
+        break;
+    case BSON_INT:
+        ds = 4;
+        break;
+    case BSON_LONG:
+    case BSON_DOUBLE:
+    case BSON_TIMESTAMP:
+    case BSON_DATE:
+        ds = 8;
+        break;
+    case BSON_OID:
+        ds = 12;
+        break;
+    case BSON_STRING:
+    case BSON_SYMBOL:
+    case BSON_CODE:
+        ds = 4 + bson_iterator_int_raw( i );
+        break;
+    case BSON_BINDATA:
+        ds = 5 + bson_iterator_int_raw( i );
+        break;
+    case BSON_OBJECT:
+    case BSON_ARRAY:
+    case BSON_CODEWSCOPE:
+        ds = bson_iterator_int_raw( i );
+        break;
+    case BSON_DBREF:
+        ds = 4+12 + bson_iterator_int_raw( i );
+        break;
+    case BSON_REGEX: {
+        const char *s = bson_iterator_value( i );
+        const char *p = s;
+        p += strlen( p )+1;
+        p += strlen( p )+1;
+        ds = p-s;
+        break;
+    }
+
+    default: {
+        char msg[] = "unknown type: 000000000000";
+        bson_numstr( msg+14, ( unsigned )( i->cur[0] ) );
+        bson_fatal_msg( 0, msg );
+        return 0;
+    }
+    }
+
+    i->cur += 1 + strlen( i->cur + 1 ) + 1 + ds;
+
+    return ( bson_type )( *i->cur );
+}
+
+bson_type bson_iterator_type( const bson_iterator *i ) {
+    return ( bson_type )i->cur[0];
+}
+
+const char *bson_iterator_key( const bson_iterator *i ) {
+    return i->cur + 1;
+}
+
+const char *bson_iterator_value( const bson_iterator *i ) {
+    const char *t = i->cur + 1;
+    t += strlen( t ) + 1;
+    return t;
+}
+
+/* types */
+
+int bson_iterator_int_raw( const bson_iterator *i ) {
+    int out;
+    bson_little_endian32( &out, bson_iterator_value( i ) );
+    return out;
+}
+
+double bson_iterator_double_raw( const bson_iterator *i ) {
+    double out;
+    bson_little_endian64( &out, bson_iterator_value( i ) );
+    return out;
+}
+
+int64_t bson_iterator_long_raw( const bson_iterator *i ) {
+    int64_t out;
+    bson_little_endian64( &out, bson_iterator_value( i ) );
+    return out;
+}
+
+bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ) {
+    return bson_iterator_value( i )[0];
+}
+
+bson_oid_t *bson_iterator_oid( const bson_iterator *i ) {
+    return ( bson_oid_t * )bson_iterator_value( i );
+}
+
+int bson_iterator_int( const bson_iterator *i ) {
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_INT:
+        return bson_iterator_int_raw( i );
+    case BSON_LONG:
+        return bson_iterator_long_raw( i );
+    case BSON_DOUBLE:
+        return bson_iterator_double_raw( i );
+    default:
+        return 0;
+    }
+}
+
+double bson_iterator_double( const bson_iterator *i ) {
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_INT:
+        return bson_iterator_int_raw( i );
+    case BSON_LONG:
+        return bson_iterator_long_raw( i );
+    case BSON_DOUBLE:
+        return bson_iterator_double_raw( i );
+    default:
+        return 0;
+    }
+}
+
+int64_t bson_iterator_long( const bson_iterator *i ) {
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_INT:
+        return bson_iterator_int_raw( i );
+    case BSON_LONG:
+        return bson_iterator_long_raw( i );
+    case BSON_DOUBLE:
+        return bson_iterator_double_raw( i );
+    default:
+        return 0;
+    }
+}
+
+bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ) {
+    bson_timestamp_t ts;
+    bson_little_endian32( &( ts.i ), bson_iterator_value( i ) );
+    bson_little_endian32( &( ts.t ), bson_iterator_value( i ) + 4 );
+    return ts;
+}
+
+bson_bool_t bson_iterator_bool( const bson_iterator *i ) {
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_BOOL:
+        return bson_iterator_bool_raw( i );
+    case BSON_INT:
+        return bson_iterator_int_raw( i ) != 0;
+    case BSON_LONG:
+        return bson_iterator_long_raw( i ) != 0;
+    case BSON_DOUBLE:
+        return bson_iterator_double_raw( i ) != 0;
+    case BSON_EOO:
+    case BSON_NULL:
+        return 0;
+    default:
+        return 1;
+    }
+}
+
+const char *bson_iterator_string( const bson_iterator *i ) {
+    return bson_iterator_value( i ) + 4;
+}
+
+int bson_iterator_string_len( const bson_iterator *i ) {
+    return bson_iterator_int_raw( i );
+}
+
+const char *bson_iterator_code( const bson_iterator *i ) {
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_STRING:
+    case BSON_CODE:
+        return bson_iterator_value( i ) + 4;
+    case BSON_CODEWSCOPE:
+        return bson_iterator_value( i ) + 8;
+    default:
+        return NULL;
+    }
+}
+
+void bson_iterator_code_scope( const bson_iterator *i, bson *scope ) {
+    if ( bson_iterator_type( i ) == BSON_CODEWSCOPE ) {
+        int code_len;
+        bson_little_endian32( &code_len, bson_iterator_value( i )+4 );
+        bson_init_data( scope, ( void * )( bson_iterator_value( i )+8+code_len ) );
+    } else {
+        bson_empty( scope );
+    }
+}
+
+bson_date_t bson_iterator_date( const bson_iterator *i ) {
+    return bson_iterator_long_raw( i );
+}
+
+time_t bson_iterator_time_t( const bson_iterator *i ) {
+    return bson_iterator_date( i ) / 1000;
+}
+
+int bson_iterator_bin_len( const bson_iterator *i ) {
+    return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
+           ? bson_iterator_int_raw( i ) - 4
+           : bson_iterator_int_raw( i );
+}
+
+char bson_iterator_bin_type( const bson_iterator *i ) {
+    return bson_iterator_value( i )[4];
+}
+
+const char *bson_iterator_bin_data( const bson_iterator *i ) {
+    return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
+           ? bson_iterator_value( i ) + 9
+           : bson_iterator_value( i ) + 5;
+}
+
+const char *bson_iterator_regex( const bson_iterator *i ) {
+    return bson_iterator_value( i );
+}
+
+const char *bson_iterator_regex_opts( const bson_iterator *i ) {
+    const char *p = bson_iterator_value( i );
+    return p + strlen( p ) + 1;
+
+}
+
+void bson_iterator_subobject( const bson_iterator *i, bson *sub ) {
+    bson_init_data( sub, ( char * )bson_iterator_value( i ) );
+    _bson_reset( sub );
+    sub->finished = 1;
+}
+
+void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ) {
+    bson_iterator_from_buffer( sub, bson_iterator_value( i ) );
+}
+
+/* ----------------------------
+   BUILDING
+   ------------------------------ */
+
+static void _bson_init_size( bson *b, int size ) {
+    if( size == 0 )
+        b->data = NULL;
+    else
+        b->data = ( char * )bson_malloc( size );
+    b->dataSize = size;
+    b->cur = b->data + 4;
+    _bson_reset( b );
+}
+
+void bson_init( bson *b ) {
+    _bson_init_size( b, initialBufferSize );
+}
+
+void bson_init_size( bson *b, int size ) {
+    _bson_init_size( b, size );
+}
+
+void bson_append_byte( bson *b, char c ) {
+    b->cur[0] = c;
+    b->cur++;
+}
+
+void bson_append( bson *b, const void *data, int len ) {
+    memcpy( b->cur , data , len );
+    b->cur += len;
+}
+
+void bson_append32( bson *b, const void *data ) {
+    bson_little_endian32( b->cur, data );
+    b->cur += 4;
+}
+
+void bson_append64( bson *b, const void *data ) {
+    bson_little_endian64( b->cur, data );
+    b->cur += 8;
+}
+
+int bson_ensure_space( bson *b, const int bytesNeeded ) {
+    int pos = b->cur - b->data;
+    char *orig = b->data;
+    int new_size;
+
+    if ( pos + bytesNeeded <= b->dataSize )
+        return BSON_OK;
+
+    new_size = 1.5 * ( b->dataSize + bytesNeeded );
+
+    if( new_size < b->dataSize ) {
+        if( ( b->dataSize + bytesNeeded ) < INT_MAX )
+            new_size = INT_MAX;
+        else {
+            b->err = BSON_SIZE_OVERFLOW;
+            return BSON_ERROR;
+        }
+    }
+
+    b->data = bson_realloc( b->data, new_size );
+    if ( !b->data )
+        bson_fatal_msg( !!b->data, "realloc() failed" );
+
+    b->dataSize = new_size;
+    b->cur += b->data - orig;
+
+    return BSON_OK;
+}
+
+int bson_finish( bson *b ) {
+    int i;
+
+    if( b->err & BSON_NOT_UTF8 )
+        return BSON_ERROR;
+
+    if ( ! b->finished ) {
+        if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
+        bson_append_byte( b, 0 );
+        i = b->cur - b->data;
+        bson_little_endian32( b->data, &i );
+        b->finished = 1;
+    }
+
+    return BSON_OK;
+}
+
+void bson_destroy( bson *b ) {
+    bson_free( b->data );
+    b->err = 0;
+    b->data = 0;
+    b->cur = 0;
+    b->finished = 1;
+}
+
+static int bson_append_estart( bson *b, int type, const char *name, const int dataSize ) {
+    const int len = strlen( name ) + 1;
+
+    if ( b->finished ) {
+        b->err |= BSON_ALREADY_FINISHED;
+        return BSON_ERROR;
+    }
+
+    if ( bson_ensure_space( b, 1 + len + dataSize ) == BSON_ERROR ) {
+        return BSON_ERROR;
+    }
+
+    if( bson_check_field_name( b, ( const char * )name, len - 1 ) == BSON_ERROR ) {
+        bson_builder_error( b );
+        return BSON_ERROR;
+    }
+
+    bson_append_byte( b, ( char )type );
+    bson_append( b, name, len );
+    return BSON_OK;
+}
+
+/* ----------------------------
+   BUILDING TYPES
+   ------------------------------ */
+
+int bson_append_int( bson *b, const char *name, const int i ) {
+    if ( bson_append_estart( b, BSON_INT, name, 4 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append32( b , &i );
+    return BSON_OK;
+}
+
+int bson_append_long( bson *b, const char *name, const int64_t i ) {
+    if ( bson_append_estart( b , BSON_LONG, name, 8 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append64( b , &i );
+    return BSON_OK;
+}
+
+int bson_append_double( bson *b, const char *name, const double d ) {
+    if ( bson_append_estart( b, BSON_DOUBLE, name, 8 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append64( b , &d );
+    return BSON_OK;
+}
+
+int bson_append_bool( bson *b, const char *name, const bson_bool_t i ) {
+    if ( bson_append_estart( b, BSON_BOOL, name, 1 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append_byte( b , i != 0 );
+    return BSON_OK;
+}
+
+int bson_append_null( bson *b, const char *name ) {
+    if ( bson_append_estart( b , BSON_NULL, name, 0 ) == BSON_ERROR )
+        return BSON_ERROR;
+    return BSON_OK;
+}
+
+int bson_append_undefined( bson *b, const char *name ) {
+    if ( bson_append_estart( b, BSON_UNDEFINED, name, 0 ) == BSON_ERROR )
+        return BSON_ERROR;
+    return BSON_OK;
+}
+
+int bson_append_string_base( bson *b, const char *name,
+                             const char *value, int len, bson_type type ) {
+
+    int sl = len + 1;
+    if ( bson_check_string( b, ( const char * )value, sl - 1 ) == BSON_ERROR )
+        return BSON_ERROR;
+    if ( bson_append_estart( b, type, name, 4 + sl ) == BSON_ERROR ) {
+        return BSON_ERROR;
+    }
+    bson_append32( b , &sl );
+    bson_append( b , value , sl - 1 );
+    bson_append( b , "\0" , 1 );
+    return BSON_OK;
+}
+
+int bson_append_string( bson *b, const char *name, const char *value ) {
+    return bson_append_string_base( b, name, value, strlen ( value ), BSON_STRING );
+}
+
+int bson_append_symbol( bson *b, const char *name, const char *value ) {
+    return bson_append_string_base( b, name, value, strlen ( value ), BSON_SYMBOL );
+}
+
+int bson_append_code( bson *b, const char *name, const char *value ) {
+    return bson_append_string_base( b, name, value, strlen ( value ), BSON_CODE );
+}
+
+int bson_append_string_n( bson *b, const char *name, const char *value, int len ) {
+    return bson_append_string_base( b, name, value, len, BSON_STRING );
+}
+
+int bson_append_symbol_n( bson *b, const char *name, const char *value, int len ) {
+    return bson_append_string_base( b, name, value, len, BSON_SYMBOL );
+}
+
+int bson_append_code_n( bson *b, const char *name, const char *value, int len ) {
+    return bson_append_string_base( b, name, value, len, BSON_CODE );
+}
+
+int bson_append_code_w_scope_n( bson *b, const char *name,
+                                const char *code, int len, const bson *scope ) {
+
+    int sl = len + 1;
+    int size = 4 + 4 + sl + bson_size( scope );
+    if ( bson_append_estart( b, BSON_CODEWSCOPE, name, size ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append32( b, &size );
+    bson_append32( b, &sl );
+    bson_append( b, code, sl );
+    bson_append( b, scope->data, bson_size( scope ) );
+    return BSON_OK;
+}
+
+int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ) {
+    return bson_append_code_w_scope_n( b, name, code, strlen ( code ), scope );
+}
+
+int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ) {
+    if ( type == BSON_BIN_BINARY_OLD ) {
+        int subtwolen = len + 4;
+        if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+4+len ) == BSON_ERROR )
+            return BSON_ERROR;
+        bson_append32( b, &subtwolen );
+        bson_append_byte( b, type );
+        bson_append32( b, &len );
+        bson_append( b, str, len );
+    } else {
+        if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+len ) == BSON_ERROR )
+            return BSON_ERROR;
+        bson_append32( b, &len );
+        bson_append_byte( b, type );
+        bson_append( b, str, len );
+    }
+    return BSON_OK;
+}
+
+int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ) {
+    if ( bson_append_estart( b, BSON_OID, name, 12 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append( b , oid , 12 );
+    return BSON_OK;
+}
+
+int bson_append_new_oid( bson *b, const char *name ) {
+    bson_oid_t oid;
+    bson_oid_gen( &oid );
+    return bson_append_oid( b, name, &oid );
+}
+
+int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ) {
+    const int plen = strlen( pattern )+1;
+    const int olen = strlen( opts )+1;
+    if ( bson_append_estart( b, BSON_REGEX, name, plen + olen ) == BSON_ERROR )
+        return BSON_ERROR;
+    if ( bson_check_string( b, pattern, plen - 1 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append( b , pattern , plen );
+    bson_append( b , opts , olen );
+    return BSON_OK;
+}
+
+int bson_append_bson( bson *b, const char *name, const bson *bson ) {
+    if ( bson_append_estart( b, BSON_OBJECT, name, bson_size( bson ) ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append( b , bson->data , bson_size( bson ) );
+    return BSON_OK;
+}
+
+int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ) {
+    bson_iterator next = *elem;
+    int size;
+
+    bson_iterator_next( &next );
+    size = next.cur - elem->cur;
+
+    if ( name_or_null == NULL ) {
+        if( bson_ensure_space( b, size ) == BSON_ERROR )
+            return BSON_ERROR;
+        bson_append( b, elem->cur, size );
+    } else {
+        int data_size = size - 2 - strlen( bson_iterator_key( elem ) );
+        bson_append_estart( b, elem->cur[0], name_or_null, data_size );
+        bson_append( b, bson_iterator_value( elem ), data_size );
+    }
+
+    return BSON_OK;
+}
+
+int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ) {
+    if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
+
+    bson_append32( b , &( ts->i ) );
+    bson_append32( b , &( ts->t ) );
+
+    return BSON_OK;
+}
+
+int bson_append_date( bson *b, const char *name, bson_date_t millis ) {
+    if ( bson_append_estart( b, BSON_DATE, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
+    bson_append64( b , &millis );
+    return BSON_OK;
+}
+
+int bson_append_time_t( bson *b, const char *name, time_t secs ) {
+    return bson_append_date( b, name, ( bson_date_t )secs * 1000 );
+}
+
+int bson_append_start_object( bson *b, const char *name ) {
+    if ( bson_append_estart( b, BSON_OBJECT, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
+    b->stack[ b->stackPos++ ] = b->cur - b->data;
+    bson_append32( b , &zero );
+    return BSON_OK;
+}
+
+int bson_append_start_array( bson *b, const char *name ) {
+    if ( bson_append_estart( b, BSON_ARRAY, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
+    b->stack[ b->stackPos++ ] = b->cur - b->data;
+    bson_append32( b , &zero );
+    return BSON_OK;
+}
+
+int bson_append_finish_object( bson *b ) {
+    char *start;
+    int i;
+    if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
+    bson_append_byte( b , 0 );
+
+    start = b->data + b->stack[ --b->stackPos ];
+    i = b->cur - start;
+    bson_little_endian32( start, &i );
+
+    return BSON_OK;
+}
+
+int bson_append_finish_array( bson *b ) {
+    return bson_append_finish_object( b );
+}
+
+
+/* Error handling and allocators. */
+
+static bson_err_handler err_handler = NULL;
+
+bson_err_handler set_bson_err_handler( bson_err_handler func ) {
+    bson_err_handler old = err_handler;
+    err_handler = func;
+    return old;
+}
+
+void *bson_malloc( int size ) {
+    void *p;
+    p = bson_malloc_func( size );
+    bson_fatal_msg( !!p, "malloc() failed" );
+    return p;
+}
+
+void *bson_realloc( void *ptr, int size ) {
+    void *p;
+    p = bson_realloc_func( ptr, size );
+    bson_fatal_msg( !!p, "realloc() failed" );
+    return p;
+}
+
+int _bson_errprintf( const char *format, ... ) {
+    va_list ap;
+    int ret;
+    va_start( ap, format );
+    ret = vfprintf( stderr, format, ap );
+    va_end( ap );
+
+    return ret;
+}
+
+/**
+ * This method is invoked when a non-fatal bson error is encountered.
+ * Calls the error handler if available.
+ *
+ *  @param
+ */
+void bson_builder_error( bson *b ) {
+    if( err_handler )
+        err_handler( "BSON error." );
+}
+
+void bson_fatal( int ok ) {
+    bson_fatal_msg( ok, "" );
+}
+
+void bson_fatal_msg( int ok , const char *msg ) {
+    if ( ok )
+        return;
+
+    if ( err_handler ) {
+        err_handler( msg );
+    }
+
+    bson_errprintf( "error: %s\n" , msg );
+    exit( -5 );
+}
+
+
+/* Efficiently copy an integer to a string. */
+extern const char bson_numstrs[1000][4];
+
+void bson_numstr( char *str, int i ) {
+    if( i < 1000 )
+        memcpy( str, bson_numstrs[i], 4 );
+    else
+        bson_sprintf( str,"%d", i );
+}
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h
new file mode 100644 (file)
index 0000000..2e385b9
--- /dev/null
@@ -0,0 +1,975 @@
+/**
+ * @file bson.h
+ * @brief BSON Declarations
+ */
+
+/*    Copyright 2009-2011 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef _BSON_H_
+#define _BSON_H_
+
+#include "platform.h"
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+MONGO_EXTERN_C_START
+
+#define BSON_OK 0
+#define BSON_ERROR -1
+
+enum bson_error_t {
+    BSON_SIZE_OVERFLOW = 1 /**< Trying to create a BSON object larger than INT_MAX. */
+};
+
+enum bson_validity_t {
+    BSON_VALID = 0,                 /**< BSON is valid and UTF-8 compliant. */
+    BSON_NOT_UTF8 = ( 1<<1 ),       /**< A key or a string is not valid UTF-8. */
+    BSON_FIELD_HAS_DOT = ( 1<<2 ),  /**< Warning: key contains '.' character. */
+    BSON_FIELD_INIT_DOLLAR = ( 1<<3 ), /**< Warning: key starts with '$' character. */
+    BSON_ALREADY_FINISHED = ( 1<<4 )  /**< Trying to modify a finished BSON object. */
+};
+
+enum bson_binary_subtype_t {
+    BSON_BIN_BINARY = 0,
+    BSON_BIN_FUNC = 1,
+    BSON_BIN_BINARY_OLD = 2,
+    BSON_BIN_UUID = 3,
+    BSON_BIN_MD5 = 5,
+    BSON_BIN_USER = 128
+};
+
+typedef enum {
+    BSON_EOO = 0,
+    BSON_DOUBLE = 1,
+    BSON_STRING = 2,
+    BSON_OBJECT = 3,
+    BSON_ARRAY = 4,
+    BSON_BINDATA = 5,
+    BSON_UNDEFINED = 6,
+    BSON_OID = 7,
+    BSON_BOOL = 8,
+    BSON_DATE = 9,
+    BSON_NULL = 10,
+    BSON_REGEX = 11,
+    BSON_DBREF = 12, /**< Deprecated. */
+    BSON_CODE = 13,
+    BSON_SYMBOL = 14,
+    BSON_CODEWSCOPE = 15,
+    BSON_INT = 16,
+    BSON_TIMESTAMP = 17,
+    BSON_LONG = 18
+} bson_type;
+
+typedef int bson_bool_t;
+
+typedef struct {
+    const char *cur;
+    bson_bool_t first;
+} bson_iterator;
+
+typedef struct {
+    char *data;
+    char *cur;
+    int dataSize;
+    bson_bool_t finished;
+    int stack[32];
+    int stackPos;
+    int err; /**< Bitfield representing errors or warnings on this buffer */
+    char *errstr; /**< A string representation of the most recent error or warning. */
+} bson;
+
+#pragma pack(1)
+typedef union {
+    char bytes[12];
+    int ints[3];
+} bson_oid_t;
+#pragma pack()
+
+typedef int64_t bson_date_t; /* milliseconds since epoch UTC */
+
+typedef struct {
+    int i; /* increment */
+    int t; /* time in seconds */
+} bson_timestamp_t;
+
+/* ----------------------------
+   READING
+   ------------------------------ */
+
+/**
+ * Size of a BSON object.
+ *
+ * @param b the BSON object.
+ *
+ * @return the size.
+ */
+int bson_size( const bson *b );
+
+/**
+ * Print a string representation of a BSON object.
+ *
+ * @param b the BSON object to print.
+ */
+void bson_print( bson *b );
+
+/**
+ * Return a pointer to the raw buffer stored by this bson object.
+ *
+ * @param b a BSON object
+ */
+const char *bson_data( bson *b );
+
+/**
+ * Print a string representation of a BSON object.
+ *
+ * @param bson the raw data to print.
+ * @param depth the depth to recurse the object.x
+ */
+void bson_print_raw( const char *bson , int depth );
+
+/**
+ * Advance a bson_iterator to the named field.
+ *
+ * @param it the bson_iterator to use.
+ * @param obj the BSON object to use.
+ * @param name the name of the field to find.
+ *
+ * @return the type of the found object or BSON_EOO if it is not found.
+ */
+bson_type bson_find( bson_iterator *it, const bson *obj, const char *name );
+
+/**
+ * Initialize a bson_iterator.
+ *
+ * @param i the bson_iterator to initialize.
+ * @param bson the BSON object to associate with the iterator.
+ */
+void bson_iterator_init( bson_iterator *i , const bson *b );
+
+/**
+ * Initialize a bson iterator from a const char* buffer. Note
+ * that this is mostly used internally.
+ *
+ * @param i the bson_iterator to initialize.
+ * @param buffer the buffer to point to.
+ */
+void bson_iterator_from_buffer( bson_iterator *i, const char *buffer );
+
+/* more returns true for eoo. best to loop with bson_iterator_next(&it) */
+/**
+ * Check to see if the bson_iterator has more data.
+ *
+ * @param i the iterator.
+ *
+ * @return  returns true if there is more data.
+ */
+bson_bool_t bson_iterator_more( const bson_iterator *i );
+
+/**
+ * Point the iterator at the next BSON object.
+ *
+ * @param i the bson_iterator.
+ *
+ * @return the type of the next BSON object.
+ */
+bson_type bson_iterator_next( bson_iterator *i );
+
+/**
+ * Get the type of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return  the type of the current BSON object.
+ */
+bson_type bson_iterator_type( const bson_iterator *i );
+
+/**
+ * Get the key of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the key of the current BSON object.
+ */
+const char *bson_iterator_key( const bson_iterator *i );
+
+/**
+ * Get the value of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return  the value of the current BSON object.
+ */
+const char *bson_iterator_value( const bson_iterator *i );
+
+/* these convert to the right type (return 0 if non-numeric) */
+/**
+ * Get the double value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return  the value of the current BSON object.
+ */
+double bson_iterator_double( const bson_iterator *i );
+
+/**
+ * Get the int value of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return  the value of the current BSON object.
+ */
+int bson_iterator_int( const bson_iterator *i );
+
+/**
+ * Get the long value of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+int64_t bson_iterator_long( const bson_iterator *i );
+
+/* return the bson timestamp as a whole or in parts */
+/**
+ * Get the timestamp value of the BSON object currently pointed to by
+ * the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i );
+
+/**
+ * Get the boolean value of the BSON object currently pointed to by
+ * the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+/* false: boolean false, 0 in any type, or null */
+/* true: anything else (even empty strings and objects) */
+bson_bool_t bson_iterator_bool( const bson_iterator *i );
+
+/**
+ * Get the double value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+/* these assume you are using the right type */
+double bson_iterator_double_raw( const bson_iterator *i );
+
+/**
+ * Get the int value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+int bson_iterator_int_raw( const bson_iterator *i );
+
+/**
+ * Get the long value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+int64_t bson_iterator_long_raw( const bson_iterator *i );
+
+/**
+ * Get the bson_bool_t value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+bson_bool_t bson_iterator_bool_raw( const bson_iterator *i );
+
+/**
+ * Get the bson_oid_t value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+bson_oid_t *bson_iterator_oid( const bson_iterator *i );
+
+/**
+ * Get the string value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return  the value of the current BSON object.
+ */
+/* these can also be used with bson_code and bson_symbol*/
+const char *bson_iterator_string( const bson_iterator *i );
+
+/**
+ * Get the string length of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the length of the current BSON object.
+ */
+int bson_iterator_string_len( const bson_iterator *i );
+
+/**
+ * Get the code value of the BSON object currently pointed to by the
+ * iterator. Works with bson_code, bson_codewscope, and BSON_STRING
+ * returns NULL for everything else.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the code value of the current BSON object.
+ */
+/* works with bson_code, bson_codewscope, and BSON_STRING */
+/* returns NULL for everything else */
+const char *bson_iterator_code( const bson_iterator *i );
+
+/**
+ * Calls bson_empty on scope if not a bson_codewscope
+ *
+ * @param i the bson_iterator.
+ * @param scope the bson scope.
+ */
+/* calls bson_empty on scope if not a bson_codewscope */
+void bson_iterator_code_scope( const bson_iterator *i, bson *scope );
+
+/**
+ * Get the date value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the date value of the current BSON object.
+ */
+/* both of these only work with bson_date */
+bson_date_t bson_iterator_date( const bson_iterator *i );
+
+/**
+ * Get the time value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the time value of the current BSON object.
+ */
+time_t bson_iterator_time_t( const bson_iterator *i );
+
+/**
+ * Get the length of the BSON binary object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the length of the current BSON binary object.
+ */
+int bson_iterator_bin_len( const bson_iterator *i );
+
+/**
+ * Get the type of the BSON binary object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the type of the current BSON binary object.
+ */
+char bson_iterator_bin_type( const bson_iterator *i );
+
+/**
+ * Get the value of the BSON binary object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON binary object.
+ */
+const char *bson_iterator_bin_data( const bson_iterator *i );
+
+/**
+ * Get the value of the BSON regex object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON regex object.
+ */
+const char *bson_iterator_regex( const bson_iterator *i );
+
+/**
+ * Get the options of the BSON regex object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator.
+ *
+ * @return the options of the current BSON regex object.
+ */
+const char *bson_iterator_regex_opts( const bson_iterator *i );
+
+/* these work with BSON_OBJECT and BSON_ARRAY */
+/**
+ * Get the BSON subobject currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator.
+ * @param sub the BSON subobject destination.
+ */
+void bson_iterator_subobject( const bson_iterator *i, bson *sub );
+
+/**
+ * Get a bson_iterator that on the BSON subobject.
+ *
+ * @param i the bson_iterator.
+ * @param sub the iterator to point at the BSON subobject.
+ */
+void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub );
+
+/* str must be at least 24 hex chars + null byte */
+/**
+ * Create a bson_oid_t from a string.
+ *
+ * @param oid the bson_oid_t destination.
+ * @param str a null terminated string comprised of at least 24 hex chars.
+ */
+void bson_oid_from_string( bson_oid_t *oid, const char *str );
+
+/**
+ * Create a string representation of the bson_oid_t.
+ *
+ * @param oid the bson_oid_t source.
+ * @param str the string representation destination.
+ */
+void bson_oid_to_string( const bson_oid_t *oid, char *str );
+
+/**
+ * Create a bson_oid object.
+ *
+ * @param oid the destination for the newly created bson_oid_t.
+ */
+void bson_oid_gen( bson_oid_t *oid );
+
+/**
+ * Set a function to be used to generate the second four bytes
+ * of an object id.
+ *
+ * @param func a pointer to a function that returns an int.
+ */
+void bson_set_oid_fuzz( int ( *func )( void ) );
+
+/**
+ * Set a function to be used to generate the incrementing part
+ * of an object id (last four bytes). If you need thread-safety
+ * in generating object ids, you should set this function.
+ *
+ * @param func a pointer to a function that returns an int.
+ */
+void bson_set_oid_inc( int ( *func )( void ) );
+
+/**
+ * Get the time a bson_oid_t was created.
+ *
+ * @param oid the bson_oid_t.
+ */
+time_t bson_oid_generated_time( bson_oid_t *oid ); /* Gives the time the OID was created */
+
+/* ----------------------------
+   BUILDING
+   ------------------------------ */
+
+/**
+ *  Initialize a new bson object. If not created
+ *  with bson_new, you must initialize each new bson
+ *  object using this function.
+ *
+ *  @note When finished, you must pass the bson object to
+ *      bson_destroy( ).
+ */
+void bson_init( bson *b );
+
+/**
+ * Initialize a BSON object, and point its data
+ * pointer to the provided char*.
+ *
+ * @param b the BSON object to initialize.
+ * @param data the raw BSON data.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_init_data( bson *b , char *data );
+
+/**
+ * Initialize a BSON object, and set its
+ * buffer to the given size.
+ *
+ * @param b the BSON object to initialize.
+ * @param size the initial size of the buffer.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+void bson_init_size( bson *b, int size );
+
+/**
+ * Grow a bson object.
+ *
+ * @param b the bson to grow.
+ * @param bytesNeeded the additional number of bytes needed.
+ *
+ * @return BSON_OK or BSON_ERROR with the bson error object set.
+ *   Exits if allocation fails.
+ */
+int bson_ensure_space( bson *b, const int bytesNeeded );
+
+/**
+ * Finalize a bson object.
+ *
+ * @param b the bson object to finalize.
+ *
+ * @return the standard error code. To deallocate memory,
+ *   call bson_destroy on the bson object.
+ */
+int bson_finish( bson *b );
+
+/**
+ * Destroy a bson object.
+ *
+ * @param b the bson object to destroy.
+ *
+ */
+void bson_destroy( bson *b );
+
+/**
+ * Returns a pointer to a static empty BSON object.
+ *
+ * @param obj the BSON object to initialize.
+ *
+ * @return the empty initialized BSON object.
+ */
+/* returns pointer to static empty bson object */
+bson *bson_empty( bson *obj );
+
+/**
+ * Copy BSON data only from one object to another.
+ *
+ * @param out the copy destination BSON object.
+ * @param in the copy source BSON object.
+ */
+void bson_copy_basic( bson *out, const bson *in );
+
+/**
+ * Make a complete copy of the a BSON object.
+ *
+ * @param out the copy destination BSON object.
+ * @param in the copy source BSON object.
+ */
+void bson_copy( bson *out, const bson *in ); /* puts data in new buffer. NOOP if out==NULL */
+
+/**
+ * Append a previously created bson_oid_t to a bson object.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the bson_oid_t.
+ * @param oid the bson_oid_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid );
+
+/**
+ * Append a bson_oid_t to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the bson_oid_t.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_new_oid( bson *b, const char *name );
+
+/**
+ * Append an int to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the int.
+ * @param i the int to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_int( bson *b, const char *name, const int i );
+
+/**
+ * Append an long to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the long.
+ * @param i the long to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_long( bson *b, const char *name, const int64_t i );
+
+/**
+ * Append an double to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the double.
+ * @param d the double to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_double( bson *b, const char *name, const double d );
+
+/**
+ * Append a string to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the string.
+ * @param str the string to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+*/
+int bson_append_string( bson *b, const char *name, const char *str );
+
+/**
+ * Append len bytes of a string to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the string.
+ * @param str the string to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_string_n( bson *b, const char *name, const char *str, int len );
+
+/**
+ * Append a symbol to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the symbol.
+ * @param str the symbol to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_symbol( bson *b, const char *name, const char *str );
+
+/**
+ * Append len bytes of a symbol to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the symbol.
+ * @param str the symbol to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_symbol_n( bson *b, const char *name, const char *str, int len );
+
+/**
+ * Append code to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the code to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_code( bson *b, const char *name, const char *str );
+
+/**
+ * Append len bytes of code to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the code to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_code_n( bson *b, const char *name, const char *str, int len );
+
+/**
+ * Append code to a bson with scope.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the string to append.
+ * @param scope a BSON object containing the scope.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope );
+
+/**
+ * Append len bytes of code to a bson with scope.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the string to append.
+ * @param len the number of bytes from str to append.
+ * @param scope a BSON object containing the scope.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_code_w_scope_n( bson *b, const char *name, const char *code, int size, const bson *scope );
+
+/**
+ * Append binary data to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the data.
+ * @param type the binary data type.
+ * @param str the binary data.
+ * @param len the length of the data.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_binary( bson *b, const char *name, char type, const char *str, int len );
+
+/**
+ * Append a bson_bool_t to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the boolean value.
+ * @param v the bson_bool_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_bool( bson *b, const char *name, const bson_bool_t v );
+
+/**
+ * Append a null value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the null value.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_null( bson *b, const char *name );
+
+/**
+ * Append an undefined value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the undefined value.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_undefined( bson *b, const char *name );
+
+/**
+ * Append a regex value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the regex value.
+ * @param pattern the regex pattern to append.
+ * @param the regex options.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts );
+
+/**
+ * Append bson data to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the bson data.
+ * @param bson the bson object to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_bson( bson *b, const char *name, const bson *bson );
+
+/**
+ * Append a BSON element to a bson from the current point of an iterator.
+ *
+ * @param b the bson to append to.
+ * @param name_or_null the key for the BSON element, or NULL.
+ * @param elem the bson_iterator.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem );
+
+/**
+ * Append a bson_timestamp_t value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the timestampe value.
+ * @param ts the bson_timestamp_t value to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts );
+
+/* these both append a bson_date */
+/**
+ * Append a bson_date_t value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the date value.
+ * @param millis the bson_date_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_date( bson *b, const char *name, bson_date_t millis );
+
+/**
+ * Append a time_t value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the date value.
+ * @param secs the time_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_time_t( bson *b, const char *name, time_t secs );
+
+/**
+ * Start appending a new object to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the name of the new object.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_start_object( bson *b, const char *name );
+
+/**
+ * Start appending a new array to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the name of the new array.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_start_array( bson *b, const char *name );
+
+/**
+ * Finish appending a new object or array to a bson.
+ *
+ * @param b the bson to append to.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_finish_object( bson *b );
+
+/**
+ * Finish appending a new object or array to a bson. This
+ * is simply an alias for bson_append_finish_object.
+ *
+ * @param b the bson to append to.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_finish_array( bson *b );
+
+void bson_numstr( char *str, int i );
+
+void bson_incnumstr( char *str );
+
+/* Error handling and stadard library function over-riding. */
+/* -------------------------------------------------------- */
+
+/* bson_err_handlers shouldn't return!!! */
+typedef void( *bson_err_handler )( const char *errmsg );
+
+typedef int (*bson_printf_func)( const char *, ... );
+typedef int (*bson_fprintf_func)( FILE *, const char *, ... );
+typedef int (*bson_sprintf_func)( char *, const char *, ... );
+
+extern void *( *bson_malloc_func )( size_t );
+extern void *( *bson_realloc_func )( void *, size_t );
+extern void ( *bson_free )( void * );
+
+extern bson_printf_func bson_printf;
+extern bson_fprintf_func bson_fprintf;
+extern bson_sprintf_func bson_sprintf;
+
+extern bson_printf_func bson_errprintf;
+
+/**
+ * Allocates memory and checks return value, exiting fatally if malloc() fails.
+ *
+ * @param size bytes to allocate.
+ *
+ * @return a pointer to the allocated memory.
+ *
+ * @sa malloc(3)
+ */
+void *bson_malloc( int size );
+
+/**
+ * Changes the size of allocated memory and checks return value,
+ * exiting fatally if realloc() fails.
+ *
+ * @param ptr pointer to the space to reallocate.
+ * @param size bytes to allocate.
+ *
+ * @return a pointer to the allocated memory.
+ *
+ * @sa realloc()
+ */
+void *bson_realloc( void *ptr, int size );
+
+/**
+ * Set a function for error handling.
+ *
+ * @param func a bson_err_handler function.
+ *
+ * @return the old error handling function, or NULL.
+ */
+bson_err_handler set_bson_err_handler( bson_err_handler func );
+
+/* does nothing if ok != 0 */
+/**
+ * Exit fatally.
+ *
+ * @param ok exits if ok is equal to 0.
+ */
+void bson_fatal( int ok );
+
+/**
+ * Exit fatally with an error message.
+  *
+ * @param ok exits if ok is equal to 0.
+ * @param msg prints to stderr before exiting.
+ */
+void bson_fatal_msg( int ok, const char *msg );
+
+/**
+ * Invoke the error handler, but do not exit.
+ *
+ * @param b the buffer object.
+ */
+void bson_builder_error( bson *b );
+
+MONGO_EXTERN_C_END
+#endif
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c
new file mode 100644 (file)
index 0000000..8d2da15
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2009-2011 10gen, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Portions Copyright 2001 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+
+#include "bson.h"
+#include "encoding.h"
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ */
+static const char trailingBytesForUTF8[256] = {
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * The length can be set by:
+ *  length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns 0.  The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+static int isLegalUTF8( const unsigned char *source, int length ) {
+    unsigned char a;
+    const unsigned char *srcptr = source + length;
+    switch ( length ) {
+    default:
+        return 0;
+        /* Everything else falls through when "true"... */
+    case 4:
+        if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
+    case 3:
+        if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
+    case 2:
+        if ( ( a = ( *--srcptr ) ) > 0xBF ) return 0;
+        switch ( *source ) {
+            /* no fall-through in this inner switch */
+        case 0xE0:
+            if ( a < 0xA0 ) return 0;
+            break;
+        case 0xF0:
+            if ( a < 0x90 ) return 0;
+            break;
+        case 0xF4:
+            if ( a > 0x8F ) return 0;
+            break;
+        default:
+            if ( a < 0x80 ) return 0;
+        }
+    case 1:
+        if ( *source >= 0x80 && *source < 0xC2 ) return 0;
+        if ( *source > 0xF4 ) return 0;
+    }
+    return 1;
+}
+
+static int bson_validate_string( bson *b, const unsigned char *string,
+                                 const int length, const char check_utf8, const char check_dot,
+                                 const char check_dollar ) {
+
+    int position = 0;
+    int sequence_length = 1;
+
+    if( check_dollar && string[0] == '$' ) {
+        b->err |= BSON_FIELD_INIT_DOLLAR;
+    }
+
+    while ( position < length ) {
+        if ( check_dot && *( string + position ) == '.' ) {
+            b->err |= BSON_FIELD_HAS_DOT;
+        }
+
+        if ( check_utf8 ) {
+            sequence_length = trailingBytesForUTF8[*( string + position )] + 1;
+            if ( ( position + sequence_length ) > length ) {
+                b->err |= BSON_NOT_UTF8;
+                return BSON_ERROR;
+            }
+            if ( !isLegalUTF8( string + position, sequence_length ) ) {
+                b->err |= BSON_NOT_UTF8;
+                return BSON_ERROR;
+            }
+        }
+        position += sequence_length;
+    }
+
+    return BSON_OK;
+}
+
+
+int bson_check_string( bson *b, const char *string,
+                       const int length ) {
+
+    return bson_validate_string( b, ( const unsigned char * )string, length, 1, 0, 0 );
+}
+
+int bson_check_field_name( bson *b, const char *string,
+                           const int length ) {
+
+    return bson_validate_string( b, ( const unsigned char * )string, length, 1, 1, 1 );
+}
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.h
new file mode 100644 (file)
index 0000000..a2a07c6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2009-2011 10gen, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BSON_ENCODING_H_
+#define _BSON_ENCODING_H_
+
+MONGO_EXTERN_C_START
+
+/**
+ * Check that a field name is valid UTF8, does not start with a '$',
+ * and contains no '.' characters. Set bson bit field appropriately.
+ * Note that we don't need to check for '\0' because we're using
+ * strlen(3), which stops at '\0'.
+ *
+ * @param b The bson object to which field name will be appended.
+ * @param string The field name as char*.
+ * @param length The length of the field name.
+ *
+ * @return BSON_OK if valid UTF8 and BSON_ERROR if not. All BSON strings must be
+ *     valid UTF8. This function will also check whether the string
+ *     contains '.' or starts with '$', since the validity of this depends on context.
+ *     Set the value of b->err appropriately.
+ */
+int bson_check_field_name( bson *b, const char *string,
+                           const int length );
+
+/**
+ * Check that a string is valid UTF8. Sets the buffer bit field appropriately.
+ *
+ * @param b The bson object to which string will be appended.
+ * @param string The string to check.
+ * @param length The length of the string.
+ *
+ * @return BSON_OK if valid UTF-8; otherwise, BSON_ERROR.
+ *     Sets b->err on error.
+ */
+bson_bool_t bson_check_string( bson *b, const char *string,
+                               const int length );
+
+MONGO_EXTERN_C_END
+#endif
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c
new file mode 100644 (file)
index 0000000..f51b397
--- /dev/null
@@ -0,0 +1,685 @@
+/* gridfs.c */
+
+/*    Copyright 2009-2011 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "gridfs.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+static bson *chunk_new( bson_oid_t id, int chunkNumber,
+                        const char *data, int len ) {
+    bson *b = bson_malloc( sizeof( bson ) );
+
+    bson_init( b );
+    bson_append_oid( b, "files_id", &id );
+    bson_append_int( b, "n", chunkNumber );
+    bson_append_binary( b, "data", BSON_BIN_BINARY, data, len );
+    bson_finish( b );
+    return  b;
+}
+
+static void chunk_free( bson *oChunk ) {
+    bson_destroy( oChunk );
+    bson_free( oChunk );
+}
+
+int gridfs_init( mongo *client, const char *dbname, const char *prefix,
+    gridfs *gfs ) {
+
+    int options;
+    bson b;
+    bson_bool_t success;
+
+    gfs->client = client;
+
+    /* Allocate space to own the dbname */
+    gfs->dbname = ( const char * )bson_malloc( strlen( dbname )+1 );
+    strcpy( ( char * )gfs->dbname, dbname );
+
+    /* Allocate space to own the prefix */
+    if ( prefix == NULL ) prefix = "fs";
+    gfs->prefix = ( const char * )bson_malloc( strlen( prefix )+1 );
+    strcpy( ( char * )gfs->prefix, prefix );
+
+    /* Allocate space to own files_ns */
+    gfs->files_ns =
+        ( const char * ) bson_malloc ( strlen( prefix )+strlen( dbname )+strlen( ".files" )+2 );
+    strcpy( ( char * )gfs->files_ns, dbname );
+    strcat( ( char * )gfs->files_ns, "." );
+    strcat( ( char * )gfs->files_ns, prefix );
+    strcat( ( char * )gfs->files_ns, ".files" );
+
+    /* Allocate space to own chunks_ns */
+    gfs->chunks_ns = ( const char * ) bson_malloc( strlen( prefix ) + strlen( dbname )
+                     + strlen( ".chunks" ) + 2 );
+    strcpy( ( char * )gfs->chunks_ns, dbname );
+    strcat( ( char * )gfs->chunks_ns, "." );
+    strcat( ( char * )gfs->chunks_ns, prefix );
+    strcat( ( char * )gfs->chunks_ns, ".chunks" );
+
+    bson_init( &b );
+    bson_append_int( &b, "filename", 1 );
+    bson_finish( &b );
+    options = 0;
+    success = ( mongo_create_index( gfs->client, gfs->files_ns, &b, options, NULL ) == MONGO_OK );
+    bson_destroy( &b );
+    if ( !success ) {
+        bson_free( ( char * )gfs->dbname );
+        bson_free( ( char * )gfs->prefix );
+        bson_free( ( char * )gfs->files_ns );
+        bson_free( ( char * )gfs->chunks_ns );
+        return MONGO_ERROR;
+    }
+
+    bson_init( &b );
+    bson_append_int( &b, "files_id", 1 );
+    bson_append_int( &b, "n", 1 );
+    bson_finish( &b );
+    options = MONGO_INDEX_UNIQUE;
+    success = ( mongo_create_index( gfs->client, gfs->chunks_ns, &b, options, NULL ) == MONGO_OK );
+    bson_destroy( &b );
+    if ( !success ) {
+        bson_free( ( char * )gfs->dbname );
+        bson_free( ( char * )gfs->prefix );
+        bson_free( ( char * )gfs->files_ns );
+        bson_free( ( char * )gfs->chunks_ns );
+        return MONGO_ERROR;
+    }
+
+    return MONGO_OK;
+}
+
+void gridfs_destroy( gridfs *gfs ) {
+    if ( gfs == NULL ) return;
+    if ( gfs->dbname ) bson_free( ( char * )gfs->dbname );
+    if ( gfs->prefix ) bson_free( ( char * )gfs->prefix );
+    if ( gfs->files_ns ) bson_free( ( char * )gfs->files_ns );
+    if ( gfs->chunks_ns ) bson_free( ( char * )gfs->chunks_ns );
+}
+
+static int gridfs_insert_file( gridfs *gfs, const char *name,
+                                const bson_oid_t id, gridfs_offset length,
+                                const char *contenttype ) {
+    bson command;
+    bson ret;
+    bson res;
+    bson_iterator it;
+    int result;
+
+    /* Check run md5 */
+    bson_init( &command );
+    bson_append_oid( &command, "filemd5", &id );
+    bson_append_string( &command, "root", gfs->prefix );
+    bson_finish( &command );
+    assert( mongo_run_command( gfs->client, gfs->dbname, &command, &res ) == MONGO_OK );
+    bson_destroy( &command );
+
+    /* Create and insert BSON for file metadata */
+    bson_init( &ret );
+    bson_append_oid( &ret, "_id", &id );
+    if ( name != NULL && *name != '\0' ) {
+        bson_append_string( &ret, "filename", name );
+    }
+    bson_append_long( &ret, "length", length );
+    bson_append_int( &ret, "chunkSize", DEFAULT_CHUNK_SIZE );
+    bson_append_date( &ret, "uploadDate", ( bson_date_t )1000*time( NULL ) );
+    bson_find( &it, &res, "md5" );
+    bson_append_string( &ret, "md5", bson_iterator_string( &it ) );
+    bson_destroy( &res );
+    if ( contenttype != NULL && *contenttype != '\0' ) {
+        bson_append_string( &ret, "contentType", contenttype );
+    }
+    bson_finish( &ret );
+    result = mongo_insert( gfs->client, gfs->files_ns, &ret );
+    bson_destroy( &ret );
+
+    return result;
+}
+
+int gridfs_store_buffer( gridfs *gfs, const char *data,
+                          gridfs_offset length, const char *remotename,
+                          const char *contenttype ) {
+
+    char const *end = data + length;
+    const char *data_ptr = data;
+    bson_oid_t id;
+    int chunkNumber = 0;
+    int chunkLen;
+    bson *oChunk;
+
+    /* Large files Assertion */
+    assert( length <= 0xffffffff );
+
+    /* Generate and append an oid*/
+    bson_oid_gen( &id );
+
+    /* Insert the file's data chunk by chunk */
+    while ( data_ptr < end ) {
+        chunkLen = DEFAULT_CHUNK_SIZE < ( unsigned int )( end - data_ptr ) ?
+                   DEFAULT_CHUNK_SIZE : ( unsigned int )( end - data_ptr );
+        oChunk = chunk_new( id, chunkNumber, data_ptr, chunkLen );
+        mongo_insert( gfs->client, gfs->chunks_ns, oChunk );
+        chunk_free( oChunk );
+        chunkNumber++;
+        data_ptr += chunkLen;
+    }
+
+    /* Inserts file's metadata */
+    return gridfs_insert_file( gfs, remotename, id, length, contenttype );
+}
+
+void gridfile_writer_init( gridfile *gfile, gridfs *gfs,
+                           const char *remote_name, const char *content_type ) {
+    gfile->gfs = gfs;
+
+    bson_oid_gen( &( gfile->id ) );
+    gfile->chunk_num = 0;
+    gfile->length = 0;
+    gfile->pending_len = 0;
+    gfile->pending_data = NULL;
+
+    gfile->remote_name = ( char * )bson_malloc( strlen( remote_name ) + 1 );
+    strcpy( ( char * )gfile->remote_name, remote_name );
+
+    gfile->content_type = ( char * )bson_malloc( strlen( content_type ) + 1 );
+    strcpy( ( char * )gfile->content_type, content_type );
+}
+
+void gridfile_write_buffer( gridfile *gfile, const char *data,
+    gridfs_offset length ) {
+
+    int bytes_left = 0;
+    int data_partial_len = 0;
+    int chunks_to_write = 0;
+    char *buffer;
+    bson *oChunk;
+    gridfs_offset to_write = length + gfile->pending_len;
+
+    if ( to_write < DEFAULT_CHUNK_SIZE ) { /* Less than one chunk to write */
+        if( gfile->pending_data ) {
+            gfile->pending_data = ( char * )bson_realloc( ( void * )gfile->pending_data, gfile->pending_len + to_write );
+            memcpy( gfile->pending_data + gfile->pending_len, data, length );
+        } else if ( to_write > 0 ) {
+            gfile->pending_data = ( char * )bson_malloc( to_write );
+            memcpy( gfile->pending_data, data, length );
+        }
+        gfile->pending_len += length;
+
+    } else { /* At least one chunk of data to write */
+
+        /* If there's a pending chunk to be written, we need to combine
+         * the buffer provided up to DEFAULT_CHUNK_SIZE.
+         */
+        if ( gfile->pending_len > 0 ) {
+            chunks_to_write = to_write / DEFAULT_CHUNK_SIZE;
+            bytes_left = to_write % DEFAULT_CHUNK_SIZE;
+
+            data_partial_len = DEFAULT_CHUNK_SIZE - gfile->pending_len;
+            buffer = ( char * )bson_malloc( DEFAULT_CHUNK_SIZE );
+            memcpy( buffer, gfile->pending_data, gfile->pending_len );
+            memcpy( buffer + gfile->pending_len, data, data_partial_len );
+
+            oChunk = chunk_new( gfile->id, gfile->chunk_num, buffer, DEFAULT_CHUNK_SIZE );
+            mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
+            chunk_free( oChunk );
+            gfile->chunk_num++;
+            gfile->length += DEFAULT_CHUNK_SIZE;
+            data += data_partial_len;
+
+            chunks_to_write--;
+
+            bson_free( buffer );
+        }
+
+        while( chunks_to_write > 0 ) {
+            oChunk = chunk_new( gfile->id, gfile->chunk_num, data, DEFAULT_CHUNK_SIZE );
+            mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
+            chunk_free( oChunk );
+            gfile->chunk_num++;
+            chunks_to_write--;
+            gfile->length += DEFAULT_CHUNK_SIZE;
+            data += DEFAULT_CHUNK_SIZE;
+        }
+
+        bson_free( gfile->pending_data );
+
+        /* If there are any leftover bytes, store them as pending data. */
+        if( bytes_left == 0 )
+            gfile->pending_data = NULL;
+        else {
+            gfile->pending_data = ( char * )bson_malloc( bytes_left );
+            memcpy( gfile->pending_data, data, bytes_left );
+        }
+
+        gfile->pending_len = bytes_left;
+    }
+}
+
+int gridfile_writer_done( gridfile *gfile ) {
+
+    /* write any remaining pending chunk data.
+     * pending data will always take up less than one chunk */
+    bson *oChunk;
+    int response;
+    if( gfile->pending_data ) {
+        oChunk = chunk_new( gfile->id, gfile->chunk_num, gfile->pending_data, gfile->pending_len );
+        mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
+        chunk_free( oChunk );
+        bson_free( gfile->pending_data );
+        gfile->length += gfile->pending_len;
+    }
+
+    /* insert into files collection */
+    response = gridfs_insert_file( gfile->gfs, gfile->remote_name, gfile->id,
+                                   gfile->length, gfile->content_type );
+
+    bson_free( gfile->remote_name );
+    bson_free( gfile->content_type );
+
+    return response;
+}
+
+int gridfs_store_file( gridfs *gfs, const char *filename,
+                        const char *remotename, const char *contenttype ) {
+
+    char buffer[DEFAULT_CHUNK_SIZE];
+    FILE *fd;
+    bson_oid_t id;
+    int chunkNumber = 0;
+    gridfs_offset length = 0;
+    gridfs_offset chunkLen = 0;
+    bson *oChunk;
+
+    /* Open the file and the correct stream */
+    if ( strcmp( filename, "-" ) == 0 ) fd = stdin;
+    else fd = fopen( filename, "rb" );
+    assert( fd != NULL ); /* No such file */
+
+    /* Generate and append an oid*/
+    bson_oid_gen( &id );
+
+    /* Insert the file chunk by chunk */
+    chunkLen = fread( buffer, 1, DEFAULT_CHUNK_SIZE, fd );
+    do {
+        oChunk = chunk_new( id, chunkNumber, buffer, chunkLen );
+        mongo_insert( gfs->client, gfs->chunks_ns, oChunk );
+        chunk_free( oChunk );
+        length += chunkLen;
+        chunkNumber++;
+        chunkLen = fread( buffer, 1, DEFAULT_CHUNK_SIZE, fd );
+    } while ( chunkLen != 0 );
+
+    /* Close the file stream */
+    if ( fd != stdin ) fclose( fd );
+
+    /* Large files Assertion */
+    /* assert(length <= 0xffffffff); */
+
+    /* Optional Remote Name */
+    if ( remotename == NULL || *remotename == '\0' ) {
+        remotename = filename;
+    }
+
+    /* Inserts file's metadata */
+    return gridfs_insert_file( gfs, remotename, id, length, contenttype );
+}
+
+void gridfs_remove_filename( gridfs *gfs, const char *filename ) {
+    bson query;
+    mongo_cursor *files;
+    bson file;
+    bson_iterator it;
+    bson_oid_t id;
+    bson b;
+
+    bson_init( &query );
+    bson_append_string( &query, "filename", filename );
+    bson_finish( &query );
+    files = mongo_find( gfs->client, gfs->files_ns, &query, NULL, 0, 0, 0 );
+    bson_destroy( &query );
+
+    /* Remove each file and it's chunks from files named filename */
+    while ( mongo_cursor_next( files ) == MONGO_OK ) {
+        file = files->current;
+        bson_find( &it, &file, "_id" );
+        id = *bson_iterator_oid( &it );
+
+        /* Remove the file with the specified id */
+        bson_init( &b );
+        bson_append_oid( &b, "_id", &id );
+        bson_finish( &b );
+        mongo_remove( gfs->client, gfs->files_ns, &b );
+        bson_destroy( &b );
+
+        /* Remove all chunks from the file with the specified id */
+        bson_init( &b );
+        bson_append_oid( &b, "files_id", &id );
+        bson_finish( &b );
+        mongo_remove( gfs->client, gfs->chunks_ns, &b );
+        bson_destroy( &b );
+    }
+
+    mongo_cursor_destroy( files );
+}
+
+int gridfs_find_query( gridfs *gfs, bson *query,
+                       gridfile *gfile ) {
+
+    bson uploadDate;
+    bson finalQuery;
+    bson out;
+    int i;
+
+    bson_init( &uploadDate );
+    bson_append_int( &uploadDate, "uploadDate", -1 );
+    bson_finish( &uploadDate );
+
+    bson_init( &finalQuery );
+    bson_append_bson( &finalQuery, "query", query );
+    bson_append_bson( &finalQuery, "orderby", &uploadDate );
+    bson_finish( &finalQuery );
+
+    i = ( mongo_find_one( gfs->client, gfs->files_ns,
+                          &finalQuery, NULL, &out ) == MONGO_OK );
+    bson_destroy( &uploadDate );
+    bson_destroy( &finalQuery );
+    if ( !i )
+        return MONGO_ERROR;
+    else {
+        gridfile_init( gfs, &out, gfile );
+        bson_destroy( &out );
+        return MONGO_OK;
+    }
+}
+
+int gridfs_find_filename( gridfs *gfs, const char *filename,
+                          gridfile *gfile )
+
+{
+    bson query;
+    int i;
+
+    bson_init( &query );
+    bson_append_string( &query, "filename", filename );
+    bson_finish( &query );
+    i = gridfs_find_query( gfs, &query, gfile );
+    bson_destroy( &query );
+    return i;
+}
+
+int gridfile_init( gridfs *gfs, bson *meta, gridfile *gfile )
+
+{
+    gfile->gfs = gfs;
+    gfile->pos = 0;
+    gfile->meta = ( bson * )bson_malloc( sizeof( bson ) );
+    if ( gfile->meta == NULL ) return MONGO_ERROR;
+    bson_copy( gfile->meta, meta );
+    return MONGO_OK;
+}
+
+void gridfile_destroy( gridfile *gfile )
+
+{
+    bson_destroy( gfile->meta );
+    bson_free( gfile->meta );
+}
+
+bson_bool_t gridfile_exists( gridfile *gfile ) {
+    return ( bson_bool_t )( gfile != NULL || gfile->meta == NULL );
+}
+
+const char *gridfile_get_filename( gridfile *gfile ) {
+    bson_iterator it;
+
+    bson_find( &it, gfile->meta, "filename" );
+    return bson_iterator_string( &it );
+}
+
+int gridfile_get_chunksize( gridfile *gfile ) {
+    bson_iterator it;
+
+    bson_find( &it, gfile->meta, "chunkSize" );
+    return bson_iterator_int( &it );
+}
+
+gridfs_offset gridfile_get_contentlength( gridfile *gfile ) {
+    bson_iterator it;
+
+    bson_find( &it, gfile->meta, "length" );
+
+    if( bson_iterator_type( &it ) == BSON_INT )
+        return ( gridfs_offset )bson_iterator_int( &it );
+    else
+        return ( gridfs_offset )bson_iterator_long( &it );
+}
+
+const char *gridfile_get_contenttype( gridfile *gfile ) {
+    bson_iterator it;
+
+    if ( bson_find( &it, gfile->meta, "contentType" ) )
+        return bson_iterator_string( &it );
+    else return NULL;
+}
+
+bson_date_t gridfile_get_uploaddate( gridfile *gfile ) {
+    bson_iterator it;
+
+    bson_find( &it, gfile->meta, "uploadDate" );
+    return bson_iterator_date( &it );
+}
+
+const char *gridfile_get_md5( gridfile *gfile ) {
+    bson_iterator it;
+
+    bson_find( &it, gfile->meta, "md5" );
+    return bson_iterator_string( &it );
+}
+
+const char *gridfile_get_field( gridfile *gfile, const char *name ) {
+    bson_iterator it;
+
+    bson_find( &it, gfile->meta, name );
+    return bson_iterator_value( &it );
+}
+
+bson_bool_t gridfile_get_boolean( gridfile *gfile, const char *name ) {
+    bson_iterator it;
+
+    bson_find( &it, gfile->meta, name );
+    return bson_iterator_bool( &it );
+}
+
+bson gridfile_get_metadata( gridfile *gfile ) {
+    bson sub;
+    bson_iterator it;
+
+    if ( bson_find( &it, gfile->meta, "metadata" ) ) {
+        bson_iterator_subobject( &it, &sub );
+        return sub;
+    } else {
+        bson_empty( &sub );
+        return sub;
+    }
+}
+
+int gridfile_get_numchunks( gridfile *gfile ) {
+    bson_iterator it;
+    gridfs_offset length;
+    gridfs_offset chunkSize;
+    double numchunks;
+
+    bson_find( &it, gfile->meta, "length" );
+
+    if( bson_iterator_type( &it ) == BSON_INT )
+        length = ( gridfs_offset )bson_iterator_int( &it );
+    else
+        length = ( gridfs_offset )bson_iterator_long( &it );
+
+    bson_find( &it, gfile->meta, "chunkSize" );
+    chunkSize = bson_iterator_int( &it );
+    numchunks = ( ( double )length/( double )chunkSize );
+    return ( numchunks - ( int )numchunks > 0 )
+           ? ( int )( numchunks+1 )
+           : ( int )( numchunks );
+}
+
+bson gridfile_get_chunk( gridfile *gfile, int n ) {
+    bson query;
+    bson out;
+    bson_iterator it;
+    bson_oid_t id;
+
+    bson_init( &query );
+    bson_find( &it, gfile->meta, "_id" );
+    id = *bson_iterator_oid( &it );
+    bson_append_oid( &query, "files_id", &id );
+    bson_append_int( &query, "n", n );
+    bson_finish( &query );
+
+    assert( mongo_find_one( gfile->gfs->client,
+                            gfile->gfs->chunks_ns,
+                            &query, NULL, &out ) == MONGO_OK );
+
+    bson_destroy( &query );
+    return out;
+}
+
+mongo_cursor *gridfile_get_chunks( gridfile *gfile, int start, int size ) {
+    bson_iterator it;
+    bson_oid_t id;
+    bson gte;
+    bson query;
+    bson orderby;
+    bson command;
+    mongo_cursor *cursor;
+
+    bson_find( &it, gfile->meta, "_id" );
+    id = *bson_iterator_oid( &it );
+
+    bson_init( &query );
+    bson_append_oid( &query, "files_id", &id );
+    if ( size == 1 ) {
+        bson_append_int( &query, "n", start );
+    } else {
+        bson_init( &gte );
+        bson_append_int( &gte, "$gte", start );
+        bson_finish( &gte );
+        bson_append_bson( &query, "n", &gte );
+        bson_destroy( &gte );
+    }
+    bson_finish( &query );
+
+    bson_init( &orderby );
+    bson_append_int( &orderby, "n", 1 );
+    bson_finish( &orderby );
+
+    bson_init( &command );
+    bson_append_bson( &command, "query", &query );
+    bson_append_bson( &command, "orderby", &orderby );
+    bson_finish( &command );
+
+    cursor = mongo_find( gfile->gfs->client, gfile->gfs->chunks_ns,
+                         &command, NULL, size, 0, 0 );
+
+    bson_destroy( &command );
+    bson_destroy( &query );
+    bson_destroy( &orderby );
+
+    return cursor;
+}
+
+gridfs_offset gridfile_write_file( gridfile *gfile, FILE *stream ) {
+    int i;
+    size_t len;
+    bson chunk;
+    bson_iterator it;
+    const char *data;
+    const int num = gridfile_get_numchunks( gfile );
+
+    for ( i=0; i<num; i++ ) {
+        chunk = gridfile_get_chunk( gfile, i );
+        bson_find( &it, &chunk, "data" );
+        len = bson_iterator_bin_len( &it );
+        data = bson_iterator_bin_data( &it );
+        fwrite( data , sizeof( char ), len, stream );
+        bson_destroy( &chunk );
+    }
+
+    return gridfile_get_contentlength( gfile );
+}
+
+gridfs_offset gridfile_read( gridfile *gfile, gridfs_offset size, char *buf ) {
+    mongo_cursor *chunks;
+    bson chunk;
+
+    int first_chunk;
+    int last_chunk;
+    int total_chunks;
+    gridfs_offset chunksize;
+    gridfs_offset contentlength;
+    gridfs_offset bytes_left;
+    int i;
+    bson_iterator it;
+    gridfs_offset chunk_len;
+    const char *chunk_data;
+
+    contentlength = gridfile_get_contentlength( gfile );
+    chunksize = gridfile_get_chunksize( gfile );
+    size = ( contentlength - gfile->pos < size )
+           ? contentlength - gfile->pos
+           : size;
+    bytes_left = size;
+
+    first_chunk = ( gfile->pos )/chunksize;
+    last_chunk = ( gfile->pos+size-1 )/chunksize;
+    total_chunks = last_chunk - first_chunk + 1;
+    chunks = gridfile_get_chunks( gfile, first_chunk, total_chunks );
+
+    for ( i = 0; i < total_chunks; i++ ) {
+        mongo_cursor_next( chunks );
+        chunk = chunks->current;
+        bson_find( &it, &chunk, "data" );
+        chunk_len = bson_iterator_bin_len( &it );
+        chunk_data = bson_iterator_bin_data( &it );
+        if ( i == 0 ) {
+            chunk_data += ( gfile->pos )%chunksize;
+            chunk_len -= ( gfile->pos )%chunksize;
+        }
+        if ( bytes_left > chunk_len ) {
+            memcpy( buf, chunk_data, chunk_len );
+            bytes_left -= chunk_len;
+            buf += chunk_len;
+        } else {
+            memcpy( buf, chunk_data, bytes_left );
+        }
+    }
+
+    mongo_cursor_destroy( chunks );
+    gfile->pos = gfile->pos + size;
+
+    return size;
+}
+
+gridfs_offset gridfile_seek( gridfile *gfile, gridfs_offset offset ) {
+    gridfs_offset length;
+
+    length = gridfile_get_contentlength( gfile );
+    gfile->pos = length < offset ? length : offset;
+    return gfile->pos;
+}
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.h
new file mode 100644 (file)
index 0000000..36595ee
--- /dev/null
@@ -0,0 +1,326 @@
+/** @file gridfs.h
+ *
+ *  @brief GridFS declarations
+ *
+ * */
+
+/*    Copyright 2009-2011 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "mongo.h"
+
+#ifndef GRIDFS_INCLUDED
+#define GRIDFS_INCLUDED
+
+enum {DEFAULT_CHUNK_SIZE = 256 * 1024};
+
+typedef uint64_t gridfs_offset;
+
+/* A GridFS represents a single collection of GridFS files in the database. */
+typedef struct {
+    mongo *client; /**> The client to db-connection. */
+    const char *dbname; /**> The root database name */
+    const char *prefix; /**> The prefix of the GridFS's collections, default is NULL */
+    const char *files_ns; /**> The namespace where the file's metadata is stored */
+    const char *chunks_ns; /**. The namespace where the files's data is stored in chunks */
+} gridfs;
+
+/* A GridFile is a single GridFS file. */
+typedef struct {
+    gridfs *gfs;        /**> The GridFS where the GridFile is located */
+    bson *meta;         /**> The GridFile's bson object where all its metadata is located */
+    gridfs_offset pos;  /**> The position is the offset in the file */
+    bson_oid_t id;      /**> The files_id of the gridfile */
+    char *remote_name;  /**> The name of the gridfile as a string */
+    char *content_type; /**> The gridfile's content type */
+    gridfs_offset length; /**> The length of this gridfile */
+    int chunk_num;      /**> The number of the current chunk being written to */
+    char *pending_data; /**> A buffer storing data still to be written to chunks */
+    int pending_len;    /**> Length of pending_data buffer */
+} gridfile;
+
+/**
+ *  Initializes a GridFS object
+ *  @param client - db connection
+ *  @param dbname - database name
+ *  @param prefix - collection prefix, default is fs if NULL or empty
+ *  @param gfs - the GridFS object to initialize
+ *
+ *  @return - MONGO_OK or MONGO_ERROR.
+ */
+int gridfs_init( mongo *client, const char *dbname,
+                 const char *prefix, gridfs *gfs );
+
+/**
+ * Destroys a GridFS object. Call this when finished with
+ * the object..
+ *
+ * @param gfs a grid
+ */
+void gridfs_destroy( gridfs *gfs );
+
+/**
+ *  Initializes a gridfile for writing incrementally with gridfs_write_buffer.
+ *  Once initialized, you can write any number of buffers with gridfs_write_buffer.
+ *  When done, you must call gridfs_writer_done to save the file metadata.
+ *
+ */
+void gridfile_writer_init( gridfile *gfile, gridfs *gfs, const char *remote_name,
+                           const char *content_type );
+
+/**
+ *  Write to a GridFS file incrementally. You can call this function any number
+ *  of times with a new buffer each time. This allows you to effectively
+ *  stream to a GridFS file. When finished, be sure to call gridfs_writer_done.
+ *
+ */
+void gridfile_write_buffer( gridfile *gfile, const char *data,
+                            gridfs_offset length );
+
+/**
+ *  Signal that writing of this gridfile is complete by
+ *  writing any buffered chunks along with the entry in the
+ *  files collection.
+ *
+ *  @return - MONGO_OK or MONGO_ERROR.
+ */
+int gridfile_writer_done( gridfile *gfile );
+
+/**
+ *  Store a buffer as a GridFS file.
+ *  @param gfs - the working GridFS
+ *  @param data - pointer to buffer to store in GridFS
+ *  @param length - length of the buffer
+ *  @param remotename - filename for use in the database
+ *  @param contenttype - optional MIME type for this object
+ *
+ *  @return - MONGO_OK or MONGO_ERROR.
+ */
+int gridfs_store_buffer( gridfs *gfs, const char *data, gridfs_offset length,
+                          const char *remotename,
+                          const char *contenttype );
+
+/**
+ *  Open the file referenced by filename and store it as a GridFS file.
+ *  @param gfs - the working GridFS
+ *  @param filename - local filename relative to the process
+ *  @param remotename - optional filename for use in the database
+ *  @param contenttype - optional MIME type for this object
+ *
+ *  @return - MONGO_OK or MONGO_ERROR.
+ */
+int gridfs_store_file( gridfs *gfs, const char *filename,
+                        const char *remotename, const char *contenttype );
+
+/**
+ *  Removes the files referenced by filename from the db
+ *  @param gfs - the working GridFS
+ *  @param filename - the filename of the file/s to be removed
+ */
+void gridfs_remove_filename( gridfs *gfs, const char *filename );
+
+/**
+ *  Find the first file matching the provided query within the
+ *  GridFS files collection, and return the file as a GridFile.
+ *
+ *  @param gfs - the working GridFS
+ *  @param query - a pointer to the bson with the query data
+ *  @param gfile - the output GridFile to be initialized
+ *
+ *  @return MONGO_OK if successful, MONGO_ERROR otherwise
+ */
+int gridfs_find_query( gridfs *gfs, bson *query, gridfile *gfile );
+
+/**
+ *  Find the first file referenced by filename within the GridFS
+ *  and return it as a GridFile
+ *  @param gfs - the working GridFS
+ *  @param filename - filename of the file to find
+ *  @param gfile - the output GridFile to be intialized
+ *
+ *  @return MONGO_OK or MONGO_ERROR.
+ */
+int gridfs_find_filename( gridfs *gfs, const char *filename, gridfile *gfile );
+
+/**
+ *  Initializes a GridFile containing the GridFS and file bson
+ *  @param gfs - the GridFS where the GridFile is located
+ *  @param meta - the file object
+ *  @param gfile - the output GridFile that is being initialized
+ *
+ *  @return - MONGO_OK or MONGO_ERROR.
+ */
+int gridfile_init( gridfs *gfs, bson *meta, gridfile *gfile );
+
+/**
+ *  Destroys the GridFile
+ *
+ *  @param oGridFIle - the GridFile being destroyed
+ */
+void gridfile_destroy( gridfile *gfile );
+
+/**
+ *  Returns whether or not the GridFile exists
+ *  @param gfile - the GridFile being examined
+ */
+bson_bool_t gridfile_exists( gridfile *gfile );
+
+/**
+ *  Returns the filename of GridFile
+ *  @param gfile - the working GridFile
+ *
+ *  @return - the filename of the Gridfile
+ */
+const char *gridfile_get_filename( gridfile *gfile );
+
+/**
+ *  Returns the size of the chunks of the GridFile
+ *  @param gfile - the working GridFile
+ *
+ *  @return - the size of the chunks of the Gridfile
+ */
+int gridfile_get_chunksize( gridfile *gfile );
+
+/**
+ *  Returns the length of GridFile's data
+ *
+ *  @param gfile - the working GridFile
+ *
+ *  @return - the length of the Gridfile's data
+ */
+gridfs_offset gridfile_get_contentlength( gridfile *gfile );
+
+/**
+ *  Returns the MIME type of the GridFile
+ *
+ *  @param gfile - the working GridFile
+ *
+ *  @return - the MIME type of the Gridfile
+ *            (NULL if no type specified)
+ */
+const char *gridfile_get_contenttype( gridfile *gfile );
+
+/**
+ *  Returns the upload date of GridFile
+ *
+ *  @param gfile - the working GridFile
+ *
+ *  @return - the upload date of the Gridfile
+ */
+bson_date_t gridfile_get_uploaddate( gridfile *gfile );
+
+/**
+ *  Returns the MD5 of GridFile
+ *
+ *  @param gfile - the working GridFile
+ *
+ *  @return - the MD5 of the Gridfile
+ */
+const char *gridfile_get_md5( gridfile *gfile );
+
+/**
+ *  Returns the field in GridFile specified by name
+ *
+ *  @param gfile - the working GridFile
+ *  @param name - the name of the field to be returned
+ *
+ *  @return - the data of the field specified
+ *            (NULL if none exists)
+ */
+const char *gridfile_get_field( gridfile *gfile,
+                                const char *name );
+
+/**
+ *  Returns a boolean field in GridFile specified by name
+ *  @param gfile - the working GridFile
+ *  @param name - the name of the field to be returned
+ *
+ *  @return - the boolean of the field specified
+ *            (NULL if none exists)
+ */
+bson_bool_t gridfile_get_boolean( gridfile *gfile,
+                                  const char *name );
+
+/**
+ *  Returns the metadata of GridFile
+ *  @param gfile - the working GridFile
+ *
+ *  @return - the metadata of the Gridfile in a bson object
+ *            (an empty bson is returned if none exists)
+ */
+bson gridfile_get_metadata( gridfile *gfile );
+
+/**
+ *  Returns the number of chunks in the GridFile
+ *  @param gfile - the working GridFile
+ *
+ *  @return - the number of chunks in the Gridfile
+ */
+int gridfile_get_numchunks( gridfile *gfile );
+
+/**
+ *  Returns chunk n of GridFile
+ *  @param gfile - the working GridFile
+ *
+ *  @return - the nth chunk of the Gridfile
+ */
+bson gridfile_get_chunk( gridfile *gfile, int n );
+
+/**
+ *  Returns a mongo_cursor of *size* chunks starting with chunk *start*
+ *
+ *  @param gfile - the working GridFile
+ *  @param start - the first chunk in the cursor
+ *  @param size - the number of chunks to be returned
+ *
+ *  @return - mongo_cursor of the chunks (must be destroyed after use)
+ */
+mongo_cursor *gridfile_get_chunks( gridfile *gfile, int start, int size );
+
+/**
+ *  Writes the GridFile to a stream
+ *
+ *  @param gfile - the working GridFile
+ *  @param stream - the file stream to write to
+ */
+gridfs_offset gridfile_write_file( gridfile *gfile, FILE *stream );
+
+/**
+ *  Reads length bytes from the GridFile to a buffer
+ *  and updates the position in the file.
+ *  (assumes the buffer is large enough)
+ *  (if size is greater than EOF gridfile_read reads until EOF)
+ *
+ *  @param gfile - the working GridFile
+ *  @param size - the amount of bytes to be read
+ *  @param buf - the buffer to read to
+ *
+ *  @return - the number of bytes read
+ */
+gridfs_offset gridfile_read( gridfile *gfile, gridfs_offset size, char *buf );
+
+/**
+ *  Updates the position in the file
+ *  (If the offset goes beyond the contentlength,
+ *  the position is updated to the end of the file.)
+ *
+ *  @param gfile - the working GridFile
+ *  @param offset - the position to update to
+ *
+ *  @return - resulting offset location
+ */
+gridfs_offset gridfile_seek( gridfile *gfile, gridfs_offset offset );
+
+#endif
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.c
new file mode 100644 (file)
index 0000000..d1c1e3a
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+       http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+       either statically or dynamically; added missing #include <string.h>
+       in library.
+  2002-03-11 lpd Corrected argument list for main(), and added int return
+       type, in test program and T value program.
+  2002-02-21 lpd Added missing #include <stdio.h> in test program.
+  2000-07-03 lpd Patched to eliminate warnings about "constant is
+       unsigned in ANSI C, signed in traditional"; made test program
+       self-checking.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER      /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef MONGO_BIG_ENDIAN
+#  define BYTE_ORDER 1
+#else
+#  define BYTE_ORDER -1
+#endif
+
+#define T_MASK ((mongo_md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3    0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6    0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9    0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13    0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16    0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19    0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22    0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25    0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28    0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31    0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35    0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38    0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41    0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44    0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47    0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50    0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53    0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57    0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60    0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63    0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+mongo_md5_process(mongo_md5_state_t *pms, const mongo_md5_byte_t *data /*[64]*/)
+{
+    mongo_md5_word_t
+       a = pms->abcd[0], b = pms->abcd[1],
+       c = pms->abcd[2], d = pms->abcd[3];
+    mongo_md5_word_t t;
+#if BYTE_ORDER > 0
+    /* Define storage only for big-endian CPUs. */
+    mongo_md5_word_t X[16];
+#else
+    /* Define storage for little-endian or both types of CPUs. */
+    mongo_md5_word_t xbuf[16];
+    const mongo_md5_word_t *X;
+#endif
+
+    {
+#if BYTE_ORDER == 0
+       /*
+        * Determine dynamically whether this is a big-endian or
+        * little-endian machine, since we can use a more efficient
+        * algorithm on the latter.
+        */
+       static const int w = 1;
+
+       if (*((const mongo_md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0            /* little-endian */
+       {
+           /*
+            * On little-endian machines, we can process properly aligned
+            * data without copying it.
+            */
+           if (!((data - (const mongo_md5_byte_t *)0) & 3)) {
+               /* data are properly aligned */
+               X = (const mongo_md5_word_t *)data;
+           } else {
+               /* not aligned */
+               memcpy(xbuf, data, 64);
+               X = xbuf;
+           }
+       }
+#endif
+#if BYTE_ORDER == 0
+       else                    /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0            /* big-endian */
+       {
+           /*
+            * On big-endian machines, we must arrange the bytes in the
+            * right order.
+            */
+           const mongo_md5_byte_t *xp = data;
+           int i;
+
+#  if BYTE_ORDER == 0
+           X = xbuf;           /* (dynamic only) */
+#  else
+#    define xbuf X             /* (static only) */
+#  endif
+           for (i = 0; i < 16; ++i, xp += 4)
+               xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+       }
+#endif
+    }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation
+       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  7,  T1);
+    SET(d, a, b, c,  1, 12,  T2);
+    SET(c, d, a, b,  2, 17,  T3);
+    SET(b, c, d, a,  3, 22,  T4);
+    SET(a, b, c, d,  4,  7,  T5);
+    SET(d, a, b, c,  5, 12,  T6);
+    SET(c, d, a, b,  6, 17,  T7);
+    SET(b, c, d, a,  7, 22,  T8);
+    SET(a, b, c, d,  8,  7,  T9);
+    SET(d, a, b, c,  9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12,  7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+     /* Round 2. */
+     /* Let [abcd k s i] denote the operation
+          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  1,  5, T17);
+    SET(d, a, b, c,  6,  9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a,  0, 20, T20);
+    SET(a, b, c, d,  5,  5, T21);
+    SET(d, a, b, c, 10,  9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a,  4, 20, T24);
+    SET(a, b, c, d,  9,  5, T25);
+    SET(d, a, b, c, 14,  9, T26);
+    SET(c, d, a, b,  3, 14, T27);
+    SET(b, c, d, a,  8, 20, T28);
+    SET(a, b, c, d, 13,  5, T29);
+    SET(d, a, b, c,  2,  9, T30);
+    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+     /* Round 3. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  5,  4, T33);
+    SET(d, a, b, c,  8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d,  1,  4, T37);
+    SET(d, a, b, c,  4, 11, T38);
+    SET(c, d, a, b,  7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13,  4, T41);
+    SET(d, a, b, c,  0, 11, T42);
+    SET(c, d, a, b,  3, 16, T43);
+    SET(b, c, d, a,  6, 23, T44);
+    SET(a, b, c, d,  9,  4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a,  2, 23, T48);
+#undef SET
+
+     /* Round 4. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  6, T49);
+    SET(d, a, b, c,  7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a,  5, 21, T52);
+    SET(a, b, c, d, 12,  6, T53);
+    SET(d, a, b, c,  3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a,  1, 21, T56);
+    SET(a, b, c, d,  8,  6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b,  6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d,  4,  6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b,  2, 15, T63);
+    SET(b, c, d, a,  9, 21, T64);
+#undef SET
+
+     /* Then perform the following additions. (That is increment each
+        of the four registers by the value it had before this block
+        was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void
+mongo_md5_init(mongo_md5_state_t *pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+    pms->abcd[3] = 0x10325476;
+}
+
+void
+mongo_md5_append(mongo_md5_state_t *pms, const mongo_md5_byte_t *data, int nbytes)
+{
+    const mongo_md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    mongo_md5_word_t nbits = (mongo_md5_word_t)(nbytes << 3);
+
+    if (nbytes <= 0)
+       return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+       pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+       int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+       memcpy(pms->buf + offset, p, copy);
+       if (offset + copy < 64)
+           return;
+       p += copy;
+       left -= copy;
+       mongo_md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+       mongo_md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+       memcpy(pms->buf, p, left);
+}
+
+void
+mongo_md5_finish(mongo_md5_state_t *pms, mongo_md5_byte_t digest[16])
+{
+    static const mongo_md5_byte_t pad[64] = {
+       0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    mongo_md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+       data[i] = (mongo_md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    mongo_md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    mongo_md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+       digest[i] = (mongo_md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.h
new file mode 100644 (file)
index 0000000..540da3a
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+       http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Removed support for non-ANSI compilers; removed
+       references to Ghostscript; clarified derivation from RFC 1321;
+       now handles byte order either statically or dynamically.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+       added conditionalization for C++ compilation from Martin
+       Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char mongo_md5_byte_t; /* 8-bit byte */
+typedef unsigned int mongo_md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct mongo_md5_state_s {
+    mongo_md5_word_t count[2]; /* message length in bits, lsw first */
+    mongo_md5_word_t abcd[4];          /* digest buffer */
+    mongo_md5_byte_t buf[64];          /* accumulate block */
+} mongo_md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+    /* Initialize the algorithm. */
+    void mongo_md5_init(mongo_md5_state_t *pms);
+
+    /* Append a string to the message. */
+    void mongo_md5_append(mongo_md5_state_t *pms, const mongo_md5_byte_t *data, int nbytes);
+
+    /* Finish the message and return the digest. */
+    void mongo_md5_finish(mongo_md5_state_t *pms, mongo_md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c
new file mode 100644 (file)
index 0000000..2090e74
--- /dev/null
@@ -0,0 +1,1217 @@
+/* mongo.c */
+
+/*    Copyright 2009-2011 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "mongo.h"
+#include "md5.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef _USE_LINUX_SYSTEM
+#include "platform/linux/net.h"
+#elif defined _USE_CUSTOM_SYSTEM
+#include "platform/custom/net.h"
+#else
+#include "net.h"
+#endif
+
+static const int ZERO = 0;
+static const int ONE = 1;
+mongo_message *mongo_message_create( int len , int id , int responseTo , int op ) {
+    mongo_message *mm = ( mongo_message * )bson_malloc( len );
+
+    if ( !id )
+        id = rand();
+
+    /* native endian (converted on send) */
+    mm->head.len = len;
+    mm->head.id = id;
+    mm->head.responseTo = responseTo;
+    mm->head.op = op;
+
+    return mm;
+}
+
+/* Always calls bson_free(mm) */
+int mongo_message_send( mongo *conn, mongo_message *mm ) {
+    mongo_header head; /* little endian */
+    int res;
+    bson_little_endian32( &head.len, &mm->head.len );
+    bson_little_endian32( &head.id, &mm->head.id );
+    bson_little_endian32( &head.responseTo, &mm->head.responseTo );
+    bson_little_endian32( &head.op, &mm->head.op );
+
+    res = mongo_write_socket( conn, &head, sizeof( head ) );
+    if( res != MONGO_OK ) {
+        bson_free( mm );
+        return res;
+    }
+
+    res = mongo_write_socket( conn, &mm->data, mm->head.len - sizeof( head ) );
+    if( res != MONGO_OK ) {
+        bson_free( mm );
+        return res;
+    }
+
+    bson_free( mm );
+    return MONGO_OK;
+}
+
+int mongo_read_response( mongo *conn, mongo_reply **reply ) {
+    mongo_header head; /* header from network */
+    mongo_reply_fields fields; /* header from network */
+    mongo_reply *out;  /* native endian */
+    unsigned int len;
+    int res;
+
+    mongo_read_socket( conn, &head, sizeof( head ) );
+    mongo_read_socket( conn, &fields, sizeof( fields ) );
+
+    bson_little_endian32( &len, &head.len );
+
+    if ( len < sizeof( head )+sizeof( fields ) || len > 64*1024*1024 )
+        return MONGO_READ_SIZE_ERROR;  /* most likely corruption */
+
+    out = ( mongo_reply * )bson_malloc( len );
+
+    out->head.len = len;
+    bson_little_endian32( &out->head.id, &head.id );
+    bson_little_endian32( &out->head.responseTo, &head.responseTo );
+    bson_little_endian32( &out->head.op, &head.op );
+
+    bson_little_endian32( &out->fields.flag, &fields.flag );
+    bson_little_endian64( &out->fields.cursorID, &fields.cursorID );
+    bson_little_endian32( &out->fields.start, &fields.start );
+    bson_little_endian32( &out->fields.num, &fields.num );
+
+    res = mongo_read_socket( conn, &out->objs, len-sizeof( head )-sizeof( fields ) );
+    if( res != MONGO_OK ) {
+        bson_free( out );
+        return res;
+    }
+
+    *reply = out;
+
+    return MONGO_OK;
+}
+
+
+char *mongo_data_append( char *start , const void *data , int len ) {
+    memcpy( start , data , len );
+    return start + len;
+}
+
+char *mongo_data_append32( char *start , const void *data ) {
+    bson_little_endian32( start , data );
+    return start + 4;
+}
+
+char *mongo_data_append64( char *start , const void *data ) {
+    bson_little_endian64( start , data );
+    return start + 8;
+}
+
+/* Connection API */
+
+static int mongo_check_is_master( mongo *conn ) {
+    bson out;
+    bson_iterator it;
+    bson_bool_t ismaster = 0;
+
+    out.data = NULL;
+
+    if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) {
+        if( bson_find( &it, &out, "ismaster" ) )
+            ismaster = bson_iterator_bool( &it );
+    } else {
+        return MONGO_ERROR;
+    }
+
+    bson_destroy( &out );
+
+    if( ismaster )
+        return MONGO_OK;
+    else {
+        conn->err = MONGO_CONN_NOT_MASTER;
+        return MONGO_ERROR;
+    }
+}
+
+void mongo_init( mongo *conn ) {
+    conn->replset = NULL;
+    conn->err = 0;
+    conn->errstr = NULL;
+    conn->lasterrcode = 0;
+    conn->lasterrstr = NULL;
+
+    conn->conn_timeout_ms = 0;
+    conn->op_timeout_ms = 0;
+}
+
+int mongo_connect( mongo *conn , const char *host, int port ) {
+    conn->primary = bson_malloc( sizeof( mongo_host_port ) );
+    strncpy( conn->primary->host, host, strlen( host ) + 1 );
+    conn->primary->port = port;
+    conn->primary->next = NULL;
+
+    mongo_init( conn );
+    if( mongo_socket_connect( conn, host, port ) != MONGO_OK )
+        return MONGO_ERROR;
+
+    if( mongo_check_is_master( conn ) != MONGO_OK )
+        return MONGO_ERROR;
+    else
+        return MONGO_OK;
+}
+
+void mongo_replset_init( mongo *conn, const char *name ) {
+    mongo_init( conn );
+
+    conn->replset = bson_malloc( sizeof( mongo_replset ) );
+    conn->replset->primary_connected = 0;
+    conn->replset->seeds = NULL;
+    conn->replset->hosts = NULL;
+    conn->replset->name = ( char * )bson_malloc( strlen( name ) + 1 );
+    memcpy( conn->replset->name, name, strlen( name ) + 1  );
+
+    conn->primary = bson_malloc( sizeof( mongo_host_port ) );
+}
+
+static void mongo_replset_add_node( mongo_host_port **list, const char *host, int port ) {
+    mongo_host_port *host_port = bson_malloc( sizeof( mongo_host_port ) );
+    host_port->port = port;
+    host_port->next = NULL;
+    strncpy( host_port->host, host, strlen( host ) + 1 );
+
+    if( *list == NULL )
+        *list = host_port;
+    else {
+        mongo_host_port *p = *list;
+        while( p->next != NULL )
+            p = p->next;
+        p->next = host_port;
+    }
+}
+
+static void mongo_replset_free_list( mongo_host_port **list ) {
+    mongo_host_port *node = *list;
+    mongo_host_port *prev;
+
+    while( node != NULL ) {
+        prev = node;
+        node = node->next;
+        bson_free( prev );
+    }
+
+    *list = NULL;
+}
+
+void mongo_replset_add_seed( mongo *conn, const char *host, int port ) {
+    mongo_replset_add_node( &conn->replset->seeds, host, port );
+}
+
+void mongo_parse_host( const char *host_string, mongo_host_port *host_port ) {
+    int len, idx, split;
+    len = split = idx = 0;
+
+    /* Split the host_port string at the ':' */
+    while( 1 ) {
+        if( *( host_string + len ) == '\0' )
+            break;
+        if( *( host_string + len ) == ':' )
+            split = len;
+
+        len++;
+    }
+
+    /* If 'split' is set, we know the that port exists;
+     * Otherwise, we set the default port. */
+    idx = split ? split : len;
+    memcpy( host_port->host, host_string, idx );
+    memcpy( host_port->host + idx, "\0", 1 );
+    if( split )
+        host_port->port = atoi( host_string + idx + 1 );
+    else
+        host_port->port = MONGO_DEFAULT_PORT;
+}
+
+static void mongo_replset_check_seed( mongo *conn ) {
+    bson out;
+    bson hosts;
+    const char *data;
+    bson_iterator it;
+    bson_iterator it_sub;
+    const char *host_string;
+    mongo_host_port *host_port = NULL;
+
+    out.data = NULL;
+
+    hosts.data = NULL;
+
+    if( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) {
+
+        if( bson_find( &it, &out, "hosts" ) ) {
+            data = bson_iterator_value( &it );
+            bson_iterator_from_buffer( &it_sub, data );
+
+            /* Iterate over host list, adding each host to the
+             * connection's host list. */
+            while( bson_iterator_next( &it_sub ) ) {
+                host_string = bson_iterator_string( &it_sub );
+
+                host_port = bson_malloc( sizeof( mongo_host_port ) );
+                mongo_parse_host( host_string, host_port );
+
+                if( host_port ) {
+                    mongo_replset_add_node( &conn->replset->hosts,
+                                            host_port->host, host_port->port );
+
+                    bson_free( host_port );
+                    host_port = NULL;
+                }
+            }
+        }
+    }
+
+    bson_destroy( &out );
+    bson_destroy( &hosts );
+    mongo_close_socket( conn->sock );
+    conn->sock = 0;
+    conn->connected = 0;
+
+}
+
+/* Find out whether the current connected node is master, and
+ * verify that the node's replica set name matched the provided name
+ */
+static int mongo_replset_check_host( mongo *conn ) {
+
+    bson out;
+    bson_iterator it;
+    bson_bool_t ismaster = 0;
+    const char *set_name;
+
+    out.data = NULL;
+
+    if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) {
+        if( bson_find( &it, &out, "ismaster" ) )
+            ismaster = bson_iterator_bool( &it );
+
+        if( bson_find( &it, &out, "setName" ) ) {
+            set_name = bson_iterator_string( &it );
+            if( strcmp( set_name, conn->replset->name ) != 0 ) {
+                bson_destroy( &out );
+                conn->err = MONGO_CONN_BAD_SET_NAME;
+                return MONGO_ERROR;
+            }
+        }
+    }
+
+    bson_destroy( &out );
+
+    if( ismaster ) {
+        conn->replset->primary_connected = 1;
+    } else {
+        mongo_close_socket( conn->sock );
+    }
+
+    return MONGO_OK;
+}
+
+int mongo_replset_connect( mongo *conn ) {
+
+    int res = 0;
+    mongo_host_port *node;
+
+    conn->sock = 0;
+    conn->connected = 0;
+
+    /* First iterate over the seed nodes to get the canonical list of hosts
+     * from the replica set. Break out once we have a host list.
+     */
+    node = conn->replset->seeds;
+    while( node != NULL ) {
+        res = mongo_socket_connect( conn, ( const char * )&node->host, node->port );
+        if( res != MONGO_OK )
+            return MONGO_ERROR;
+
+        mongo_replset_check_seed( conn );
+
+        if( conn->replset->hosts )
+            break;
+
+        node = node->next;
+    }
+
+    /* Iterate over the host list, checking for the primary node. */
+    if( !conn->replset->hosts ) {
+        conn->err = MONGO_CONN_NO_PRIMARY;
+        return MONGO_ERROR;
+    } else {
+        node = conn->replset->hosts;
+
+        while( node != NULL ) {
+            res = mongo_socket_connect( conn, ( const char * )&node->host, node->port );
+
+            if( res == MONGO_OK ) {
+                if( mongo_replset_check_host( conn ) != MONGO_OK )
+                    return MONGO_ERROR;
+
+                /* Primary found, so return. */
+                else if( conn->replset->primary_connected )
+                    return MONGO_OK;
+
+                /* No primary, so close the connection. */
+                else {
+                    mongo_close_socket( conn->sock );
+                    conn->sock = 0;
+                    conn->connected = 0;
+                }
+            }
+
+            node = node->next;
+        }
+    }
+
+
+    conn->err = MONGO_CONN_NO_PRIMARY;
+    return MONGO_ERROR;
+}
+
+int mongo_set_op_timeout( mongo *conn, int millis ) {
+    conn->op_timeout_ms = millis;
+    if( conn->sock && conn->connected )
+        mongo_set_socket_op_timeout( conn, millis );
+
+    return MONGO_OK;
+}
+
+int mongo_reconnect( mongo *conn ) {
+    int res;
+    mongo_disconnect( conn );
+
+    if( conn->replset ) {
+        conn->replset->primary_connected = 0;
+        mongo_replset_free_list( &conn->replset->hosts );
+        conn->replset->hosts = NULL;
+        res = mongo_replset_connect( conn );
+        return res;
+    } else
+        return mongo_socket_connect( conn, conn->primary->host, conn->primary->port );
+}
+
+int mongo_check_connection( mongo *conn ) {
+    if( ! conn->connected )
+        return MONGO_ERROR;
+
+    if( mongo_simple_int_command( conn, "admin", "ping", 1, NULL ) == MONGO_OK )
+        return MONGO_OK;
+    else
+        return MONGO_ERROR;
+}
+
+void mongo_disconnect( mongo *conn ) {
+    if( ! conn->connected )
+        return;
+
+    if( conn->replset ) {
+        conn->replset->primary_connected = 0;
+        mongo_replset_free_list( &conn->replset->hosts );
+        conn->replset->hosts = NULL;
+    }
+
+    mongo_close_socket( conn->sock );
+
+    conn->sock = 0;
+    conn->connected = 0;
+}
+
+void mongo_destroy( mongo *conn ) {
+    mongo_disconnect( conn );
+
+    if( conn->replset ) {
+        mongo_replset_free_list( &conn->replset->seeds );
+        mongo_replset_free_list( &conn->replset->hosts );
+        bson_free( conn->replset->name );
+        bson_free( conn->replset );
+        conn->replset = NULL;
+    }
+
+    bson_free( conn->primary );
+    bson_free( conn->errstr );
+    bson_free( conn->lasterrstr );
+
+    conn->err = 0;
+    conn->errstr = NULL;
+    conn->lasterrcode = 0;
+    conn->lasterrstr = NULL;
+}
+
+/* Determine whether this BSON object is valid for the given operation.  */
+static int mongo_bson_valid( mongo *conn, bson *bson, int write ) {
+    if( ! bson->finished ) {
+        conn->err = MONGO_BSON_NOT_FINISHED;
+        return MONGO_ERROR;
+    }
+
+    if( bson->err & BSON_NOT_UTF8 ) {
+        conn->err = MONGO_BSON_INVALID;
+        return MONGO_ERROR;
+    }
+
+    if( write ) {
+        if( ( bson->err & BSON_FIELD_HAS_DOT ) ||
+                ( bson->err & BSON_FIELD_INIT_DOLLAR ) ) {
+
+            conn->err = MONGO_BSON_INVALID;
+            return MONGO_ERROR;
+
+        }
+    }
+
+    conn->err = 0;
+    conn->errstr = NULL;
+
+    return MONGO_OK;
+}
+
+/* Determine whether this BSON object is valid for the given operation.  */
+static int mongo_cursor_bson_valid( mongo_cursor *cursor, bson *bson ) {
+    if( ! bson->finished ) {
+        cursor->err = MONGO_BSON_NOT_FINISHED;
+        return MONGO_ERROR;
+    }
+
+    if( bson->err & BSON_NOT_UTF8 ) {
+        cursor->err = MONGO_BSON_INVALID;
+        return MONGO_ERROR;
+    }
+
+    return MONGO_OK;
+}
+
+/* MongoDB CRUD API */
+
+int mongo_insert_batch( mongo *conn, const char *ns,
+                        bson **bsons, int count ) {
+
+    int size =  16 + 4 + strlen( ns ) + 1;
+    int i;
+    mongo_message *mm;
+    char *data;
+
+    for( i=0; i<count; i++ ) {
+        size += bson_size( bsons[i] );
+        if( mongo_bson_valid( conn, bsons[i], 1 ) != MONGO_OK )
+            return MONGO_ERROR;
+    }
+
+    mm = mongo_message_create( size , 0 , 0 , MONGO_OP_INSERT );
+
+    data = &mm->data;
+    data = mongo_data_append32( data, &ZERO );
+    data = mongo_data_append( data, ns, strlen( ns ) + 1 );
+
+    for( i=0; i<count; i++ ) {
+        data = mongo_data_append( data, bsons[i]->data, bson_size( bsons[i] ) );
+    }
+
+    return mongo_message_send( conn, mm );
+}
+
+int mongo_insert( mongo *conn , const char *ns , bson *bson ) {
+
+    char *data;
+    mongo_message *mm;
+
+    /* Make sure that BSON is valid for insert. */
+    if( mongo_bson_valid( conn, bson, 1 ) != MONGO_OK ) {
+        return MONGO_ERROR;
+    }
+
+    mm = mongo_message_create( 16 /* header */
+                               + 4 /* ZERO */
+                               + strlen( ns )
+                               + 1 + bson_size( bson )
+                               , 0, 0, MONGO_OP_INSERT );
+
+    data = &mm->data;
+    data = mongo_data_append32( data, &ZERO );
+    data = mongo_data_append( data, ns, strlen( ns ) + 1 );
+    data = mongo_data_append( data, bson->data, bson_size( bson ) );
+
+    return mongo_message_send( conn, mm );
+}
+
+int mongo_update( mongo *conn, const char *ns, const bson *cond,
+                  const bson *op, int flags ) {
+
+    char *data;
+    mongo_message *mm;
+
+    /* Make sure that the op BSON is valid UTF-8.
+     * TODO: decide whether to check cond as well.
+     * */
+    if( mongo_bson_valid( conn, ( bson * )op, 0 ) != MONGO_OK ) {
+        return MONGO_ERROR;
+    }
+
+    mm = mongo_message_create( 16 /* header */
+                               + 4  /* ZERO */
+                               + strlen( ns ) + 1
+                               + 4  /* flags */
+                               + bson_size( cond )
+                               + bson_size( op )
+                               , 0 , 0 , MONGO_OP_UPDATE );
+
+    data = &mm->data;
+    data = mongo_data_append32( data, &ZERO );
+    data = mongo_data_append( data, ns, strlen( ns ) + 1 );
+    data = mongo_data_append32( data, &flags );
+    data = mongo_data_append( data, cond->data, bson_size( cond ) );
+    data = mongo_data_append( data, op->data, bson_size( op ) );
+
+    return mongo_message_send( conn, mm );
+}
+
+int mongo_remove( mongo *conn, const char *ns, const bson *cond ) {
+    char *data;
+    mongo_message *mm = mongo_message_create( 16  /* header */
+                        + 4  /* ZERO */
+                        + strlen( ns ) + 1
+                        + 4  /* ZERO */
+                        + bson_size( cond )
+                        , 0 , 0 , MONGO_OP_DELETE );
+
+    /* Make sure that the BSON is valid UTF-8.
+     * TODO: decide whether to check cond as well.
+     * */
+    if( mongo_bson_valid( conn, ( bson * )cond, 0 ) != MONGO_OK ) {
+        return MONGO_ERROR;
+    }
+
+    data = &mm->data;
+    data = mongo_data_append32( data, &ZERO );
+    data = mongo_data_append( data, ns, strlen( ns ) + 1 );
+    data = mongo_data_append32( data, &ZERO );
+    data = mongo_data_append( data, cond->data, bson_size( cond ) );
+
+    return mongo_message_send( conn, mm );
+}
+
+
+static int mongo_cursor_op_query( mongo_cursor *cursor ) {
+    int res;
+    bson empty;
+    char *data;
+    mongo_message *mm;
+
+    /* Set up default values for query and fields, if necessary. */
+    if( ! cursor->query )
+        cursor->query = bson_empty( &empty );
+    else if( mongo_cursor_bson_valid( cursor, cursor->query ) != MONGO_OK )
+        return MONGO_ERROR;
+
+    if( ! cursor->fields )
+        cursor->fields = bson_empty( &empty );
+    else if( mongo_cursor_bson_valid( cursor, cursor->fields ) != MONGO_OK )
+        return MONGO_ERROR;
+
+    mm = mongo_message_create( 16 + /* header */
+                               4 + /*  options */
+                               strlen( cursor->ns ) + 1 + /* ns */
+                               4 + 4 + /* skip,return */
+                               bson_size( cursor->query ) +
+                               bson_size( cursor->fields ) ,
+                               0 , 0 , MONGO_OP_QUERY );
+
+    data = &mm->data;
+    data = mongo_data_append32( data , &cursor->options );
+    data = mongo_data_append( data , cursor->ns , strlen( cursor->ns ) + 1 );
+    data = mongo_data_append32( data , &cursor->skip );
+    data = mongo_data_append32( data , &cursor->limit );
+    data = mongo_data_append( data , cursor->query->data , bson_size( cursor->query ) );
+    if ( cursor->fields )
+        data = mongo_data_append( data , cursor->fields->data , bson_size( cursor->fields ) );
+
+    bson_fatal_msg( ( data == ( ( char * )mm ) + mm->head.len ), "query building fail!" );
+
+    res = mongo_message_send( cursor->conn , mm );
+    if( res != MONGO_OK ) {
+        return MONGO_ERROR;
+    }
+
+    res = mongo_read_response( cursor->conn, ( mongo_reply ** )&( cursor->reply ) );
+    if( res != MONGO_OK ) {
+        return MONGO_ERROR;
+    }
+
+    cursor->seen += cursor->reply->fields.num;
+    cursor->flags |= MONGO_CURSOR_QUERY_SENT;
+    return MONGO_OK;
+}
+
+static int mongo_cursor_get_more( mongo_cursor *cursor ) {
+    int res;
+
+    if( cursor->limit > 0 && cursor->seen >= cursor->limit ) {
+        cursor->err = MONGO_CURSOR_EXHAUSTED;
+        return MONGO_ERROR;
+    } else if( ! cursor->reply ) {
+        cursor->err = MONGO_CURSOR_INVALID;
+        return MONGO_ERROR;
+    } else if( ! cursor->reply->fields.cursorID ) {
+        cursor->err = MONGO_CURSOR_EXHAUSTED;
+        return MONGO_ERROR;
+    } else {
+        char *data;
+        int sl = strlen( cursor->ns )+1;
+        int limit = 0;
+        mongo_message *mm;
+
+        if( cursor->limit > 0 )
+            limit = cursor->limit - cursor->seen;
+
+        mm = mongo_message_create( 16 /*header*/
+                                   +4 /*ZERO*/
+                                   +sl
+                                   +4 /*numToReturn*/
+                                   +8 /*cursorID*/
+                                   , 0, 0, MONGO_OP_GET_MORE );
+        data = &mm->data;
+        data = mongo_data_append32( data, &ZERO );
+        data = mongo_data_append( data, cursor->ns, sl );
+        data = mongo_data_append32( data, &limit );
+        data = mongo_data_append64( data, &cursor->reply->fields.cursorID );
+
+        bson_free( cursor->reply );
+        res = mongo_message_send( cursor->conn, mm );
+        if( res != MONGO_OK ) {
+            mongo_cursor_destroy( cursor );
+            return MONGO_ERROR;
+        }
+
+        res = mongo_read_response( cursor->conn, &( cursor->reply ) );
+        if( res != MONGO_OK ) {
+            mongo_cursor_destroy( cursor );
+            return MONGO_ERROR;
+        }
+        cursor->current.data = NULL;
+        cursor->seen += cursor->reply->fields.num;
+
+        return MONGO_OK;
+    }
+}
+
+mongo_cursor *mongo_find( mongo *conn, const char *ns, bson *query,
+                          bson *fields, int limit, int skip, int options ) {
+
+    mongo_cursor *cursor = ( mongo_cursor * )bson_malloc( sizeof( mongo_cursor ) );
+    mongo_cursor_init( cursor, conn, ns );
+    cursor->flags |= MONGO_CURSOR_MUST_FREE;
+
+    mongo_cursor_set_query( cursor, query );
+    mongo_cursor_set_fields( cursor, fields );
+    mongo_cursor_set_limit( cursor, limit );
+    mongo_cursor_set_skip( cursor, skip );
+    mongo_cursor_set_options( cursor, options );
+
+    if( mongo_cursor_op_query( cursor ) == MONGO_OK )
+        return cursor;
+    else {
+        mongo_cursor_destroy( cursor );
+        return NULL;
+    }
+}
+
+int mongo_find_one( mongo *conn, const char *ns, bson *query,
+                    bson *fields, bson *out ) {
+
+    mongo_cursor *cursor = mongo_find( conn, ns, query, fields, 1, 0, 0 );
+
+    if ( cursor && mongo_cursor_next( cursor ) == MONGO_OK ) {
+        bson_copy_basic( out, &cursor->current );
+        mongo_cursor_destroy( cursor );
+        return MONGO_OK;
+    } else {
+        mongo_cursor_destroy( cursor );
+        return MONGO_ERROR;
+    }
+}
+
+void mongo_cursor_init( mongo_cursor *cursor, mongo *conn, const char *ns ) {
+    cursor->conn = conn;
+    cursor->ns = ( const char * )bson_malloc( strlen( ns ) + 1 );
+    strncpy( ( char * )cursor->ns, ns, strlen( ns ) + 1 );
+    cursor->current.data = NULL;
+    cursor->reply = NULL;
+    cursor->flags = 0;
+    cursor->seen = 0;
+    cursor->err = 0;
+    cursor->options = 0;
+    cursor->query = NULL;
+    cursor->fields = NULL;
+    cursor->skip = 0;
+    cursor->limit = 0;
+}
+
+void mongo_cursor_set_query( mongo_cursor *cursor, bson *query ) {
+    cursor->query = query;
+}
+
+void mongo_cursor_set_fields( mongo_cursor *cursor, bson *fields ) {
+    cursor->fields = fields;
+}
+
+void mongo_cursor_set_skip( mongo_cursor *cursor, int skip ) {
+    cursor->skip = skip;
+}
+
+void mongo_cursor_set_limit( mongo_cursor *cursor, int limit ) {
+    cursor->limit = limit;
+}
+
+void mongo_cursor_set_options( mongo_cursor *cursor, int options ) {
+    cursor->options = options;
+}
+
+const char *mongo_cursor_data( mongo_cursor *cursor ) {
+    return cursor->current.data;
+}
+
+const bson *mongo_cursor_bson( mongo_cursor *cursor ) {
+    return (const bson *)&(cursor->current);
+}
+
+int mongo_cursor_next( mongo_cursor *cursor ) {
+    char *next_object;
+    char *message_end;
+
+    if( ! ( cursor->flags & MONGO_CURSOR_QUERY_SENT ) )
+        mongo_cursor_op_query( cursor );
+
+    if( !cursor->reply )
+        return MONGO_ERROR;
+
+    /* no data */
+    if ( cursor->reply->fields.num == 0 ) {
+
+        /* Special case for tailable cursors. */
+        if( cursor->reply->fields.cursorID ) {
+            if( ( mongo_cursor_get_more( cursor ) != MONGO_OK ) ||
+                    cursor->reply->fields.num == 0 ) {
+                return MONGO_ERROR;
+            }
+        }
+
+        else
+            return MONGO_ERROR;
+    }
+
+    /* first */
+    if ( cursor->current.data == NULL ) {
+        bson_init_data( &cursor->current, &cursor->reply->objs );
+        return MONGO_OK;
+    }
+
+    next_object = cursor->current.data + bson_size( &cursor->current );
+    message_end = ( char * )cursor->reply + cursor->reply->head.len;
+
+    if ( next_object >= message_end ) {
+        if( mongo_cursor_get_more( cursor ) != MONGO_OK )
+            return MONGO_ERROR;
+
+        /* If there's still a cursor id, then the message should be pending. */
+        if( cursor->reply->fields.num == 0 && cursor->reply->fields.cursorID ) {
+            cursor->err = MONGO_CURSOR_PENDING;
+            return MONGO_ERROR;
+        }
+
+        bson_init_data( &cursor->current, &cursor->reply->objs );
+    } else {
+        bson_init_data( &cursor->current, next_object );
+    }
+
+    return MONGO_OK;
+}
+
+int mongo_cursor_destroy( mongo_cursor *cursor ) {
+    int result = MONGO_OK;
+
+    if ( !cursor ) return result;
+
+    /* Kill cursor if live. */
+    if ( cursor->reply && cursor->reply->fields.cursorID ) {
+        mongo *conn = cursor->conn;
+        mongo_message *mm = mongo_message_create( 16 /*header*/
+                            +4 /*ZERO*/
+                            +4 /*numCursors*/
+                            +8 /*cursorID*/
+                            , 0, 0, MONGO_OP_KILL_CURSORS );
+        char *data = &mm->data;
+        data = mongo_data_append32( data, &ZERO );
+        data = mongo_data_append32( data, &ONE );
+        data = mongo_data_append64( data, &cursor->reply->fields.cursorID );
+
+        result = mongo_message_send( conn, mm );
+    }
+
+    bson_free( cursor->reply );
+    bson_free( ( void * )cursor->ns );
+
+    if( cursor->flags & MONGO_CURSOR_MUST_FREE )
+        bson_free( cursor );
+
+    return result;
+}
+
+/* MongoDB Helper Functions */
+
+int mongo_create_index( mongo *conn, const char *ns, bson *key, int options, bson *out ) {
+    bson b;
+    bson_iterator it;
+    char name[255] = {'_'};
+    int i = 1;
+    char idxns[1024];
+
+    bson_iterator_init( &it, key );
+    while( i < 255 && bson_iterator_next( &it ) ) {
+        strncpy( name + i, bson_iterator_key( &it ), 255 - i );
+        i += strlen( bson_iterator_key( &it ) );
+    }
+    name[254] = '\0';
+
+    bson_init( &b );
+    bson_append_bson( &b, "key", key );
+    bson_append_string( &b, "ns", ns );
+    bson_append_string( &b, "name", name );
+    if ( options & MONGO_INDEX_UNIQUE )
+        bson_append_bool( &b, "unique", 1 );
+    if ( options & MONGO_INDEX_DROP_DUPS )
+        bson_append_bool( &b, "dropDups", 1 );
+    if ( options & MONGO_INDEX_BACKGROUND )
+        bson_append_bool( &b, "background", 1 );
+    if ( options & MONGO_INDEX_SPARSE )
+        bson_append_bool( &b, "sparse", 1 );
+    bson_finish( &b );
+
+    strncpy( idxns, ns, 1024-16 );
+    strcpy( strchr( idxns, '.' ), ".system.indexes" );
+    mongo_insert( conn, idxns, &b );
+    bson_destroy( &b );
+
+    *strchr( idxns, '.' ) = '\0'; /* just db not ns */
+    return mongo_cmd_get_last_error( conn, idxns, out );
+}
+
+bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char *field, int options, bson *out ) {
+    bson b;
+    bson_bool_t success;
+
+    bson_init( &b );
+    bson_append_int( &b, field, 1 );
+    bson_finish( &b );
+
+    success = mongo_create_index( conn, ns, &b, options, out );
+    bson_destroy( &b );
+    return success;
+}
+
+int64_t mongo_count( mongo *conn, const char *db, const char *ns, bson *query ) {
+    bson cmd;
+    bson out = {NULL, 0};
+    int64_t count = -1;
+
+    bson_init( &cmd );
+    bson_append_string( &cmd, "count", ns );
+    if ( query && bson_size( query ) > 5 ) /* not empty */
+        bson_append_bson( &cmd, "query", query );
+    bson_finish( &cmd );
+
+    if( mongo_run_command( conn, db, &cmd, &out ) == MONGO_OK ) {
+        bson_iterator it;
+        if( bson_find( &it, &out, "n" ) )
+            count = bson_iterator_long( &it );
+        bson_destroy( &cmd );
+        bson_destroy( &out );
+        return count;
+    } else {
+        bson_destroy( &out );
+        bson_destroy( &cmd );
+        return MONGO_ERROR;
+    }
+}
+
+int mongo_run_command( mongo *conn, const char *db, bson *command,
+                       bson *out ) {
+
+    bson fields;
+    int sl = strlen( db );
+    char *ns = bson_malloc( sl + 5 + 1 ); /* ".$cmd" + nul */
+    int res;
+
+    strcpy( ns, db );
+    strcpy( ns+sl, ".$cmd" );
+
+    res = mongo_find_one( conn, ns, command, bson_empty( &fields ), out );
+    bson_free( ns );
+    return res;
+}
+
+int mongo_simple_int_command( mongo *conn, const char *db,
+                              const char *cmdstr, int arg, bson *realout ) {
+
+    bson out = {NULL, 0};
+    bson cmd;
+    bson_bool_t success = 0;
+
+    bson_init( &cmd );
+    bson_append_int( &cmd, cmdstr, arg );
+    bson_finish( &cmd );
+
+    if( mongo_run_command( conn, db, &cmd, &out ) == MONGO_OK ) {
+        bson_iterator it;
+        if( bson_find( &it, &out, "ok" ) )
+            success = bson_iterator_bool( &it );
+    }
+
+    bson_destroy( &cmd );
+
+    if ( realout )
+        *realout = out;
+    else
+        bson_destroy( &out );
+
+    if( success )
+        return MONGO_OK;
+    else {
+        conn->err = MONGO_COMMAND_FAILED;
+        return MONGO_ERROR;
+    }
+}
+
+int mongo_simple_str_command( mongo *conn, const char *db,
+                              const char *cmdstr, const char *arg, bson *realout ) {
+
+    bson out = {NULL, 0};
+    int success = 0;
+
+    bson cmd;
+    bson_init( &cmd );
+    bson_append_string( &cmd, cmdstr, arg );
+    bson_finish( &cmd );
+
+    if( mongo_run_command( conn, db, &cmd, &out ) == MONGO_OK ) {
+        bson_iterator it;
+        if( bson_find( &it, &out, "ok" ) )
+            success = bson_iterator_bool( &it );
+    }
+
+    bson_destroy( &cmd );
+
+    if ( realout )
+        *realout = out;
+    else
+        bson_destroy( &out );
+
+    if( success )
+        return MONGO_OK;
+    else
+        return MONGO_ERROR;
+}
+
+int mongo_cmd_drop_db( mongo *conn, const char *db ) {
+    return mongo_simple_int_command( conn, db, "dropDatabase", 1, NULL );
+}
+
+int mongo_cmd_drop_collection( mongo *conn, const char *db, const char *collection, bson *out ) {
+    return mongo_simple_str_command( conn, db, "drop", collection, out );
+}
+
+void mongo_cmd_reset_error( mongo *conn, const char *db ) {
+    mongo_simple_int_command( conn, db, "reseterror", 1, NULL );
+}
+
+static int mongo_cmd_get_error_helper( mongo *conn, const char *db,
+                                       bson *realout, const char *cmdtype ) {
+
+    bson out = {NULL,0};
+    bson_bool_t haserror = 0;
+
+    /* Reset last error codes. */
+    conn->lasterrcode = 0;
+    bson_free( conn->lasterrstr );
+    conn->lasterrstr = NULL;
+
+    /* If there's an error, store its code and string in the connection object. */
+    if( mongo_simple_int_command( conn, db, cmdtype, 1, &out ) == MONGO_OK ) {
+        bson_iterator it;
+        haserror = ( bson_find( &it, &out, "err" ) != BSON_NULL );
+        if( haserror ) {
+            conn->lasterrstr = ( char * )bson_malloc( bson_iterator_string_len( &it ) );
+            if( conn->lasterrstr ) {
+                strcpy( conn->lasterrstr, bson_iterator_string( &it ) );
+            }
+
+            if( bson_find( &it, &out, "code" ) != BSON_NULL )
+                conn->lasterrcode = bson_iterator_int( &it );
+        }
+    }
+
+    if( realout )
+        *realout = out; /* transfer of ownership */
+    else
+        bson_destroy( &out );
+
+    if( haserror )
+        return MONGO_ERROR;
+    else
+        return MONGO_OK;
+}
+
+int mongo_cmd_get_prev_error( mongo *conn, const char *db, bson *out ) {
+    return mongo_cmd_get_error_helper( conn, db, out, "getpreverror" );
+}
+
+int mongo_cmd_get_last_error( mongo *conn, const char *db, bson *out ) {
+    return mongo_cmd_get_error_helper( conn, db, out, "getlasterror" );
+}
+
+bson_bool_t mongo_cmd_ismaster( mongo *conn, bson *realout ) {
+    bson out = {NULL,0};
+    bson_bool_t ismaster = 0;
+
+    if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) {
+        bson_iterator it;
+        bson_find( &it, &out, "ismaster" );
+        ismaster = bson_iterator_bool( &it );
+    }
+
+    if( realout )
+        *realout = out; /* transfer of ownership */
+    else
+        bson_destroy( &out );
+
+    return ismaster;
+}
+
+static void digest2hex( mongo_md5_byte_t digest[16], char hex_digest[33] ) {
+    static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+    int i;
+    for ( i=0; i<16; i++ ) {
+        hex_digest[2*i]     = hex[( digest[i] & 0xf0 ) >> 4];
+        hex_digest[2*i + 1] = hex[ digest[i] & 0x0f      ];
+    }
+    hex_digest[32] = '\0';
+}
+
+static void mongo_pass_digest( const char *user, const char *pass, char hex_digest[33] ) {
+    mongo_md5_state_t st;
+    mongo_md5_byte_t digest[16];
+
+    mongo_md5_init( &st );
+    mongo_md5_append( &st, ( const mongo_md5_byte_t * )user, strlen( user ) );
+    mongo_md5_append( &st, ( const mongo_md5_byte_t * )":mongo:", 7 );
+    mongo_md5_append( &st, ( const mongo_md5_byte_t * )pass, strlen( pass ) );
+    mongo_md5_finish( &st, digest );
+    digest2hex( digest, hex_digest );
+}
+
+int mongo_cmd_add_user( mongo *conn, const char *db, const char *user, const char *pass ) {
+    bson user_obj;
+    bson pass_obj;
+    char hex_digest[33];
+    char *ns = bson_malloc( strlen( db ) + strlen( ".system.users" ) + 1 );
+    int res;
+
+    strcpy( ns, db );
+    strcpy( ns+strlen( db ), ".system.users" );
+
+    mongo_pass_digest( user, pass, hex_digest );
+
+    bson_init( &user_obj );
+    bson_append_string( &user_obj, "user", user );
+    bson_finish( &user_obj );
+
+    bson_init( &pass_obj );
+    bson_append_start_object( &pass_obj, "$set" );
+    bson_append_string( &pass_obj, "pwd", hex_digest );
+    bson_append_finish_object( &pass_obj );
+    bson_finish( &pass_obj );
+
+    res = mongo_update( conn, ns, &user_obj, &pass_obj, MONGO_UPDATE_UPSERT );
+
+    bson_free( ns );
+    bson_destroy( &user_obj );
+    bson_destroy( &pass_obj );
+
+    return res;
+}
+
+bson_bool_t mongo_cmd_authenticate( mongo *conn, const char *db, const char *user, const char *pass ) {
+    bson from_db;
+    bson cmd;
+    bson out;
+    const char *nonce;
+    bson_bool_t success = 0;
+
+    mongo_md5_state_t st;
+    mongo_md5_byte_t digest[16];
+    char hex_digest[33];
+
+    if( mongo_simple_int_command( conn, db, "getnonce", 1, &from_db ) == MONGO_OK ) {
+        bson_iterator it;
+        bson_find( &it, &from_db, "nonce" );
+        nonce = bson_iterator_string( &it );
+    } else {
+        return MONGO_ERROR;
+    }
+
+    mongo_pass_digest( user, pass, hex_digest );
+
+    mongo_md5_init( &st );
+    mongo_md5_append( &st, ( const mongo_md5_byte_t * )nonce, strlen( nonce ) );
+    mongo_md5_append( &st, ( const mongo_md5_byte_t * )user, strlen( user ) );
+    mongo_md5_append( &st, ( const mongo_md5_byte_t * )hex_digest, 32 );
+    mongo_md5_finish( &st, digest );
+    digest2hex( digest, hex_digest );
+
+    bson_init( &cmd );
+    bson_append_int( &cmd, "authenticate", 1 );
+    bson_append_string( &cmd, "user", user );
+    bson_append_string( &cmd, "nonce", nonce );
+    bson_append_string( &cmd, "key", hex_digest );
+    bson_finish( &cmd );
+
+    bson_destroy( &from_db );
+    /*bson_init( &from_db ); */
+    if( mongo_run_command( conn, db, &cmd, &out ) == MONGO_OK ) {
+        bson_iterator it;
+        if( bson_find( &it, &out, "ok" ) )
+            success = bson_iterator_bool( &it );
+    }
+
+    bson_destroy( &from_db );
+    bson_destroy( &cmd );
+
+    if( success )
+        return MONGO_OK;
+    else
+        return MONGO_ERROR;
+}
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h
new file mode 100644 (file)
index 0000000..6f86174
--- /dev/null
@@ -0,0 +1,648 @@
+/**
+ * @file mongo.h
+ * @brief Main MongoDB Declarations
+ */
+
+/*    Copyright 2009-2011 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef _MONGO_H_
+#define _MONGO_H_
+
+#include "bson.h"
+
+MONGO_EXTERN_C_START
+
+#define MONGO_MAJOR 0
+#define MONGO_MINOR 4
+#define MONGO_PATCH 0
+
+#define MONGO_OK 0
+#define MONGO_ERROR -1
+
+#define MONGO_DEFAULT_PORT 27017
+
+typedef enum mongo_error_t {
+    MONGO_CONN_SUCCESS = 0,  /**< Connection success! */
+    MONGO_CONN_NO_SOCKET,    /**< Could not create a socket. */
+    MONGO_CONN_FAIL,         /**< An error occured while calling connect(). */
+    MONGO_CONN_ADDR_FAIL,    /**< An error occured while calling getaddrinfo(). */
+    MONGO_CONN_NOT_MASTER,   /**< Warning: connected to a non-master node (read-only). */
+    MONGO_CONN_BAD_SET_NAME, /**< Given rs name doesn't match this replica set. */
+    MONGO_CONN_NO_PRIMARY,   /**< Can't find primary in replica set. Connection closed. */
+
+    MONGO_IO_ERROR,          /**< An error occurred while reading or writing on socket. */
+    MONGO_READ_SIZE_ERROR,   /**< The response is not the expected length. */
+    MONGO_COMMAND_FAILED,    /**< The command returned with 'ok' value of 0. */
+    MONGO_CURSOR_EXHAUSTED,  /**< The cursor has no more results. */
+    MONGO_CURSOR_INVALID,    /**< The cursor has timed out or is not recognized. */
+    MONGO_CURSOR_PENDING,    /**< Tailable cursor still alive but no data. */
+    MONGO_BSON_INVALID,      /**< BSON not valid for the specified op. */
+    MONGO_BSON_NOT_FINISHED  /**< BSON object has not been finished. */
+} mongo_error_t;
+
+enum mongo_cursor_flags {
+    MONGO_CURSOR_MUST_FREE = 1,      /**< mongo_cursor_destroy should free cursor. */
+    MONGO_CURSOR_QUERY_SENT = ( 1<<1 ) /**< Initial query has been sent. */
+};
+
+enum mongo_index_opts {
+    MONGO_INDEX_UNIQUE = ( 1<<0 ),
+    MONGO_INDEX_DROP_DUPS = ( 1<<2 ),
+    MONGO_INDEX_BACKGROUND = ( 1<<3 ),
+    MONGO_INDEX_SPARSE = ( 1<<4 )
+};
+
+enum mongo_update_opts {
+    MONGO_UPDATE_UPSERT = 0x1,
+    MONGO_UPDATE_MULTI = 0x2,
+    MONGO_UPDATE_BASIC = 0x4
+};
+
+enum mongo_cursor_opts {
+    MONGO_TAILABLE = ( 1<<1 ),        /**< Create a tailable cursor. */
+    MONGO_SLAVE_OK = ( 1<<2 ),        /**< Allow queries on a non-primary node. */
+    MONGO_NO_CURSOR_TIMEOUT = ( 1<<4 ), /**< Disable cursor timeouts. */
+    MONGO_AWAIT_DATA = ( 1<<5 ),      /**< Momentarily block for more data. */
+    MONGO_EXHAUST = ( 1<<6 ),         /**< Stream in multiple 'more' packages. */
+    MONGO_PARTIAL = ( 1<<7 )          /**< Allow reads even if a shard is down. */
+};
+
+enum mongo_operations {
+    MONGO_OP_MSG = 1000,
+    MONGO_OP_UPDATE = 2001,
+    MONGO_OP_INSERT = 2002,
+    MONGO_OP_QUERY = 2004,
+    MONGO_OP_GET_MORE = 2005,
+    MONGO_OP_DELETE = 2006,
+    MONGO_OP_KILL_CURSORS = 2007
+};
+
+#pragma pack(1)
+typedef struct {
+    int len;
+    int id;
+    int responseTo;
+    int op;
+} mongo_header;
+
+typedef struct {
+    mongo_header head;
+    char data;
+} mongo_message;
+
+typedef struct {
+    int flag; /* FIX THIS COMMENT non-zero on failure */
+    int64_t cursorID;
+    int start;
+    int num;
+} mongo_reply_fields;
+
+typedef struct {
+    mongo_header head;
+    mongo_reply_fields fields;
+    char objs;
+} mongo_reply;
+#pragma pack()
+
+typedef struct mongo_host_port {
+    char host[255];
+    int port;
+    struct mongo_host_port *next;
+} mongo_host_port;
+
+typedef struct {
+    mongo_host_port *seeds;        /**< List of seeds provided by the user. */
+    mongo_host_port *hosts;        /**< List of host/ports given by the replica set */
+    char *name;                    /**< Name of the replica set. */
+    bson_bool_t primary_connected; /**< Primary node connection status. */
+} mongo_replset;
+
+typedef struct mongo {
+    mongo_host_port *primary;  /**< Primary connection info. */
+    mongo_replset *replset;    /**< replset object if connected to a replica set. */
+    int sock;                  /**< Socket file descriptor. */
+    int flags;                 /**< Flags on this connection object. */
+    int conn_timeout_ms;       /**< Connection timeout in milliseconds. */
+    int op_timeout_ms;         /**< Read and write timeout in milliseconds. */
+    bson_bool_t connected;     /**< Connection status. */
+
+    mongo_error_t err;         /**< Most recent driver error code. */
+    char *errstr;              /**< String version of most recent driver error code. */
+    int lasterrcode;           /**< getlasterror given by the server on calls. */
+    char *lasterrstr;          /**< getlasterror string generated by server. */
+} mongo;
+
+typedef struct {
+    mongo_reply *reply;  /**< reply is owned by cursor */
+    mongo *conn;       /**< connection is *not* owned by cursor */
+    const char *ns;    /**< owned by cursor */
+    int flags;         /**< Flags used internally by this drivers. */
+    int seen;          /**< Number returned so far. */
+    bson current;      /**< This cursor's current bson object. */
+    mongo_error_t err; /**< Errors on this cursor. */
+    bson *query;       /**< Bitfield containing cursor options. */
+    bson *fields;      /**< Bitfield containing cursor options. */
+    int options;       /**< Bitfield containing cursor options. */
+    int limit;         /**< Bitfield containing cursor options. */
+    int skip;          /**< Bitfield containing cursor options. */
+} mongo_cursor;
+
+/* Connection API */
+
+/** Initialize a new mongo connection object. If not created
+ *  with mongo_new, you must initialize each mongo
+ *  object using this function.
+ *
+ *  @note When finished, you must pass this object to
+ *      mongo_destroy( ).
+ *
+ *  @param conn a mongo connection object allocated on the stack
+ *      or heap.
+ */
+void mongo_init( mongo *conn );
+
+/**
+ * Connect to a single MongoDB server.
+ *
+ * @param conn a mongo object.
+ * @param host a numerical network address or a network hostname.
+ * @param port the port to connect to.
+ *
+ * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type
+ *   mongo_conn_return_t will be set on the conn->err field.
+ */
+int mongo_connect( mongo *conn , const char *host, int port );
+
+/**
+ * Set up this connection object for connecting to a replica set.
+ * To connect, pass the object to mongo_replset_connect().
+ *
+ * @param conn a mongo object.
+ * @param name the name of the replica set to connect to.
+ * */
+void mongo_replset_init( mongo *conn, const char *name );
+
+/**
+ * Add a seed node to the replica set connection object.
+ *
+ * You must specify at least one seed node before connecting to a replica set.
+ *
+ * @param conn a mongo object.
+ * @param host a numerical network address or a network hostname.
+ * @param port the port to connect to.
+ */
+void mongo_replset_add_seed( mongo *conn, const char *host, int port );
+
+/**
+ * Utility function for converting a host-port string to a mongo_host_port.
+ *
+ * @param host_string a string containing either a host or a host and port separated
+ *     by a colon.
+ * @param host_port the mongo_host_port object to write the result to.
+ */
+void mongo_parse_host( const char *host_string, mongo_host_port *host_port );
+
+/**
+ * Connect to a replica set.
+ *
+ * Before passing a connection object to this function, you must already have called
+ * mongo_set_replset and mongo_replset_add_seed.
+ *
+ * @param conn a mongo object.
+ *
+ * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type
+ *   mongo_conn_return_t will be set on the conn->err field.
+ */
+int mongo_replset_connect( mongo *conn );
+
+/** Set a timeout for operations on this connection. This
+ *  is a platform-specific feature, and only work on *nix
+ *  system. You must also compile for linux to support this.
+ *
+ *  @param conn a mongo object.
+ *  @param millis timeout time in milliseconds.
+ *
+ *  @return MONGO_OK. On error, return MONGO_ERROR and
+ *    set the conn->err field.
+ */
+int mongo_set_op_timeout( mongo *conn, int millis );
+
+/**
+ * Ensure that this connection is healthy by performing
+ * a round-trip to the server.
+ *
+ * @param conn a mongo connection
+ *
+ * @return MONGO_OK if connected; otherwise, MONGO_ERROR.
+ */
+int mongo_check_connection( mongo *conn );
+
+/**
+ * Try reconnecting to the server using the existing connection settings.
+ *
+ * This function will disconnect the current socket. If you've authenticated,
+ * you'll need to re-authenticate after calling this function.
+ *
+ * @param conn a mongo object.
+ *
+ * @return MONGO_OK or MONGO_ERROR and
+ *   set the conn->err field.
+ */
+int mongo_reconnect( mongo *conn );
+
+/**
+ * Close the current connection to the server. After calling
+ * this function, you may call mongo_reconnect with the same
+ * connection object.
+ *
+ * @param conn a mongo object.
+ */
+void mongo_disconnect( mongo *conn );
+
+/**
+ * Close any existing connection to the server and free all allocated
+ * memory associated with the conn object.
+ *
+ * You must always call this function when finished with the connection object.
+ *
+ * @param conn a mongo object.
+ */
+void mongo_destroy( mongo *conn );
+
+/**
+ * Insert a BSON document into a MongoDB server. This function
+ * will fail if the supplied BSON struct is not UTF-8 or if
+ * the keys are invalid for insert (contain '.' or start with '$').
+ *
+ * @param conn a mongo object.
+ * @param ns the namespace.
+ * @param data the bson data.
+ *
+ * @return MONGO_OK or MONGO_ERROR. If the conn->err
+ *     field is MONGO_BSON_INVALID, check the err field
+ *     on the bson struct for the reason.
+ */
+int mongo_insert( mongo *conn, const char *ns, bson *data );
+
+/**
+ * Insert a batch of BSON documents into a MongoDB server. This function
+ * will fail if any of the documents to be inserted is invalid.
+ *
+ * @param conn a mongo object.
+ * @param ns the namespace.
+ * @param data the bson data.
+ * @param num the number of documents in data.
+ *
+ * @return MONGO_OK or MONGO_ERROR.
+ *
+ */
+int mongo_insert_batch( mongo *conn , const char *ns ,
+                        bson **data , int num );
+
+/**
+ * Update a document in a MongoDB server.
+ *
+ * @param conn a mongo object.
+ * @param ns the namespace.
+ * @param cond the bson update query.
+ * @param op the bson update data.
+ * @param flags flags for the update.
+ *
+ * @return MONGO_OK or MONGO_ERROR with error stored in conn object.
+ *
+ */
+int mongo_update( mongo *conn, const char *ns, const bson *cond,
+                  const bson *op, int flags );
+
+/**
+ * Remove a document from a MongoDB server.
+ *
+ * @param conn a mongo object.
+ * @param ns the namespace.
+ * @param cond the bson query.
+ *
+ * @return MONGO_OK or MONGO_ERROR with error stored in conn object.
+ */
+int mongo_remove( mongo *conn, const char *ns, const bson *cond );
+
+/**
+ * Find documents in a MongoDB server.
+ *
+ * @param conn a mongo object.
+ * @param ns the namespace.
+ * @param query the bson query.
+ * @param fields a bson document of fields to be returned.
+ * @param limit the maximum number of documents to retrun.
+ * @param skip the number of documents to skip.
+ * @param options A bitfield containing cursor options.
+ *
+ * @return A cursor object allocated on the heap or NULL if
+ *     an error has occurred. For finer-grained error checking,
+ *     use the cursor builder API instead.
+ */
+mongo_cursor *mongo_find( mongo *conn, const char *ns, bson *query,
+                          bson *fields, int limit, int skip, int options );
+
+/**
+ * Initalize a new cursor object.
+ *
+ * @param cursor
+ * @param ns the namespace, represented as the the database
+ *     name and collection name separated by a dot. e.g., "test.users"
+ */
+void mongo_cursor_init( mongo_cursor *cursor, mongo *conn, const char *ns );
+
+/**
+ * Set the bson object specifying this cursor's query spec. If
+ * your query is the empty bson object "{}", then you need not
+ * set this value.
+ *
+ * @param cursor
+ * @param query a bson object representing the query spec. This may
+ *   be either a simple query spec or a complex spec storing values for
+ *   $query, $orderby, $hint, and/or $explain. See
+ *   http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol for details.
+ */
+void mongo_cursor_set_query( mongo_cursor *cursor, bson *query );
+
+/**
+ * Set the fields to return for this cursor. If you want to return
+ * all fields, you need not set this value.
+ *
+ * @param cursor
+ * @param fields a bson object representing the fields to return.
+ *   See http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields.
+ */
+void mongo_cursor_set_fields( mongo_cursor *cursor, bson *fields );
+
+/**
+ * Set the number of documents to skip.
+ *
+ * @param cursor
+ * @param skip
+ */
+void mongo_cursor_set_skip( mongo_cursor *cursor, int skip );
+
+/**
+ * Set the number of documents to return.
+ *
+ * @param cursor
+ * @param limit
+ */
+void mongo_cursor_set_limit( mongo_cursor *cursor, int limit );
+
+/**
+ * Set any of the available query options (e.g., MONGO_TAILABLE).
+ *
+ * @param cursor
+ * @param options a bitfield storing query options. See
+ *   mongo_cursor_bitfield_t for available constants.
+ */
+void mongo_cursor_set_options( mongo_cursor *cursor, int options );
+
+/**
+ * Return the current BSON object data as a const char*. This is useful
+ * for creating bson iterators with bson_iterator_init.
+ *
+ * @param cursor
+ */
+const char *mongo_cursor_data( mongo_cursor *cursor );
+
+/**
+ * Return the current BSON object data as a const char*. This is useful
+ * for creating bson iterators with bson_iterator_init.
+ *
+ * @param cursor
+ */
+const bson *mongo_cursor_bson( mongo_cursor *cursor );
+
+/**
+ * Iterate the cursor, returning the next item. When successful,
+ *   the returned object will be stored in cursor->current;
+ *
+ * @param cursor
+ *
+ * @return MONGO_OK. On error, returns MONGO_ERROR and sets
+ *   cursor->err with a value of mongo_error_t.
+ */
+int mongo_cursor_next( mongo_cursor *cursor );
+
+/**
+ * Destroy a cursor object. When finished with a cursor, you
+ * must pass it to this function.
+ *
+ * @param cursor the cursor to destroy.
+ *
+ * @return MONGO_OK or an error code. On error, check cursor->conn->err
+ *     for errors.
+ */
+int mongo_cursor_destroy( mongo_cursor *cursor );
+
+/**
+ * Find a single document in a MongoDB server.
+ *
+ * @param conn a mongo object.
+ * @param ns the namespace.
+ * @param query the bson query.
+ * @param fields a bson document of the fields to be returned.
+ * @param out a bson document in which to put the query result.
+ *
+ */
+/* out can be NULL if you don't care about results. useful for commands */
+bson_bool_t mongo_find_one( mongo *conn, const char *ns, bson *query,
+                            bson *fields, bson *out );
+
+/* MongoDB Helper Functions */
+
+/**
+ * Count the number of documents in a collection matching a query.
+ *
+ * @param conn a mongo object.
+ * @param db the db name.
+ * @param coll the collection name.
+ * @param query the BSON query.
+ *
+ * @return the number of matching documents. If the command fails,
+ *     MONGO_ERROR is returned.
+ */
+int64_t mongo_count( mongo *conn, const char *db, const char *coll,
+                     bson *query );
+
+/**
+ * Create a compouned index.
+ *
+ * @param conn a mongo object.
+ * @param ns the namespace.
+ * @param data the bson index data.
+ * @param options a bitfield for setting index options. Possibilities include
+ *   MONGO_INDEX_UNIQUE, MONGO_INDEX_DROP_DUPS, MONGO_INDEX_BACKGROUND,
+ *   and MONGO_INDEX_SPARSE.
+ * @param out a bson document containing errors, if any.
+ *
+ * @return MONGO_OK if index is created successfully; otherwise, MONGO_ERROR.
+ */
+int mongo_create_index( mongo *conn, const char *ns, bson *key, int options, bson *out );
+
+/**
+ * Create an index with a single key.
+ *
+ * @param conn a mongo object.
+ * @param ns the namespace.
+ * @param field the index key.
+ * @param options index options.
+ * @param out a BSON document containing errors, if any.
+ *
+ * @return true if the index was created.
+ */
+bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char *field, int options, bson *out );
+
+/* ----------------------------
+   COMMANDS
+   ------------------------------ */
+
+/**
+ * Run a command on a MongoDB server.
+ *
+ * @param conn a mongo object.
+ * @param db the name of the database.
+ * @param command the BSON command to run.
+ * @param out the BSON result of the command.
+ *
+ * @return true if the command ran without error.
+ */
+bson_bool_t mongo_run_command( mongo *conn, const char *db, bson *command, bson *out );
+
+/**
+ * Run a command that accepts a simple string key and integer value.
+ *
+ * @param conn a mongo object.
+ * @param db the name of the database.
+ * @param cmd the command to run.
+ * @param arg the integer argument to the command.
+ * @param out the BSON result of the command.
+ *
+ * @return MONGO_OK or an error code.
+ *
+ */
+int mongo_simple_int_command( mongo *conn, const char *db,
+                              const char *cmd, int arg, bson *out );
+
+/**
+ * Run a command that accepts a simple string key and value.
+ *
+ * @param conn a mongo object.
+ * @param db the name of the database.
+ * @param cmd the command to run.
+ * @param arg the string argument to the command.
+ * @param out the BSON result of the command.
+ *
+ * @return true if the command ran without error.
+ *
+ */
+bson_bool_t mongo_simple_str_command( mongo *conn, const char *db, const char *cmd, const char *arg, bson *out );
+
+/**
+ * Drop a database.
+ *
+ * @param conn a mongo object.
+ * @param db the name of the database to drop.
+ *
+ * @return MONGO_OK or an error code.
+ */
+int mongo_cmd_drop_db( mongo *conn, const char *db );
+
+/**
+ * Drop a collection.
+ *
+ * @param conn a mongo object.
+ * @param db the name of the database.
+ * @param collection the name of the collection to drop.
+ * @param out a BSON document containing the result of the command.
+ *
+ * @return true if the collection drop was successful.
+ */
+bson_bool_t mongo_cmd_drop_collection( mongo *conn, const char *db, const char *collection, bson *out );
+
+/**
+ * Add a database user.
+ *
+ * @param conn a mongo object.
+ * @param db the database in which to add the user.
+ * @param user the user name
+ * @param pass the user password
+ *
+ * @return MONGO_OK or MONGO_ERROR.
+  */
+int mongo_cmd_add_user( mongo *conn, const char *db, const char *user, const char *pass );
+
+/**
+ * Authenticate a user.
+ *
+ * @param conn a mongo object.
+ * @param db the database to authenticate against.
+ * @param user the user name to authenticate.
+ * @param pass the user's password.
+ *
+ * @return MONGO_OK on sucess and MONGO_ERROR on failure.
+ */
+int mongo_cmd_authenticate( mongo *conn, const char *db, const char *user, const char *pass );
+
+/**
+ * Check if the current server is a master.
+ *
+ * @param conn a mongo object.
+ * @param out a BSON result of the command.
+ *
+ * @return true if the server is a master.
+ */
+/* return value is master status */
+bson_bool_t mongo_cmd_ismaster( mongo *conn, bson *out );
+
+/**
+ * Get the error for the last command with the current connection.
+ *
+ * @param conn a mongo object.
+ * @param db the name of the database.
+ * @param out a BSON object containing the error details.
+ *
+ * @return MONGO_OK if no error and MONGO_ERROR on error. On error, check the values
+ *     of conn->lasterrcode and conn->lasterrstr for the error status.
+ */
+int mongo_cmd_get_last_error( mongo *conn, const char *db, bson *out );
+
+/**
+ * Get the most recent error with the current connection.
+ *
+ * @param conn a mongo object.
+ * @param db the name of the database.
+ * @param out a BSON object containing the error details.
+ *
+ * @return MONGO_OK if no error and MONGO_ERROR on error. On error, check the values
+ *     of conn->lasterrcode and conn->lasterrstr for the error status.
+ */
+int mongo_cmd_get_prev_error( mongo *conn, const char *db, bson *out );
+
+/**
+ * Reset the error state for the connection.
+ *
+ * @param conn a mongo object.
+ * @param db the name of the database.
+ */
+void mongo_cmd_reset_error( mongo *conn, const char *db );
+
+MONGO_EXTERN_C_END
+
+#endif
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.c
new file mode 100644 (file)
index 0000000..5c8ad9b
--- /dev/null
@@ -0,0 +1,98 @@
+/* net.c */
+
+/*    Copyright 2009-2011 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/* Implementation for generic version of net.h */
+#include "net.h"
+#include <string.h>
+
+int mongo_write_socket( mongo *conn, const void *buf, int len ) {
+    const char *cbuf = buf;
+    while ( len ) {
+        int sent = send( conn->sock, cbuf, len, 0 );
+        if ( sent == -1 ) {
+            conn->err = MONGO_IO_ERROR;
+            return MONGO_ERROR;
+        }
+        cbuf += sent;
+        len -= sent;
+    }
+
+    return MONGO_OK;
+}
+
+int mongo_read_socket( mongo *conn, void *buf, int len ) {
+    char *cbuf = buf;
+    while ( len ) {
+        int sent = recv( conn->sock, cbuf, len, 0 );
+        if ( sent == 0 || sent == -1 ) {
+            conn->err = MONGO_IO_ERROR;
+            return MONGO_ERROR;
+        }
+        cbuf += sent;
+        len -= sent;
+    }
+
+    return MONGO_OK;
+}
+
+/* This is a no-op in the generic implementation. */
+int mongo_set_socket_op_timeout( mongo *conn, int millis ) {
+    return MONGO_OK;
+}
+
+static int mongo_create_socket( mongo *conn ) {
+    int fd;
+
+    if( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) {
+        conn->err = MONGO_CONN_NO_SOCKET;
+        return MONGO_ERROR;
+    }
+    conn->sock = fd;
+
+    return MONGO_OK;
+}
+
+int mongo_socket_connect( mongo *conn, const char *host, int port ) {
+    struct sockaddr_in sa;
+    socklen_t addressSize;
+    int flag = 1;
+
+    if( mongo_create_socket( conn ) != MONGO_OK )
+        return MONGO_ERROR;
+
+    memset( sa.sin_zero , 0 , sizeof( sa.sin_zero ) );
+    sa.sin_family = AF_INET;
+    sa.sin_port = htons( port );
+    sa.sin_addr.s_addr = inet_addr( host );
+    addressSize = sizeof( sa );
+
+    if ( connect( conn->sock, ( struct sockaddr * )&sa, addressSize ) == -1 ) {
+        mongo_close_socket( conn->sock );
+        conn->connected = 0;
+        conn->sock = 0;
+        conn->err = MONGO_CONN_FAIL;
+        return MONGO_ERROR;
+    }
+
+    setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * ) &flag, sizeof( flag ) );
+    if( conn->op_timeout_ms > 0 )
+        mongo_set_socket_op_timeout( conn, conn->op_timeout_ms );
+
+    conn->connected = 1;
+
+    return MONGO_OK;
+}
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.h
new file mode 100644 (file)
index 0000000..4919087
--- /dev/null
@@ -0,0 +1,57 @@
+/** @file net.h */
+
+/*    Copyright 2009-2011 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/* Header for generic net.h */
+#ifndef _MONGO_NET_H_
+#define _MONGO_NET_H_
+
+#include "mongo.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <winsock.h>
+#define mongo_close_socket(sock) ( closesocket(sock) )
+typedef int socklen_t;
+#else
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <fcntl.h>
+#define mongo_close_socket(sock) ( close(sock) )
+#endif
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#if defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || _POSIX_C_SOURCE >= 1
+#define _MONGO_USE_GETADDRINFO
+#endif
+
+MONGO_EXTERN_C_START
+
+/* This is a no-op in the generic implementation. */
+int mongo_set_socket_op_timeout( mongo *conn, int millis );
+int mongo_read_socket( mongo *conn, void *buf, int len );
+int mongo_write_socket( mongo *conn, const void *buf, int len );
+int mongo_socket_connect( mongo *conn, const char *host, int port );
+
+MONGO_EXTERN_C_END
+#endif
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/numbers.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/numbers.c
new file mode 100644 (file)
index 0000000..a63e3d7
--- /dev/null
@@ -0,0 +1,127 @@
+/*    Copyright 2009-2011 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/* all the numbers that fit in a 4 byte string */
+const char bson_numstrs[1000][4] = {
+    "0",  "1",  "2",  "3",  "4",  "5",  "6",  "7",  "8",  "9",
+    "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
+    "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
+    "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
+    "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
+    "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
+    "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
+    "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
+    "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
+    "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
+
+    "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
+    "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
+    "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
+    "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
+    "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
+    "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
+    "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
+    "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
+    "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
+    "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
+
+    "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
+    "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
+    "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
+    "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
+    "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
+    "250", "251", "252", "253", "254", "255", "256", "257", "258", "259",
+    "260", "261", "262", "263", "264", "265", "266", "267", "268", "269",
+    "270", "271", "272", "273", "274", "275", "276", "277", "278", "279",
+    "280", "281", "282", "283", "284", "285", "286", "287", "288", "289",
+    "290", "291", "292", "293", "294", "295", "296", "297", "298", "299",
+
+    "300", "301", "302", "303", "304", "305", "306", "307", "308", "309",
+    "310", "311", "312", "313", "314", "315", "316", "317", "318", "319",
+    "320", "321", "322", "323", "324", "325", "326", "327", "328", "329",
+    "330", "331", "332", "333", "334", "335", "336", "337", "338", "339",
+    "340", "341", "342", "343", "344", "345", "346", "347", "348", "349",
+    "350", "351", "352", "353", "354", "355", "356", "357", "358", "359",
+    "360", "361", "362", "363", "364", "365", "366", "367", "368", "369",
+    "370", "371", "372", "373", "374", "375", "376", "377", "378", "379",
+    "380", "381", "382", "383", "384", "385", "386", "387", "388", "389",
+    "390", "391", "392", "393", "394", "395", "396", "397", "398", "399",
+
+    "400", "401", "402", "403", "404", "405", "406", "407", "408", "409",
+    "410", "411", "412", "413", "414", "415", "416", "417", "418", "419",
+    "420", "421", "422", "423", "424", "425", "426", "427", "428", "429",
+    "430", "431", "432", "433", "434", "435", "436", "437", "438", "439",
+    "440", "441", "442", "443", "444", "445", "446", "447", "448", "449",
+    "450", "451", "452", "453", "454", "455", "456", "457", "458", "459",
+    "460", "461", "462", "463", "464", "465", "466", "467", "468", "469",
+    "470", "471", "472", "473", "474", "475", "476", "477", "478", "479",
+    "480", "481", "482", "483", "484", "485", "486", "487", "488", "489",
+    "490", "491", "492", "493", "494", "495", "496", "497", "498", "499",
+
+    "500", "501", "502", "503", "504", "505", "506", "507", "508", "509",
+    "510", "511", "512", "513", "514", "515", "516", "517", "518", "519",
+    "520", "521", "522", "523", "524", "525", "526", "527", "528", "529",
+    "530", "531", "532", "533", "534", "535", "536", "537", "538", "539",
+    "540", "541", "542", "543", "544", "545", "546", "547", "548", "549",
+    "550", "551", "552", "553", "554", "555", "556", "557", "558", "559",
+    "560", "561", "562", "563", "564", "565", "566", "567", "568", "569",
+    "570", "571", "572", "573", "574", "575", "576", "577", "578", "579",
+    "580", "581", "582", "583", "584", "585", "586", "587", "588", "589",
+    "590", "591", "592", "593", "594", "595", "596", "597", "598", "599",
+
+    "600", "601", "602", "603", "604", "605", "606", "607", "608", "609",
+    "610", "611", "612", "613", "614", "615", "616", "617", "618", "619",
+    "620", "621", "622", "623", "624", "625", "626", "627", "628", "629",
+    "630", "631", "632", "633", "634", "635", "636", "637", "638", "639",
+    "640", "641", "642", "643", "644", "645", "646", "647", "648", "649",
+    "650", "651", "652", "653", "654", "655", "656", "657", "658", "659",
+    "660", "661", "662", "663", "664", "665", "666", "667", "668", "669",
+    "670", "671", "672", "673", "674", "675", "676", "677", "678", "679",
+    "680", "681", "682", "683", "684", "685", "686", "687", "688", "689",
+    "690", "691", "692", "693", "694", "695", "696", "697", "698", "699",
+
+    "700", "701", "702", "703", "704", "705", "706", "707", "708", "709",
+    "710", "711", "712", "713", "714", "715", "716", "717", "718", "719",
+    "720", "721", "722", "723", "724", "725", "726", "727", "728", "729",
+    "730", "731", "732", "733", "734", "735", "736", "737", "738", "739",
+    "740", "741", "742", "743", "744", "745", "746", "747", "748", "749",
+    "750", "751", "752", "753", "754", "755", "756", "757", "758", "759",
+    "760", "761", "762", "763", "764", "765", "766", "767", "768", "769",
+    "770", "771", "772", "773", "774", "775", "776", "777", "778", "779",
+    "780", "781", "782", "783", "784", "785", "786", "787", "788", "789",
+    "790", "791", "792", "793", "794", "795", "796", "797", "798", "799",
+
+    "800", "801", "802", "803", "804", "805", "806", "807", "808", "809",
+    "810", "811", "812", "813", "814", "815", "816", "817", "818", "819",
+    "820", "821", "822", "823", "824", "825", "826", "827", "828", "829",
+    "830", "831", "832", "833", "834", "835", "836", "837", "838", "839",
+    "840", "841", "842", "843", "844", "845", "846", "847", "848", "849",
+    "850", "851", "852", "853", "854", "855", "856", "857", "858", "859",
+    "860", "861", "862", "863", "864", "865", "866", "867", "868", "869",
+    "870", "871", "872", "873", "874", "875", "876", "877", "878", "879",
+    "880", "881", "882", "883", "884", "885", "886", "887", "888", "889",
+    "890", "891", "892", "893", "894", "895", "896", "897", "898", "899",
+
+    "900", "901", "902", "903", "904", "905", "906", "907", "908", "909",
+    "910", "911", "912", "913", "914", "915", "916", "917", "918", "919",
+    "920", "921", "922", "923", "924", "925", "926", "927", "928", "929",
+    "930", "931", "932", "933", "934", "935", "936", "937", "938", "939",
+    "940", "941", "942", "943", "944", "945", "946", "947", "948", "949",
+    "950", "951", "952", "953", "954", "955", "956", "957", "958", "959",
+    "960", "961", "962", "963", "964", "965", "966", "967", "968", "969",
+    "970", "971", "972", "973", "974", "975", "976", "977", "978", "979",
+    "980", "981", "982", "983", "984", "985", "986", "987", "988", "989",
+    "990", "991", "992", "993", "994", "995", "996", "997", "998", "999",
+};
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform.h
new file mode 100644 (file)
index 0000000..4a96af7
--- /dev/null
@@ -0,0 +1,94 @@
+/** @file platform.h */
+
+/**    Copyright 2009-2011 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+
+/* all platform-specific ifdefs should go here */
+
+#ifndef _PLATFORM_HACKS_H_
+#define _PLATFORM_HACKS_H_
+
+#ifdef __GNUC__
+#define MONGO_INLINE static __inline__
+#else
+#define MONGO_INLINE static
+#endif
+
+#ifdef __cplusplus
+#define MONGO_EXTERN_C_START extern "C" {
+#define MONGO_EXTERN_C_END }
+#else
+#define MONGO_EXTERN_C_START
+#define MONGO_EXTERN_C_END
+#endif
+
+
+#if defined(MONGO_HAVE_STDINT) || __STDC_VERSION__ >= 199901L
+#include <stdint.h>
+#elif defined(MONGO_HAVE_UNISTD)
+#include <unistd.h>
+#elif defined(MONGO_USE__INT64)
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#elif defined(MONGO_USE_LONG_LONG_INT)
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#else
+#error must have a 64bit int type
+#endif
+
+/* big endian is only used for OID generation. little is used everywhere else */
+#ifdef MONGO_BIG_ENDIAN
+#define bson_little_endian64(out, in) ( bson_swap_endian64(out, in) )
+#define bson_little_endian32(out, in) ( bson_swap_endian32(out, in) )
+#define bson_big_endian64(out, in) ( memcpy(out, in, 8) )
+#define bson_big_endian32(out, in) ( memcpy(out, in, 4) )
+#else
+#define bson_little_endian64(out, in) ( memcpy(out, in, 8) )
+#define bson_little_endian32(out, in) ( memcpy(out, in, 4) )
+#define bson_big_endian64(out, in) ( bson_swap_endian64(out, in) )
+#define bson_big_endian32(out, in) ( bson_swap_endian32(out, in) )
+#endif
+
+MONGO_EXTERN_C_START
+
+MONGO_INLINE void bson_swap_endian64( void *outp, const void *inp ) {
+    const char *in = ( const char * )inp;
+    char *out = ( char * )outp;
+
+    out[0] = in[7];
+    out[1] = in[6];
+    out[2] = in[5];
+    out[3] = in[4];
+    out[4] = in[3];
+    out[5] = in[2];
+    out[6] = in[1];
+    out[7] = in[0];
+
+}
+MONGO_INLINE void bson_swap_endian32( void *outp, const void *inp ) {
+    const char *in = ( const char * )inp;
+    char *out = ( char * )outp;
+
+    out[0] = in[3];
+    out[1] = in[2];
+    out[2] = in[1];
+    out[3] = in[0];
+}
+
+MONGO_EXTERN_C_END
+
+#endif
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.c
new file mode 100644 (file)
index 0000000..b2f7c22
--- /dev/null
@@ -0,0 +1,183 @@
+/* net.c */
+
+/*    Copyright 2009-2011 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/* Implementation for Linux version of net.h */
+#include "net.h"
+#include <string.h>
+
+int mongo_write_socket( mongo *conn, const void *buf, int len ) {
+    const char *cbuf = buf;
+    while ( len ) {
+        int sent = send( conn->sock, cbuf, len, 0 );
+        if ( sent == -1 ) {
+            conn->err = MONGO_IO_ERROR;
+            return MONGO_ERROR;
+        }
+        cbuf += sent;
+        len -= sent;
+    }
+
+    return MONGO_OK;
+}
+
+int mongo_read_socket( mongo *conn, void *buf, int len ) {
+    char *cbuf = buf;
+    while ( len ) {
+        int sent = recv( conn->sock, cbuf, len, 0 );
+        if ( sent == 0 || sent == -1 ) {
+            conn->err = MONGO_IO_ERROR;
+            return MONGO_ERROR;
+        }
+        cbuf += sent;
+        len -= sent;
+    }
+
+    return MONGO_OK;
+}
+
+static int mongo_create_socket( mongo *conn ) {
+    int fd;
+
+    if( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) {
+        conn->err = MONGO_CONN_NO_SOCKET;
+        return MONGO_ERROR;
+    }
+    conn->sock = fd;
+
+    return MONGO_OK;
+}
+
+static int mongo_set_blocking_status( mongo *conn ) {
+    int flags;
+    int blocking;
+
+    blocking = ( conn->conn_timeout_ms == 0 );
+    if( blocking )
+        return MONGO_OK;
+    else {
+        if( ( flags = fcntl( conn->sock, F_GETFL ) ) == -1 ) {
+            conn->err = MONGO_IO_ERROR;
+            mongo_close_socket( conn->sock );
+            return MONGO_ERROR;
+        }
+
+        flags |= O_NONBLOCK;
+
+        if( ( flags = fcntl( conn->sock, F_SETFL, flags ) ) == -1 ) {
+            conn->err = MONGO_IO_ERROR;
+            mongo_close_socket( conn->sock );
+            return MONGO_ERROR;
+        }
+    }
+
+    return MONGO_OK;
+}
+
+int mongo_set_socket_op_timeout( mongo *conn, int millis ) {
+    struct timeval tv;
+    tv.tv_sec = millis / 1000;
+    tv.tv_usec = ( millis % 1000 ) * 1000;
+
+    if ( setsockopt( conn->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof( tv ) ) == -1 ) {
+        conn->err = MONGO_IO_ERROR;
+        return MONGO_ERROR;
+    }
+
+    if ( setsockopt( conn->sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof( tv ) ) == -1 ) {
+        conn->err = MONGO_IO_ERROR;
+        return MONGO_ERROR;
+    }
+
+    return MONGO_OK;
+}
+
+#ifdef _MONGO_USE_GETADDRINFO
+int mongo_socket_connect( mongo *conn, const char *host, int port ) {
+
+    struct addrinfo *addrs = NULL;
+    struct addrinfo hints;
+    int flag = 1;
+    char port_str[12];
+    int ret;
+
+    conn->sock = 0;
+    conn->connected = 0;
+
+    memset( &hints, 0, sizeof( hints ) );
+    hints.ai_family = AF_INET;
+    hints.ai_socktype = SOCK_STREAM;
+
+    sprintf( port_str, "%d", port );
+
+    if( mongo_create_socket( conn ) != MONGO_OK )
+        return MONGO_ERROR;
+
+    if( getaddrinfo( host, port_str, &hints, &addrs ) != 0 ) {
+        bson_errprintf( "getaddrinfo failed: %s", gai_strerror( ret ) );
+        conn->err = MONGO_CONN_ADDR_FAIL;
+        return MONGO_ERROR;
+    }
+
+    if ( connect( conn->sock, addrs->ai_addr, addrs->ai_addrlen ) == -1 ) {
+        mongo_close_socket( conn->sock );
+        freeaddrinfo( addrs );
+        conn->err = MONGO_CONN_FAIL;
+        return MONGO_ERROR;
+    }
+
+    setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * )&flag, sizeof( flag ) );
+    if( conn->op_timeout_ms > 0 )
+        mongo_set_socket_op_timeout( conn, conn->op_timeout_ms );
+
+    conn->connected = 1;
+    freeaddrinfo( addrs );
+
+    return MONGO_OK;
+}
+#else
+int mongo_socket_connect( mongo *conn, const char *host, int port ) {
+    struct sockaddr_in sa;
+    socklen_t addressSize;
+    int flag = 1;
+
+    if( mongo_create_socket( conn ) != MONGO_OK )
+        return MONGO_ERROR;
+
+    memset( sa.sin_zero , 0 , sizeof( sa.sin_zero ) );
+    sa.sin_family = AF_INET;
+    sa.sin_port = htons( port );
+    sa.sin_addr.s_addr = inet_addr( host );
+    addressSize = sizeof( sa );
+
+    if ( connect( conn->sock, ( struct sockaddr * )&sa, addressSize ) == -1 ) {
+        mongo_close_socket( conn->sock );
+        conn->connected = 0;
+        conn->sock = 0;
+        conn->err = MONGO_CONN_FAIL;
+        return MONGO_ERROR;
+    }
+
+    setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * ) &flag, sizeof( flag ) );
+
+    if( conn->op_timeout_ms > 0 )
+        mongo_set_socket_op_timeout( conn, conn->op_timeout_ms );
+
+    conn->connected = 1;
+
+    return MONGO_OK;
+}
+#endif
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.h
new file mode 100644 (file)
index 0000000..054247c
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * @file net.h
+ * @brief Networking.
+ */
+
+/*    Copyright 2009-2011 10gen Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/* Header for Linux net.h */
+#ifndef _MONGO_NET_H_
+#define _MONGO_NET_H_
+
+#include "mongo.h"
+
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#define mongo_close_socket(sock) ( close(sock) )
+
+#if defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || _POSIX_C_SOURCE >= 1
+#define _MONGO_USE_GETADDRINFO
+#endif
+
+MONGO_EXTERN_C_START
+
+int mongo_set_socket_op_timeout( mongo *conn, int millis );
+int mongo_read_socket( mongo *conn, void *buf, int len );
+int mongo_write_socket( mongo *conn, const void *buf, int len );
+int mongo_socket_connect( mongo *conn, const char *host, int port );
+
+MONGO_EXTERN_C_END
+#endif
diff --git a/src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c b/src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c
new file mode 100644 (file)
index 0000000..d721bed
--- /dev/null
@@ -0,0 +1,413 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Daniel Swarbrick <daniel.swarbrick@seventhsignal.de>
+ * 
+ * mod_cdr_mongodb.c -- MongoDB CDR Module
+ *
+ * Derived from:
+ * mod_xml_cdr.c -- XML CDR Module to files or curl
+ *
+ */
+#include <switch.h>
+#include <mongo.h>
+
+static struct {
+       switch_memory_pool_t *pool;
+       int shutdown;
+       char *mongo_host;
+       uint32_t mongo_port;
+       char *mongo_namespace;
+       mongo mongo_conn[1];
+       switch_mutex_t *mongo_mutex;
+       switch_bool_t log_b;
+} globals;
+
+static switch_xml_config_item_t config_settings[] = {
+       /* key, flags, ptr, default_value, syntax, helptext */
+       SWITCH_CONFIG_ITEM_STRING_STRDUP("host", CONFIG_REQUIRED, &globals.mongo_host, "127.0.0.1", NULL, "MongoDB server host address"),
+       SWITCH_CONFIG_ITEM_STRING_STRDUP("namespace", CONFIG_REQUIRED, &globals.mongo_namespace, NULL, "database.collection", "MongoDB namespace"),
+
+       /* key, type, flags, ptr, default_value, data, syntax, helptext */
+       SWITCH_CONFIG_ITEM("port", SWITCH_CONFIG_INT, CONFIG_REQUIRED, &globals.mongo_port, 27017, NULL, NULL, "MongoDB server TCP port"),
+       SWITCH_CONFIG_ITEM("log-b-leg", SWITCH_CONFIG_BOOL, CONFIG_RELOADABLE, &globals.log_b, SWITCH_TRUE, NULL, NULL, "Log B-leg in addition to A-leg"),
+
+       SWITCH_CONFIG_ITEM_END()
+};
+
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_mongodb_load);
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_mongodb_shutdown);
+SWITCH_MODULE_DEFINITION(mod_cdr_mongodb, mod_cdr_mongodb_load, mod_cdr_mongodb_shutdown, NULL);
+
+static void set_bson_profile_data(bson *b, switch_caller_profile_t *caller_profile)
+{
+       bson_append_string(b, "username", caller_profile->username);
+       bson_append_string(b, "dialplan", caller_profile->dialplan);
+       bson_append_string(b, "caller_id_name", caller_profile->caller_id_name);
+       bson_append_string(b, "ani", caller_profile->ani);
+       bson_append_string(b, "aniii", caller_profile->aniii);
+       bson_append_string(b, "caller_id_number", caller_profile->caller_id_number);
+       bson_append_string(b, "network_addr", caller_profile->network_addr);
+       bson_append_string(b, "rdnis", caller_profile->rdnis);
+       bson_append_string(b, "destination_number", caller_profile->destination_number);
+       bson_append_string(b, "uuid", caller_profile->uuid);
+       bson_append_string(b, "source", caller_profile->source);
+       bson_append_string(b, "context", caller_profile->context);
+       bson_append_string(b, "chan_name", caller_profile->chan_name);
+}
+
+
+static switch_status_t my_on_reporting(switch_core_session_t *session)
+{
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
+       switch_channel_t *channel = switch_core_session_get_channel(session);
+       switch_event_header_t *hi;
+       switch_caller_profile_t *caller_profile;
+       switch_app_log_t *app_log;
+       bson cdr;
+       int is_b;
+       char *tmp;
+
+       if (globals.shutdown) {
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       is_b = channel && switch_channel_get_originator_caller_profile(channel);
+       if (!globals.log_b && is_b) {
+               const char *force_cdr = switch_channel_get_variable(channel, SWITCH_FORCE_PROCESS_CDR_VARIABLE);
+               if (!switch_true(force_cdr)) {
+                       return SWITCH_STATUS_SUCCESS;
+               }
+       }
+
+       bson_init(&cdr);
+
+       /* Channel data */
+       bson_append_start_object(&cdr, "channel_data");
+       bson_append_string(&cdr, "state", switch_channel_state_name(switch_channel_get_state(channel)));
+       bson_append_string(&cdr, "direction", switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
+       bson_append_int(&cdr, "state_number", switch_channel_get_state(channel));
+
+       if ((tmp = switch_channel_get_flag_string(channel))) {
+               bson_append_string(&cdr, "flags", tmp);
+               free(tmp);
+       }
+
+       if ((tmp = switch_channel_get_cap_string(channel))) {
+               bson_append_string(&cdr, "caps", tmp);
+               free(tmp);
+       }
+       bson_append_finish_object(&cdr);                                /* channel_data */
+
+
+       /* Channel variables */
+       bson_append_start_object(&cdr, "variables");
+       for (hi = switch_channel_variable_first(channel); hi; hi = hi->next) {
+               if (!zstr(hi->name) && !zstr(hi->value)) {
+                       bson_append_string(&cdr, hi->name, hi->value);
+               }
+       }
+       bson_append_finish_object(&cdr);                                /* variables */
+
+
+       /* App log */
+       if ((app_log = switch_core_session_get_app_log(session))) {
+               switch_app_log_t *ap;
+
+               bson_append_start_object(&cdr, "app_log");
+               for (ap = app_log; ap; ap = ap->next) {
+                       bson_append_start_object(&cdr, "application");
+                       bson_append_string(&cdr, "app_name", ap->app);
+                       bson_append_string(&cdr, "app_data", ap->arg);
+                       bson_append_long(&cdr, "app_stamp", ap->stamp);
+                       bson_append_finish_object(&cdr);                /* application */
+               }
+
+               bson_append_finish_object(&cdr);                        /* app_log */
+       }
+
+
+       /* Callflow */
+       caller_profile = switch_channel_get_caller_profile(channel);
+
+       while (caller_profile) {
+               bson_append_start_object(&cdr, "callflow");
+
+               if (!zstr(caller_profile->dialplan)) {
+                       bson_append_string(&cdr, "dialplan", caller_profile->dialplan);
+               }
+
+               if (!zstr(caller_profile->profile_index)) {
+                       bson_append_string(&cdr, "profile_index", caller_profile->profile_index);
+               }
+
+               if (caller_profile->caller_extension) {
+                       switch_caller_application_t *ap;
+
+                       bson_append_start_object(&cdr, "extension");
+
+                       bson_append_string(&cdr, "name", caller_profile->caller_extension->extension_name);
+                       bson_append_string(&cdr, "number", caller_profile->caller_extension->extension_number);
+
+                       if (caller_profile->caller_extension->current_application) {
+                               bson_append_string(&cdr, "current_app", caller_profile->caller_extension->current_application->application_name);
+                       }
+
+                       for (ap = caller_profile->caller_extension->applications; ap; ap = ap->next) {
+                               bson_append_start_object(&cdr, "application");
+                               if (ap == caller_profile->caller_extension->current_application) {
+                                       bson_append_bool(&cdr, "last_executed", 1);
+                               }
+                               bson_append_string(&cdr, "app_name", ap->application_name);
+                               bson_append_string(&cdr, "app_data", ap->application_data);
+                               bson_append_finish_object(&cdr);
+                       }
+
+                       if (caller_profile->caller_extension->children) {
+                               switch_caller_profile_t *cp = NULL;
+
+                               for (cp = caller_profile->caller_extension->children; cp; cp = cp->next) {
+
+                                       if (!cp->caller_extension) {
+                                               continue;
+                                       }
+
+                                       bson_append_start_object(&cdr, "sub_extensions");
+                                       bson_append_start_object(&cdr, "extension");
+
+                                       bson_append_string(&cdr, "name", cp->caller_extension->extension_name);
+                                       bson_append_string(&cdr, "number", cp->caller_extension->extension_number);
+                                       bson_append_string(&cdr, "dialplan", cp->dialplan);
+                                       if (cp->caller_extension->current_application) {
+                                               bson_append_string(&cdr, "current_app", cp->caller_extension->current_application->application_name);
+                                       }
+
+                                       for (ap = cp->caller_extension->applications; ap; ap = ap->next) {
+                                               bson_append_start_object(&cdr, "application");
+                                               if (ap == cp->caller_extension->current_application) {
+                                                       bson_append_bool(&cdr, "last_executed", 1);
+                                               }
+                                               bson_append_string(&cdr, "app_name", ap->application_name);
+                                               bson_append_string(&cdr, "app_data", ap->application_data);
+                                               bson_append_finish_object(&cdr);
+                                       }
+
+                                       bson_append_finish_object(&cdr);        /* extension */
+                                       bson_append_finish_object(&cdr);        /* sub_extensions */
+                               }
+                       }
+
+                       bson_append_finish_object(&cdr);                        /* extension */
+               }
+
+               bson_append_start_object(&cdr, "caller_profile");
+               set_bson_profile_data(&cdr, caller_profile);
+
+               if (caller_profile->origination_caller_profile) {
+                       switch_caller_profile_t *cp = NULL;
+
+                       bson_append_start_object(&cdr, "origination");
+                       for (cp = caller_profile->origination_caller_profile; cp; cp = cp->next) {
+                               bson_append_start_object(&cdr, "origination_caller_profile");
+                               set_bson_profile_data(&cdr, cp);
+                               bson_append_finish_object(&cdr);
+                       }
+                       bson_append_finish_object(&cdr);                        /* origination */
+               }
+
+               if (caller_profile->originator_caller_profile) {
+                       switch_caller_profile_t *cp = NULL;
+
+                       bson_append_start_object(&cdr, "originator");
+                       for (cp = caller_profile->originator_caller_profile; cp; cp = cp->next) {
+                               bson_append_start_object(&cdr, "originator_caller_profile");
+                               set_bson_profile_data(&cdr, cp);
+                               bson_append_finish_object(&cdr);
+                       }
+                       bson_append_finish_object(&cdr);                        /* originator */
+               }
+
+               if (caller_profile->originatee_caller_profile) {
+                       switch_caller_profile_t *cp = NULL;
+
+                       bson_append_start_object(&cdr, "originatee");
+                       for (cp = caller_profile->originatee_caller_profile; cp; cp = cp->next) {
+                               bson_append_start_object(&cdr, "originatee_caller_profile");
+                               set_bson_profile_data(&cdr, cp);
+                               bson_append_finish_object(&cdr);
+                       }
+                       bson_append_finish_object(&cdr);                        /* originatee */
+               }
+
+               bson_append_finish_object(&cdr);                                /* caller_profile */
+
+               /* Timestamps */
+               if (caller_profile->times) {
+                       bson_append_start_object(&cdr, "times");
+
+                       /* Insert timestamps as long ints (microseconds) to preserve accuracy */
+                       bson_append_long(&cdr, "created_time", caller_profile->times->created);
+                       bson_append_long(&cdr, "profile_created_time", caller_profile->times->profile_created);
+                       bson_append_long(&cdr, "progress_time", caller_profile->times->progress);
+                       bson_append_long(&cdr, "progress_media_time", caller_profile->times->progress_media);
+                       bson_append_long(&cdr, "answered_time", caller_profile->times->answered);
+                       bson_append_long(&cdr, "bridged_time", caller_profile->times->bridged);
+                       bson_append_long(&cdr, "last_hold_time", caller_profile->times->last_hold);
+                       bson_append_long(&cdr, "hold_accum_time", caller_profile->times->hold_accum);
+                       bson_append_long(&cdr, "hangup_time", caller_profile->times->hungup);
+                       bson_append_long(&cdr, "resurrect_time", caller_profile->times->resurrected);
+                       bson_append_long(&cdr, "transfer_time", caller_profile->times->transferred);
+                       bson_append_finish_object(&cdr);                        /* times */
+               }
+
+               bson_append_finish_object(&cdr);                                /* callflow */
+               caller_profile = caller_profile->next;
+       }
+
+       bson_finish(&cdr);
+
+       switch_mutex_lock(globals.mongo_mutex);
+
+       if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr) != MONGO_OK) {
+               if (globals.mongo_conn->err == MONGO_IO_ERROR) {
+                       mongo_error_t db_status;
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MongoDB connection failed; attempting reconnect...\n");
+                       db_status = mongo_reconnect(globals.mongo_conn);
+
+                       if (db_status != MONGO_OK) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MongoDB reconnect failed with error code %d\n", db_status);
+                               status = SWITCH_STATUS_FALSE;
+                       } else {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "MongoDB connection re-established.\n");
+                               if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr) != MONGO_OK) {
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_insert: error code %d\n", globals.mongo_conn->err);
+                                       status = SWITCH_STATUS_FALSE;
+                               }
+                       }
+
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_insert: error code %d\n", globals.mongo_conn->err);
+                       status = SWITCH_STATUS_FALSE;
+               }
+       }
+
+       switch_mutex_unlock(globals.mongo_mutex);
+       bson_destroy(&cdr);
+
+       return status;
+}
+
+
+static switch_state_handler_table_t state_handlers = {
+       /*.on_init */ NULL,
+       /*.on_routing */ NULL,
+       /*.on_execute */ NULL,
+       /*.on_hangup */ NULL,
+       /*.on_exchange_media */ NULL,
+       /*.on_soft_execute */ NULL,
+       /*.on_consume_media */ NULL,
+       /*.on_hibernate */ NULL,
+       /*.on_reset */ NULL,
+       /*.on_park */ NULL,
+       /*.on_reporting */ my_on_reporting
+};
+
+
+static switch_status_t load_config(switch_memory_pool_t *pool)
+{
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+       if (switch_xml_config_parse_module_settings("cdr_mongodb.conf", SWITCH_FALSE, config_settings) != SWITCH_STATUS_SUCCESS) {
+               return SWITCH_STATUS_FALSE;
+       }
+
+       return status;
+}
+
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_mongodb_load)
+{
+       switch_status_t status = SWITCH_STATUS_SUCCESS;
+       mongo_error_t db_status;
+
+       memset(&globals, 0, sizeof(globals));
+       globals.pool = pool;
+       if (load_config(pool) != SWITCH_STATUS_SUCCESS) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Unable to load or parse config!\n");
+       }
+
+       db_status = mongo_connect(globals.mongo_conn, globals.mongo_host, globals.mongo_port);
+
+       if (db_status != MONGO_OK) {
+               switch (globals.mongo_conn->err) {
+                       case MONGO_CONN_NO_SOCKET:
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: no socket\n");
+                               break;
+                       case MONGO_CONN_FAIL:
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: connection failed\n");
+                               break;
+                       case MONGO_CONN_NOT_MASTER:
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: not master\n");
+                               break;
+                       default:
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: unknown error %d\n", db_status);
+               }
+               return SWITCH_STATUS_FALSE;
+       } else {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected to MongoDB server %s:%d\n", globals.mongo_host, globals.mongo_port);
+       }
+
+       switch_mutex_init(&globals.mongo_mutex, SWITCH_MUTEX_NESTED, pool);
+
+       switch_core_add_state_handler(&state_handlers);
+       *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+
+       return status;
+}
+
+
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_mongodb_shutdown)
+{
+       globals.shutdown = 1;
+       switch_core_remove_state_handler(&state_handlers);
+       switch_mutex_destroy(globals.mongo_mutex);
+
+       mongo_destroy(globals.mongo_conn);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
+ */