From a91691999d4410672f37f45cf17306ac8863fcb1 Mon Sep 17 00:00:00 2001 From: Daniel Swarbrick Date: Wed, 7 Sep 2011 23:50:25 +0200 Subject: [PATCH] add MongoDB CDR module --- .../event_handlers/mod_cdr_mongodb/Makefile | 9 + .../mod_cdr_mongodb/driver/APACHE-2.0.txt | 202 +++ .../mod_cdr_mongodb/driver/HISTORY.md | 139 ++ .../mod_cdr_mongodb/driver/README.md | 60 + .../mod_cdr_mongodb/driver/SConstruct | 178 +++ .../mod_cdr_mongodb/driver/src/bson.c | 978 +++++++++++++ .../mod_cdr_mongodb/driver/src/bson.h | 975 +++++++++++++ .../mod_cdr_mongodb/driver/src/encoding.c | 148 ++ .../mod_cdr_mongodb/driver/src/encoding.h | 54 + .../mod_cdr_mongodb/driver/src/gridfs.c | 685 ++++++++++ .../mod_cdr_mongodb/driver/src/gridfs.h | 326 +++++ .../mod_cdr_mongodb/driver/src/md5.c | 381 ++++++ .../mod_cdr_mongodb/driver/src/md5.h | 91 ++ .../mod_cdr_mongodb/driver/src/mongo.c | 1217 +++++++++++++++++ .../mod_cdr_mongodb/driver/src/mongo.h | 648 +++++++++ .../mod_cdr_mongodb/driver/src/net.c | 98 ++ .../mod_cdr_mongodb/driver/src/net.h | 57 + .../mod_cdr_mongodb/driver/src/numbers.c | 127 ++ .../mod_cdr_mongodb/driver/src/platform.h | 94 ++ .../driver/src/platform/linux/net.c | 183 +++ .../driver/src/platform/linux/net.h | 51 + .../mod_cdr_mongodb/mod_cdr_mongodb.c | 413 ++++++ 22 files changed, 7114 insertions(+) create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/Makefile create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/APACHE-2.0.txt create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/README.md create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/SConstruct create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.h create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.h create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.c create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.h create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.c create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.h create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/numbers.c create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform.h create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.c create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.h create mode 100644 src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c diff --git a/src/mod/event_handlers/mod_cdr_mongodb/Makefile b/src/mod/event_handlers/mod_cdr_mongodb/Makefile new file mode 100644 index 0000000000..b9e04b983d --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/Makefile @@ -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 index 0000000000..d645695673 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/APACHE-2.0.txt @@ -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 index 0000000000..f3f0fab21d --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md @@ -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 index 0000000000..1afe39e69c --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/README.md @@ -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 index 0000000000..d3be2d89a6 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/SConstruct @@ -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 \n'): + conf.env.Append( CPPDEFINES="MONGO_HAVE_STDINT" ) + elif conf.CheckType('int64_t', '#include \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 index 0000000000..349f42284c --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c @@ -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 +#include +#include +#include +#include + +#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; istackPos; 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 index 0000000000..2e385b9160 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h @@ -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 +#include +#include +#include +#include + +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 index 0000000000..8d2da1502f --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c @@ -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 index 0000000000..a2a07c6d5d --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.h @@ -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 index 0000000000..f51b397d3f --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c @@ -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 +#include +#include +#include + +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( >e ); + bson_append_int( >e, "$gte", start ); + bson_finish( >e ); + bson_append_bson( &query, "n", >e ); + bson_destroy( >e ); + } + 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; ipos < 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 index 0000000000..36595ee341 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.h @@ -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 index 0000000000..d1c1e3ab0b --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.c @@ -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 + . 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 + 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 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 + +#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 index 0000000000..540da3a584 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.h @@ -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 + . 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 . + 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 index 0000000000..2090e74312 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c @@ -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 +#include +#include +#include + +#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; idata; + data = mongo_data_append32( data, &ZERO ); + data = mongo_data_append( data, ns, strlen( ns ) + 1 ); + + for( i=0; idata, 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 index 0000000000..6f86174101 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h @@ -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 index 0000000000..5c8ad9ba5e --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.c @@ -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 + +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 index 0000000000..49190877c8 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.h @@ -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 +#include +#define mongo_close_socket(sock) ( closesocket(sock) ) +typedef int socklen_t; +#else +#include +#include +#include +#include +#include +#include +#include +#define mongo_close_socket(sock) ( close(sock) ) +#endif + +#ifndef _WIN32 +#include +#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 index 0000000000..a63e3d73f9 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/numbers.c @@ -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 index 0000000000..4a96af77e3 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform.h @@ -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 +#elif defined(MONGO_HAVE_UNISTD) +#include +#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 index 0000000000..b2f7c22898 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.c @@ -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 + +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 index 0000000000..054247c1e8 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000000..d721bed8a5 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c @@ -0,0 +1,413 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2011, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Daniel Swarbrick + * + * mod_cdr_mongodb.c -- MongoDB CDR Module + * + * Derived from: + * mod_xml_cdr.c -- XML CDR Module to files or curl + * + */ +#include +#include + +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: + */