add MongoDB CDR module

This commit is contained in:
Daniel Swarbrick 2011-09-07 23:50:25 +02:00
parent aff4bcbe12
commit a91691999d
22 changed files with 7114 additions and 0 deletions

View File

@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,178 @@
# -*- mode: python; -*-
VERSION = "0.4"
# --- options ----
AddOption('--test-server',
dest='test_server',
default='127.0.0.1',
type='string',
nargs=1,
action='store',
help='IP address of server to use for testing')
AddOption('--seed-start-port',
dest='seed_start_port',
default=30000,
type='int',
nargs=1,
action='store',
help='IP address of server to use for testing')
AddOption('--c99',
dest='use_c99',
default=False,
action='store_true',
help='Compile with c99 (recommended for gcc)')
AddOption('--d',
dest='optimize',
default=True,
action='store_false',
help='disable optimizations')
AddOption('--use-platform',
dest='compile_platform',
default='GENERIC',
type='string',
nargs=1,
action='store',
help='Compile for a specific platform to take advantage '
' of particular system features. For the moment, this include timeouts only.'
' Current options include LINUX, '
' GENERIC, and CUSTOM. If you specific CUSTOM, you must place a'
' system-specific implementation of net.h and net.c in src/platform/custom/')
import os, sys
env = Environment( ENV=os.environ )
# ---- Docs ----
def build_docs(env, target, source):
buildscript_path = os.path.join(os.path.abspath("docs"))
sys.path.insert(0, buildscript_path)
import buildscripts
from buildscripts import docs
docs.main()
env.Alias("docs", [], [build_docs])
env.AlwaysBuild("docs")
# ---- Platforms ----
PLATFORM_TEST_DIR = None
if "LINUX" == GetOption('compile_platform'):
env.Append( CPPFLAGS=" -D_MONGO_USE_LINUX_SYSTEM" )
NET_LIB = "src/platform/linux/net.c"
PLATFORM_TEST_DIR = "test/platform/linux/"
PLATFORM_TESTS = [ "timeouts" ]
elif "CUSTOM" == GetOption('compile_platform'):
env.Append( CPPFLAGS=" -D_MONGO_USE_CUSTOM_SYSTEM" )
NET_LIB = "src/platform/custom/net.c"
else:
NET_LIB = "src/net.c"
# ---- Libraries ----
if os.sys.platform in ["darwin", "linux2"]:
env.Append( CPPFLAGS=" -pedantic -Wall -ggdb -DMONGO_HAVE_STDINT" )
env.Append( CPPPATH=["/opt/local/include/"] )
env.Append( LIBPATH=["/opt/local/lib/"] )
if GetOption('use_c99'):
env.Append( CFLAGS=" -std=c99 " )
env.Append( CXXDEFINES="MONGO_HAVE_STDINT" )
else:
env.Append( CFLAGS=" -ansi " )
if GetOption('optimize'):
env.Append( CPPFLAGS=" -O3 " )
# -O3 benchmarks *significantly* faster than -O2 when disabling networking
elif 'win32' == os.sys.platform:
env.Append( LIBS='ws2_32' )
#we shouldn't need these options in c99 mode
if not GetOption('use_c99'):
conf = Configure(env)
if not conf.CheckType('int64_t'):
if conf.CheckType('int64_t', '#include <stdint.h>\n'):
conf.env.Append( CPPDEFINES="MONGO_HAVE_STDINT" )
elif conf.CheckType('int64_t', '#include <unistd.h>\n'):
conf.env.Append( CPPDEFINES="MONGO_HAVE_UNISTD" )
elif conf.CheckType('__int64'):
conf.env.Append( CPPDEFINES="MONGO_USE__INT64" )
elif conf.CheckType('long long int'):
conf.env.Append( CPPDEFINES="MONGO_USE_LONG_LONG_INT" )
else:
print "*** what is your 64 bit int type? ****"
Exit(1)
env = conf.Finish()
have_libjson = False
conf = Configure(env)
if conf.CheckLib('json'):
have_libjson = True
env = conf.Finish()
if sys.byteorder == 'big':
env.Append( CPPDEFINES="MONGO_BIG_ENDIAN" )
env.Append( CPPPATH=["src/"] )
coreFiles = ["src/md5.c" ]
mFiles = [ "src/mongo.c", NET_LIB, "src/gridfs.c"]
bFiles = [ "src/bson.c", "src/numbers.c", "src/encoding.c"]
mLibFiles = coreFiles + mFiles + bFiles
bLibFiles = coreFiles + bFiles
m = env.Library( "mongoc" , mLibFiles )
b = env.Library( "bson" , bLibFiles )
env.Default( env.Alias( "lib" , [ m[0] , b[0] ] ) )
if os.sys.platform == "linux2":
env.Append( SHLINKFLAGS="-shared -Wl,-soname,libmongoc.so." + VERSION )
env.Append( SHLINKFLAGS = "-shared -Wl,-soname,libbson.so." + VERSION )
dynm = env.SharedLibrary( "mongoc" , mLibFiles )
dynb = env.SharedLibrary( "bson" , bLibFiles )
env.Default( env.Alias( "sharedlib" , [ dynm[0] , dynb[0] ] ) )
# ---- Benchmarking ----
benchmarkEnv = env.Clone()
benchmarkEnv.Append( CPPDEFINES=[('TEST_SERVER', r'\"%s\"'%GetOption('test_server')),
('SEED_START_PORT', r'%d'%GetOption('seed_start_port'))] )
benchmarkEnv.Append( LIBS=[m, b] )
benchmarkEnv.Prepend( LIBPATH=["."] )
benchmarkEnv.Program( "benchmark" , [ "test/benchmark.c"] )
# ---- Tests ----
testEnv = benchmarkEnv.Clone()
testCoreFiles = [ ]
def run_tests( root, tests ):
for name in tests:
filename = "%s/%s.c" % (root, name)
exe = "test_" + name
test = testEnv.Program( exe , testCoreFiles + [filename] )
test_alias = testEnv.Alias('test', [test], test[0].abspath + ' 2> ' + os.path.devnull)
AlwaysBuild(test_alias)
tests = Split("sizes resize endian_swap bson bson_subobject simple update errors "
"count_delete auth gridfs validate examples helpers oid functions cursors replica_set")
# Run standard tests
run_tests("test", tests)
# Run platform tests
if not PLATFORM_TEST_DIR is None:
run_tests( PLATFORM_TEST_DIR, PLATFORM_TESTS )
if have_libjson:
tests.append('json')
testEnv.Append( LIBS=["json"] )
# special case for cpptest
test = testEnv.Program( 'test_cpp' , testCoreFiles + ['test/cpptest.cpp'] )
test_alias = testEnv.Alias('test', [test], test[0].abspath + ' 2> '+ os.path.devnull)
AlwaysBuild(test_alias)

View File

@ -0,0 +1,978 @@
/* bson.c */
/* Copyright 2009, 2010 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <limits.h>
#include "bson.h"
#include "encoding.h"
const int initialBufferSize = 128;
/* only need one of these */
static const int zero = 0;
/* Custom standard function pointers. */
void *( *bson_malloc_func )( size_t ) = malloc;
void *( *bson_realloc_func )( void *, size_t ) = realloc;
void ( *bson_free )( void * ) = free;
bson_printf_func bson_printf = printf;
bson_fprintf_func bson_fprintf = fprintf;
bson_sprintf_func bson_sprintf = sprintf;
static int _bson_errprintf( const char *, ... );
bson_printf_func bson_errprintf = _bson_errprintf;
/* ObjectId fuzz functions. */
static int ( *oid_fuzz_func )( void ) = NULL;
static int ( *oid_inc_func )( void ) = NULL;
/* ----------------------------
READING
------------------------------ */
bson *bson_empty( bson *obj ) {
static char *data = "\005\0\0\0\0";
bson_init_data( obj, data );
obj->finished = 1;
obj->err = 0;
obj->stackPos = 0;
return obj;
}
void bson_copy_basic( bson *out, const bson *in ) {
if ( !out ) return;
bson_init_size( out, bson_size( in ) );
memcpy( out->data, in->data, bson_size( in ) );
}
void bson_copy( bson *out, const bson *in ) {
int i;
if ( !out ) return;
bson_copy_basic( out, in );
out->cur = out->data + ( in->cur - in->data );
out->dataSize = in->dataSize;
out->finished = in->finished;
out->stackPos = in->stackPos;
out->err = in->err;
for( i=0; i<out->stackPos; i++ )
out->stack[i] = in->stack[i];
}
int bson_init_data( bson *b, char *data ) {
b->data = data;
return BSON_OK;
}
static void _bson_reset( bson *b ) {
b->finished = 0;
b->stackPos = 0;
b->err = 0;
b->errstr = NULL;
}
int bson_size( const bson *b ) {
int i;
if ( ! b || ! b->data )
return 0;
bson_little_endian32( &i, b->data );
return i;
}
const char *bson_data( bson *b ) {
return (const char *)b->data;
}
static char hexbyte( char hex ) {
switch ( hex ) {
case '0':
return 0x0;
case '1':
return 0x1;
case '2':
return 0x2;
case '3':
return 0x3;
case '4':
return 0x4;
case '5':
return 0x5;
case '6':
return 0x6;
case '7':
return 0x7;
case '8':
return 0x8;
case '9':
return 0x9;
case 'a':
case 'A':
return 0xa;
case 'b':
case 'B':
return 0xb;
case 'c':
case 'C':
return 0xc;
case 'd':
case 'D':
return 0xd;
case 'e':
case 'E':
return 0xe;
case 'f':
case 'F':
return 0xf;
default:
return 0x0; /* something smarter? */
}
}
void bson_oid_from_string( bson_oid_t *oid, const char *str ) {
int i;
for ( i=0; i<12; i++ ) {
oid->bytes[i] = ( hexbyte( str[2*i] ) << 4 ) | hexbyte( str[2*i + 1] );
}
}
void bson_oid_to_string( const bson_oid_t *oid, char *str ) {
static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
int i;
for ( i=0; i<12; i++ ) {
str[2*i] = hex[( oid->bytes[i] & 0xf0 ) >> 4];
str[2*i + 1] = hex[ oid->bytes[i] & 0x0f ];
}
str[24] = '\0';
}
void bson_set_oid_fuzz( int ( *func )( void ) ) {
oid_fuzz_func = func;
}
void bson_set_oid_inc( int ( *func )( void ) ) {
oid_inc_func = func;
}
void bson_oid_gen( bson_oid_t *oid ) {
static int incr = 0;
static int fuzz = 0;
int i;
int t = time( NULL );
if( oid_inc_func )
i = oid_inc_func();
else
i = incr++;
if ( !fuzz ) {
if ( oid_fuzz_func )
fuzz = oid_fuzz_func();
else {
srand( t );
fuzz = rand();
}
}
bson_big_endian32( &oid->ints[0], &t );
oid->ints[1] = fuzz;
bson_big_endian32( &oid->ints[2], &i );
}
time_t bson_oid_generated_time( bson_oid_t *oid ) {
time_t out;
bson_big_endian32( &out, &oid->ints[0] );
return out;
}
void bson_print( bson *b ) {
bson_print_raw( b->data , 0 );
}
void bson_print_raw( const char *data , int depth ) {
bson_iterator i;
const char *key;
int temp;
bson_timestamp_t ts;
char oidhex[25];
bson scope;
bson_iterator_from_buffer( &i, data );
while ( bson_iterator_next( &i ) ) {
bson_type t = bson_iterator_type( &i );
if ( t == 0 )
break;
key = bson_iterator_key( &i );
for ( temp=0; temp<=depth; temp++ )
printf( "\t" );
bson_printf( "%s : %d \t " , key , t );
switch ( t ) {
case BSON_DOUBLE:
printf( "%f" , bson_iterator_double( &i ) );
break;
case BSON_STRING:
printf( "%s" , bson_iterator_string( &i ) );
break;
case BSON_SYMBOL:
printf( "SYMBOL: %s" , bson_iterator_string( &i ) );
break;
case BSON_OID:
bson_oid_to_string( bson_iterator_oid( &i ), oidhex );
printf( "%s" , oidhex );
break;
case BSON_BOOL:
printf( "%s" , bson_iterator_bool( &i ) ? "true" : "false" );
break;
case BSON_DATE:
printf( "%ld" , ( long int )bson_iterator_date( &i ) );
break;
case BSON_BINDATA:
printf( "BSON_BINDATA" );
break;
case BSON_UNDEFINED:
printf( "BSON_UNDEFINED" );
break;
case BSON_NULL:
printf( "BSON_NULL" );
break;
case BSON_REGEX:
printf( "BSON_REGEX: %s", bson_iterator_regex( &i ) );
break;
case BSON_CODE:
printf( "BSON_CODE: %s", bson_iterator_code( &i ) );
break;
case BSON_CODEWSCOPE:
printf( "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) );
bson_init( &scope );
bson_iterator_code_scope( &i, &scope );
printf( "\n\t SCOPE: " );
bson_print( &scope );
break;
case BSON_INT:
printf( "%d" , bson_iterator_int( &i ) );
break;
case BSON_LONG:
printf( "%lld" , ( long long int )bson_iterator_long( &i ) );
break;
case BSON_TIMESTAMP:
ts = bson_iterator_timestamp( &i );
printf( "i: %d, t: %d", ts.i, ts.t );
break;
case BSON_OBJECT:
case BSON_ARRAY:
printf( "\n" );
bson_print_raw( bson_iterator_value( &i ) , depth + 1 );
break;
default:
bson_errprintf( "can't print type : %d\n" , t );
}
printf( "\n" );
}
}
/* ----------------------------
ITERATOR
------------------------------ */
void bson_iterator_init( bson_iterator *i, const bson *b ) {
i->cur = b->data + 4;
i->first = 1;
}
void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ) {
i->cur = buffer + 4;
i->first = 1;
}
bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ) {
bson_iterator_init( it, (bson *)obj );
while( bson_iterator_next( it ) ) {
if ( strcmp( name, bson_iterator_key( it ) ) == 0 )
break;
}
return bson_iterator_type( it );
}
bson_bool_t bson_iterator_more( const bson_iterator *i ) {
return *( i->cur );
}
bson_type bson_iterator_next( bson_iterator *i ) {
int ds;
if ( i->first ) {
i->first = 0;
return ( bson_type )( *i->cur );
}
switch ( bson_iterator_type( i ) ) {
case BSON_EOO:
return BSON_EOO; /* don't advance */
case BSON_UNDEFINED:
case BSON_NULL:
ds = 0;
break;
case BSON_BOOL:
ds = 1;
break;
case BSON_INT:
ds = 4;
break;
case BSON_LONG:
case BSON_DOUBLE:
case BSON_TIMESTAMP:
case BSON_DATE:
ds = 8;
break;
case BSON_OID:
ds = 12;
break;
case BSON_STRING:
case BSON_SYMBOL:
case BSON_CODE:
ds = 4 + bson_iterator_int_raw( i );
break;
case BSON_BINDATA:
ds = 5 + bson_iterator_int_raw( i );
break;
case BSON_OBJECT:
case BSON_ARRAY:
case BSON_CODEWSCOPE:
ds = bson_iterator_int_raw( i );
break;
case BSON_DBREF:
ds = 4+12 + bson_iterator_int_raw( i );
break;
case BSON_REGEX: {
const char *s = bson_iterator_value( i );
const char *p = s;
p += strlen( p )+1;
p += strlen( p )+1;
ds = p-s;
break;
}
default: {
char msg[] = "unknown type: 000000000000";
bson_numstr( msg+14, ( unsigned )( i->cur[0] ) );
bson_fatal_msg( 0, msg );
return 0;
}
}
i->cur += 1 + strlen( i->cur + 1 ) + 1 + ds;
return ( bson_type )( *i->cur );
}
bson_type bson_iterator_type( const bson_iterator *i ) {
return ( bson_type )i->cur[0];
}
const char *bson_iterator_key( const bson_iterator *i ) {
return i->cur + 1;
}
const char *bson_iterator_value( const bson_iterator *i ) {
const char *t = i->cur + 1;
t += strlen( t ) + 1;
return t;
}
/* types */
int bson_iterator_int_raw( const bson_iterator *i ) {
int out;
bson_little_endian32( &out, bson_iterator_value( i ) );
return out;
}
double bson_iterator_double_raw( const bson_iterator *i ) {
double out;
bson_little_endian64( &out, bson_iterator_value( i ) );
return out;
}
int64_t bson_iterator_long_raw( const bson_iterator *i ) {
int64_t out;
bson_little_endian64( &out, bson_iterator_value( i ) );
return out;
}
bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ) {
return bson_iterator_value( i )[0];
}
bson_oid_t *bson_iterator_oid( const bson_iterator *i ) {
return ( bson_oid_t * )bson_iterator_value( i );
}
int bson_iterator_int( const bson_iterator *i ) {
switch ( bson_iterator_type( i ) ) {
case BSON_INT:
return bson_iterator_int_raw( i );
case BSON_LONG:
return bson_iterator_long_raw( i );
case BSON_DOUBLE:
return bson_iterator_double_raw( i );
default:
return 0;
}
}
double bson_iterator_double( const bson_iterator *i ) {
switch ( bson_iterator_type( i ) ) {
case BSON_INT:
return bson_iterator_int_raw( i );
case BSON_LONG:
return bson_iterator_long_raw( i );
case BSON_DOUBLE:
return bson_iterator_double_raw( i );
default:
return 0;
}
}
int64_t bson_iterator_long( const bson_iterator *i ) {
switch ( bson_iterator_type( i ) ) {
case BSON_INT:
return bson_iterator_int_raw( i );
case BSON_LONG:
return bson_iterator_long_raw( i );
case BSON_DOUBLE:
return bson_iterator_double_raw( i );
default:
return 0;
}
}
bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ) {
bson_timestamp_t ts;
bson_little_endian32( &( ts.i ), bson_iterator_value( i ) );
bson_little_endian32( &( ts.t ), bson_iterator_value( i ) + 4 );
return ts;
}
bson_bool_t bson_iterator_bool( const bson_iterator *i ) {
switch ( bson_iterator_type( i ) ) {
case BSON_BOOL:
return bson_iterator_bool_raw( i );
case BSON_INT:
return bson_iterator_int_raw( i ) != 0;
case BSON_LONG:
return bson_iterator_long_raw( i ) != 0;
case BSON_DOUBLE:
return bson_iterator_double_raw( i ) != 0;
case BSON_EOO:
case BSON_NULL:
return 0;
default:
return 1;
}
}
const char *bson_iterator_string( const bson_iterator *i ) {
return bson_iterator_value( i ) + 4;
}
int bson_iterator_string_len( const bson_iterator *i ) {
return bson_iterator_int_raw( i );
}
const char *bson_iterator_code( const bson_iterator *i ) {
switch ( bson_iterator_type( i ) ) {
case BSON_STRING:
case BSON_CODE:
return bson_iterator_value( i ) + 4;
case BSON_CODEWSCOPE:
return bson_iterator_value( i ) + 8;
default:
return NULL;
}
}
void bson_iterator_code_scope( const bson_iterator *i, bson *scope ) {
if ( bson_iterator_type( i ) == BSON_CODEWSCOPE ) {
int code_len;
bson_little_endian32( &code_len, bson_iterator_value( i )+4 );
bson_init_data( scope, ( void * )( bson_iterator_value( i )+8+code_len ) );
} else {
bson_empty( scope );
}
}
bson_date_t bson_iterator_date( const bson_iterator *i ) {
return bson_iterator_long_raw( i );
}
time_t bson_iterator_time_t( const bson_iterator *i ) {
return bson_iterator_date( i ) / 1000;
}
int bson_iterator_bin_len( const bson_iterator *i ) {
return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
? bson_iterator_int_raw( i ) - 4
: bson_iterator_int_raw( i );
}
char bson_iterator_bin_type( const bson_iterator *i ) {
return bson_iterator_value( i )[4];
}
const char *bson_iterator_bin_data( const bson_iterator *i ) {
return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
? bson_iterator_value( i ) + 9
: bson_iterator_value( i ) + 5;
}
const char *bson_iterator_regex( const bson_iterator *i ) {
return bson_iterator_value( i );
}
const char *bson_iterator_regex_opts( const bson_iterator *i ) {
const char *p = bson_iterator_value( i );
return p + strlen( p ) + 1;
}
void bson_iterator_subobject( const bson_iterator *i, bson *sub ) {
bson_init_data( sub, ( char * )bson_iterator_value( i ) );
_bson_reset( sub );
sub->finished = 1;
}
void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ) {
bson_iterator_from_buffer( sub, bson_iterator_value( i ) );
}
/* ----------------------------
BUILDING
------------------------------ */
static void _bson_init_size( bson *b, int size ) {
if( size == 0 )
b->data = NULL;
else
b->data = ( char * )bson_malloc( size );
b->dataSize = size;
b->cur = b->data + 4;
_bson_reset( b );
}
void bson_init( bson *b ) {
_bson_init_size( b, initialBufferSize );
}
void bson_init_size( bson *b, int size ) {
_bson_init_size( b, size );
}
void bson_append_byte( bson *b, char c ) {
b->cur[0] = c;
b->cur++;
}
void bson_append( bson *b, const void *data, int len ) {
memcpy( b->cur , data , len );
b->cur += len;
}
void bson_append32( bson *b, const void *data ) {
bson_little_endian32( b->cur, data );
b->cur += 4;
}
void bson_append64( bson *b, const void *data ) {
bson_little_endian64( b->cur, data );
b->cur += 8;
}
int bson_ensure_space( bson *b, const int bytesNeeded ) {
int pos = b->cur - b->data;
char *orig = b->data;
int new_size;
if ( pos + bytesNeeded <= b->dataSize )
return BSON_OK;
new_size = 1.5 * ( b->dataSize + bytesNeeded );
if( new_size < b->dataSize ) {
if( ( b->dataSize + bytesNeeded ) < INT_MAX )
new_size = INT_MAX;
else {
b->err = BSON_SIZE_OVERFLOW;
return BSON_ERROR;
}
}
b->data = bson_realloc( b->data, new_size );
if ( !b->data )
bson_fatal_msg( !!b->data, "realloc() failed" );
b->dataSize = new_size;
b->cur += b->data - orig;
return BSON_OK;
}
int bson_finish( bson *b ) {
int i;
if( b->err & BSON_NOT_UTF8 )
return BSON_ERROR;
if ( ! b->finished ) {
if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
bson_append_byte( b, 0 );
i = b->cur - b->data;
bson_little_endian32( b->data, &i );
b->finished = 1;
}
return BSON_OK;
}
void bson_destroy( bson *b ) {
bson_free( b->data );
b->err = 0;
b->data = 0;
b->cur = 0;
b->finished = 1;
}
static int bson_append_estart( bson *b, int type, const char *name, const int dataSize ) {
const int len = strlen( name ) + 1;
if ( b->finished ) {
b->err |= BSON_ALREADY_FINISHED;
return BSON_ERROR;
}
if ( bson_ensure_space( b, 1 + len + dataSize ) == BSON_ERROR ) {
return BSON_ERROR;
}
if( bson_check_field_name( b, ( const char * )name, len - 1 ) == BSON_ERROR ) {
bson_builder_error( b );
return BSON_ERROR;
}
bson_append_byte( b, ( char )type );
bson_append( b, name, len );
return BSON_OK;
}
/* ----------------------------
BUILDING TYPES
------------------------------ */
int bson_append_int( bson *b, const char *name, const int i ) {
if ( bson_append_estart( b, BSON_INT, name, 4 ) == BSON_ERROR )
return BSON_ERROR;
bson_append32( b , &i );
return BSON_OK;
}
int bson_append_long( bson *b, const char *name, const int64_t i ) {
if ( bson_append_estart( b , BSON_LONG, name, 8 ) == BSON_ERROR )
return BSON_ERROR;
bson_append64( b , &i );
return BSON_OK;
}
int bson_append_double( bson *b, const char *name, const double d ) {
if ( bson_append_estart( b, BSON_DOUBLE, name, 8 ) == BSON_ERROR )
return BSON_ERROR;
bson_append64( b , &d );
return BSON_OK;
}
int bson_append_bool( bson *b, const char *name, const bson_bool_t i ) {
if ( bson_append_estart( b, BSON_BOOL, name, 1 ) == BSON_ERROR )
return BSON_ERROR;
bson_append_byte( b , i != 0 );
return BSON_OK;
}
int bson_append_null( bson *b, const char *name ) {
if ( bson_append_estart( b , BSON_NULL, name, 0 ) == BSON_ERROR )
return BSON_ERROR;
return BSON_OK;
}
int bson_append_undefined( bson *b, const char *name ) {
if ( bson_append_estart( b, BSON_UNDEFINED, name, 0 ) == BSON_ERROR )
return BSON_ERROR;
return BSON_OK;
}
int bson_append_string_base( bson *b, const char *name,
const char *value, int len, bson_type type ) {
int sl = len + 1;
if ( bson_check_string( b, ( const char * )value, sl - 1 ) == BSON_ERROR )
return BSON_ERROR;
if ( bson_append_estart( b, type, name, 4 + sl ) == BSON_ERROR ) {
return BSON_ERROR;
}
bson_append32( b , &sl );
bson_append( b , value , sl - 1 );
bson_append( b , "\0" , 1 );
return BSON_OK;
}
int bson_append_string( bson *b, const char *name, const char *value ) {
return bson_append_string_base( b, name, value, strlen ( value ), BSON_STRING );
}
int bson_append_symbol( bson *b, const char *name, const char *value ) {
return bson_append_string_base( b, name, value, strlen ( value ), BSON_SYMBOL );
}
int bson_append_code( bson *b, const char *name, const char *value ) {
return bson_append_string_base( b, name, value, strlen ( value ), BSON_CODE );
}
int bson_append_string_n( bson *b, const char *name, const char *value, int len ) {
return bson_append_string_base( b, name, value, len, BSON_STRING );
}
int bson_append_symbol_n( bson *b, const char *name, const char *value, int len ) {
return bson_append_string_base( b, name, value, len, BSON_SYMBOL );
}
int bson_append_code_n( bson *b, const char *name, const char *value, int len ) {
return bson_append_string_base( b, name, value, len, BSON_CODE );
}
int bson_append_code_w_scope_n( bson *b, const char *name,
const char *code, int len, const bson *scope ) {
int sl = len + 1;
int size = 4 + 4 + sl + bson_size( scope );
if ( bson_append_estart( b, BSON_CODEWSCOPE, name, size ) == BSON_ERROR )
return BSON_ERROR;
bson_append32( b, &size );
bson_append32( b, &sl );
bson_append( b, code, sl );
bson_append( b, scope->data, bson_size( scope ) );
return BSON_OK;
}
int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ) {
return bson_append_code_w_scope_n( b, name, code, strlen ( code ), scope );
}
int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ) {
if ( type == BSON_BIN_BINARY_OLD ) {
int subtwolen = len + 4;
if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+4+len ) == BSON_ERROR )
return BSON_ERROR;
bson_append32( b, &subtwolen );
bson_append_byte( b, type );
bson_append32( b, &len );
bson_append( b, str, len );
} else {
if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+len ) == BSON_ERROR )
return BSON_ERROR;
bson_append32( b, &len );
bson_append_byte( b, type );
bson_append( b, str, len );
}
return BSON_OK;
}
int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ) {
if ( bson_append_estart( b, BSON_OID, name, 12 ) == BSON_ERROR )
return BSON_ERROR;
bson_append( b , oid , 12 );
return BSON_OK;
}
int bson_append_new_oid( bson *b, const char *name ) {
bson_oid_t oid;
bson_oid_gen( &oid );
return bson_append_oid( b, name, &oid );
}
int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ) {
const int plen = strlen( pattern )+1;
const int olen = strlen( opts )+1;
if ( bson_append_estart( b, BSON_REGEX, name, plen + olen ) == BSON_ERROR )
return BSON_ERROR;
if ( bson_check_string( b, pattern, plen - 1 ) == BSON_ERROR )
return BSON_ERROR;
bson_append( b , pattern , plen );
bson_append( b , opts , olen );
return BSON_OK;
}
int bson_append_bson( bson *b, const char *name, const bson *bson ) {
if ( bson_append_estart( b, BSON_OBJECT, name, bson_size( bson ) ) == BSON_ERROR )
return BSON_ERROR;
bson_append( b , bson->data , bson_size( bson ) );
return BSON_OK;
}
int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ) {
bson_iterator next = *elem;
int size;
bson_iterator_next( &next );
size = next.cur - elem->cur;
if ( name_or_null == NULL ) {
if( bson_ensure_space( b, size ) == BSON_ERROR )
return BSON_ERROR;
bson_append( b, elem->cur, size );
} else {
int data_size = size - 2 - strlen( bson_iterator_key( elem ) );
bson_append_estart( b, elem->cur[0], name_or_null, data_size );
bson_append( b, bson_iterator_value( elem ), data_size );
}
return BSON_OK;
}
int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ) {
if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
bson_append32( b , &( ts->i ) );
bson_append32( b , &( ts->t ) );
return BSON_OK;
}
int bson_append_date( bson *b, const char *name, bson_date_t millis ) {
if ( bson_append_estart( b, BSON_DATE, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
bson_append64( b , &millis );
return BSON_OK;
}
int bson_append_time_t( bson *b, const char *name, time_t secs ) {
return bson_append_date( b, name, ( bson_date_t )secs * 1000 );
}
int bson_append_start_object( bson *b, const char *name ) {
if ( bson_append_estart( b, BSON_OBJECT, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
b->stack[ b->stackPos++ ] = b->cur - b->data;
bson_append32( b , &zero );
return BSON_OK;
}
int bson_append_start_array( bson *b, const char *name ) {
if ( bson_append_estart( b, BSON_ARRAY, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
b->stack[ b->stackPos++ ] = b->cur - b->data;
bson_append32( b , &zero );
return BSON_OK;
}
int bson_append_finish_object( bson *b ) {
char *start;
int i;
if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
bson_append_byte( b , 0 );
start = b->data + b->stack[ --b->stackPos ];
i = b->cur - start;
bson_little_endian32( start, &i );
return BSON_OK;
}
int bson_append_finish_array( bson *b ) {
return bson_append_finish_object( b );
}
/* Error handling and allocators. */
static bson_err_handler err_handler = NULL;
bson_err_handler set_bson_err_handler( bson_err_handler func ) {
bson_err_handler old = err_handler;
err_handler = func;
return old;
}
void *bson_malloc( int size ) {
void *p;
p = bson_malloc_func( size );
bson_fatal_msg( !!p, "malloc() failed" );
return p;
}
void *bson_realloc( void *ptr, int size ) {
void *p;
p = bson_realloc_func( ptr, size );
bson_fatal_msg( !!p, "realloc() failed" );
return p;
}
int _bson_errprintf( const char *format, ... ) {
va_list ap;
int ret;
va_start( ap, format );
ret = vfprintf( stderr, format, ap );
va_end( ap );
return ret;
}
/**
* This method is invoked when a non-fatal bson error is encountered.
* Calls the error handler if available.
*
* @param
*/
void bson_builder_error( bson *b ) {
if( err_handler )
err_handler( "BSON error." );
}
void bson_fatal( int ok ) {
bson_fatal_msg( ok, "" );
}
void bson_fatal_msg( int ok , const char *msg ) {
if ( ok )
return;
if ( err_handler ) {
err_handler( msg );
}
bson_errprintf( "error: %s\n" , msg );
exit( -5 );
}
/* Efficiently copy an integer to a string. */
extern const char bson_numstrs[1000][4];
void bson_numstr( char *str, int i ) {
if( i < 1000 )
memcpy( str, bson_numstrs[i], 4 );
else
bson_sprintf( str,"%d", i );
}

View File

@ -0,0 +1,975 @@
/**
* @file bson.h
* @brief BSON Declarations
*/
/* Copyright 2009-2011 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _BSON_H_
#define _BSON_H_
#include "platform.h"
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
MONGO_EXTERN_C_START
#define BSON_OK 0
#define BSON_ERROR -1
enum bson_error_t {
BSON_SIZE_OVERFLOW = 1 /**< Trying to create a BSON object larger than INT_MAX. */
};
enum bson_validity_t {
BSON_VALID = 0, /**< BSON is valid and UTF-8 compliant. */
BSON_NOT_UTF8 = ( 1<<1 ), /**< A key or a string is not valid UTF-8. */
BSON_FIELD_HAS_DOT = ( 1<<2 ), /**< Warning: key contains '.' character. */
BSON_FIELD_INIT_DOLLAR = ( 1<<3 ), /**< Warning: key starts with '$' character. */
BSON_ALREADY_FINISHED = ( 1<<4 ) /**< Trying to modify a finished BSON object. */
};
enum bson_binary_subtype_t {
BSON_BIN_BINARY = 0,
BSON_BIN_FUNC = 1,
BSON_BIN_BINARY_OLD = 2,
BSON_BIN_UUID = 3,
BSON_BIN_MD5 = 5,
BSON_BIN_USER = 128
};
typedef enum {
BSON_EOO = 0,
BSON_DOUBLE = 1,
BSON_STRING = 2,
BSON_OBJECT = 3,
BSON_ARRAY = 4,
BSON_BINDATA = 5,
BSON_UNDEFINED = 6,
BSON_OID = 7,
BSON_BOOL = 8,
BSON_DATE = 9,
BSON_NULL = 10,
BSON_REGEX = 11,
BSON_DBREF = 12, /**< Deprecated. */
BSON_CODE = 13,
BSON_SYMBOL = 14,
BSON_CODEWSCOPE = 15,
BSON_INT = 16,
BSON_TIMESTAMP = 17,
BSON_LONG = 18
} bson_type;
typedef int bson_bool_t;
typedef struct {
const char *cur;
bson_bool_t first;
} bson_iterator;
typedef struct {
char *data;
char *cur;
int dataSize;
bson_bool_t finished;
int stack[32];
int stackPos;
int err; /**< Bitfield representing errors or warnings on this buffer */
char *errstr; /**< A string representation of the most recent error or warning. */
} bson;
#pragma pack(1)
typedef union {
char bytes[12];
int ints[3];
} bson_oid_t;
#pragma pack()
typedef int64_t bson_date_t; /* milliseconds since epoch UTC */
typedef struct {
int i; /* increment */
int t; /* time in seconds */
} bson_timestamp_t;
/* ----------------------------
READING
------------------------------ */
/**
* Size of a BSON object.
*
* @param b the BSON object.
*
* @return the size.
*/
int bson_size( const bson *b );
/**
* Print a string representation of a BSON object.
*
* @param b the BSON object to print.
*/
void bson_print( bson *b );
/**
* Return a pointer to the raw buffer stored by this bson object.
*
* @param b a BSON object
*/
const char *bson_data( bson *b );
/**
* Print a string representation of a BSON object.
*
* @param bson the raw data to print.
* @param depth the depth to recurse the object.x
*/
void bson_print_raw( const char *bson , int depth );
/**
* Advance a bson_iterator to the named field.
*
* @param it the bson_iterator to use.
* @param obj the BSON object to use.
* @param name the name of the field to find.
*
* @return the type of the found object or BSON_EOO if it is not found.
*/
bson_type bson_find( bson_iterator *it, const bson *obj, const char *name );
/**
* Initialize a bson_iterator.
*
* @param i the bson_iterator to initialize.
* @param bson the BSON object to associate with the iterator.
*/
void bson_iterator_init( bson_iterator *i , const bson *b );
/**
* Initialize a bson iterator from a const char* buffer. Note
* that this is mostly used internally.
*
* @param i the bson_iterator to initialize.
* @param buffer the buffer to point to.
*/
void bson_iterator_from_buffer( bson_iterator *i, const char *buffer );
/* more returns true for eoo. best to loop with bson_iterator_next(&it) */
/**
* Check to see if the bson_iterator has more data.
*
* @param i the iterator.
*
* @return returns true if there is more data.
*/
bson_bool_t bson_iterator_more( const bson_iterator *i );
/**
* Point the iterator at the next BSON object.
*
* @param i the bson_iterator.
*
* @return the type of the next BSON object.
*/
bson_type bson_iterator_next( bson_iterator *i );
/**
* Get the type of the BSON object currently pointed to by the iterator.
*
* @param i the bson_iterator
*
* @return the type of the current BSON object.
*/
bson_type bson_iterator_type( const bson_iterator *i );
/**
* Get the key of the BSON object currently pointed to by the iterator.
*
* @param i the bson_iterator
*
* @return the key of the current BSON object.
*/
const char *bson_iterator_key( const bson_iterator *i );
/**
* Get the value of the BSON object currently pointed to by the iterator.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
const char *bson_iterator_value( const bson_iterator *i );
/* these convert to the right type (return 0 if non-numeric) */
/**
* Get the double value of the BSON object currently pointed to by the
* iterator.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
double bson_iterator_double( const bson_iterator *i );
/**
* Get the int value of the BSON object currently pointed to by the iterator.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
int bson_iterator_int( const bson_iterator *i );
/**
* Get the long value of the BSON object currently pointed to by the iterator.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
int64_t bson_iterator_long( const bson_iterator *i );
/* return the bson timestamp as a whole or in parts */
/**
* Get the timestamp value of the BSON object currently pointed to by
* the iterator.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i );
/**
* Get the boolean value of the BSON object currently pointed to by
* the iterator.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
/* false: boolean false, 0 in any type, or null */
/* true: anything else (even empty strings and objects) */
bson_bool_t bson_iterator_bool( const bson_iterator *i );
/**
* Get the double value of the BSON object currently pointed to by the
* iterator. Assumes the correct type is used.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
/* these assume you are using the right type */
double bson_iterator_double_raw( const bson_iterator *i );
/**
* Get the int value of the BSON object currently pointed to by the
* iterator. Assumes the correct type is used.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
int bson_iterator_int_raw( const bson_iterator *i );
/**
* Get the long value of the BSON object currently pointed to by the
* iterator. Assumes the correct type is used.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
int64_t bson_iterator_long_raw( const bson_iterator *i );
/**
* Get the bson_bool_t value of the BSON object currently pointed to by the
* iterator. Assumes the correct type is used.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
bson_bool_t bson_iterator_bool_raw( const bson_iterator *i );
/**
* Get the bson_oid_t value of the BSON object currently pointed to by the
* iterator.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
bson_oid_t *bson_iterator_oid( const bson_iterator *i );
/**
* Get the string value of the BSON object currently pointed to by the
* iterator.
*
* @param i the bson_iterator
*
* @return the value of the current BSON object.
*/
/* these can also be used with bson_code and bson_symbol*/
const char *bson_iterator_string( const bson_iterator *i );
/**
* Get the string length of the BSON object currently pointed to by the
* iterator.
*
* @param i the bson_iterator
*
* @return the length of the current BSON object.
*/
int bson_iterator_string_len( const bson_iterator *i );
/**
* Get the code value of the BSON object currently pointed to by the
* iterator. Works with bson_code, bson_codewscope, and BSON_STRING
* returns NULL for everything else.
*
* @param i the bson_iterator
*
* @return the code value of the current BSON object.
*/
/* works with bson_code, bson_codewscope, and BSON_STRING */
/* returns NULL for everything else */
const char *bson_iterator_code( const bson_iterator *i );
/**
* Calls bson_empty on scope if not a bson_codewscope
*
* @param i the bson_iterator.
* @param scope the bson scope.
*/
/* calls bson_empty on scope if not a bson_codewscope */
void bson_iterator_code_scope( const bson_iterator *i, bson *scope );
/**
* Get the date value of the BSON object currently pointed to by the
* iterator.
*
* @param i the bson_iterator
*
* @return the date value of the current BSON object.
*/
/* both of these only work with bson_date */
bson_date_t bson_iterator_date( const bson_iterator *i );
/**
* Get the time value of the BSON object currently pointed to by the
* iterator.
*
* @param i the bson_iterator
*
* @return the time value of the current BSON object.
*/
time_t bson_iterator_time_t( const bson_iterator *i );
/**
* Get the length of the BSON binary object currently pointed to by the
* iterator.
*
* @param i the bson_iterator
*
* @return the length of the current BSON binary object.
*/
int bson_iterator_bin_len( const bson_iterator *i );
/**
* Get the type of the BSON binary object currently pointed to by the
* iterator.
*
* @param i the bson_iterator
*
* @return the type of the current BSON binary object.
*/
char bson_iterator_bin_type( const bson_iterator *i );
/**
* Get the value of the BSON binary object currently pointed to by the
* iterator.
*
* @param i the bson_iterator
*
* @return the value of the current BSON binary object.
*/
const char *bson_iterator_bin_data( const bson_iterator *i );
/**
* Get the value of the BSON regex object currently pointed to by the
* iterator.
*
* @param i the bson_iterator
*
* @return the value of the current BSON regex object.
*/
const char *bson_iterator_regex( const bson_iterator *i );
/**
* Get the options of the BSON regex object currently pointed to by the
* iterator.
*
* @param i the bson_iterator.
*
* @return the options of the current BSON regex object.
*/
const char *bson_iterator_regex_opts( const bson_iterator *i );
/* these work with BSON_OBJECT and BSON_ARRAY */
/**
* Get the BSON subobject currently pointed to by the
* iterator.
*
* @param i the bson_iterator.
* @param sub the BSON subobject destination.
*/
void bson_iterator_subobject( const bson_iterator *i, bson *sub );
/**
* Get a bson_iterator that on the BSON subobject.
*
* @param i the bson_iterator.
* @param sub the iterator to point at the BSON subobject.
*/
void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub );
/* str must be at least 24 hex chars + null byte */
/**
* Create a bson_oid_t from a string.
*
* @param oid the bson_oid_t destination.
* @param str a null terminated string comprised of at least 24 hex chars.
*/
void bson_oid_from_string( bson_oid_t *oid, const char *str );
/**
* Create a string representation of the bson_oid_t.
*
* @param oid the bson_oid_t source.
* @param str the string representation destination.
*/
void bson_oid_to_string( const bson_oid_t *oid, char *str );
/**
* Create a bson_oid object.
*
* @param oid the destination for the newly created bson_oid_t.
*/
void bson_oid_gen( bson_oid_t *oid );
/**
* Set a function to be used to generate the second four bytes
* of an object id.
*
* @param func a pointer to a function that returns an int.
*/
void bson_set_oid_fuzz( int ( *func )( void ) );
/**
* Set a function to be used to generate the incrementing part
* of an object id (last four bytes). If you need thread-safety
* in generating object ids, you should set this function.
*
* @param func a pointer to a function that returns an int.
*/
void bson_set_oid_inc( int ( *func )( void ) );
/**
* Get the time a bson_oid_t was created.
*
* @param oid the bson_oid_t.
*/
time_t bson_oid_generated_time( bson_oid_t *oid ); /* Gives the time the OID was created */
/* ----------------------------
BUILDING
------------------------------ */
/**
* Initialize a new bson object. If not created
* with bson_new, you must initialize each new bson
* object using this function.
*
* @note When finished, you must pass the bson object to
* bson_destroy( ).
*/
void bson_init( bson *b );
/**
* Initialize a BSON object, and point its data
* pointer to the provided char*.
*
* @param b the BSON object to initialize.
* @param data the raw BSON data.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_init_data( bson *b , char *data );
/**
* Initialize a BSON object, and set its
* buffer to the given size.
*
* @param b the BSON object to initialize.
* @param size the initial size of the buffer.
*
* @return BSON_OK or BSON_ERROR.
*/
void bson_init_size( bson *b, int size );
/**
* Grow a bson object.
*
* @param b the bson to grow.
* @param bytesNeeded the additional number of bytes needed.
*
* @return BSON_OK or BSON_ERROR with the bson error object set.
* Exits if allocation fails.
*/
int bson_ensure_space( bson *b, const int bytesNeeded );
/**
* Finalize a bson object.
*
* @param b the bson object to finalize.
*
* @return the standard error code. To deallocate memory,
* call bson_destroy on the bson object.
*/
int bson_finish( bson *b );
/**
* Destroy a bson object.
*
* @param b the bson object to destroy.
*
*/
void bson_destroy( bson *b );
/**
* Returns a pointer to a static empty BSON object.
*
* @param obj the BSON object to initialize.
*
* @return the empty initialized BSON object.
*/
/* returns pointer to static empty bson object */
bson *bson_empty( bson *obj );
/**
* Copy BSON data only from one object to another.
*
* @param out the copy destination BSON object.
* @param in the copy source BSON object.
*/
void bson_copy_basic( bson *out, const bson *in );
/**
* Make a complete copy of the a BSON object.
*
* @param out the copy destination BSON object.
* @param in the copy source BSON object.
*/
void bson_copy( bson *out, const bson *in ); /* puts data in new buffer. NOOP if out==NULL */
/**
* Append a previously created bson_oid_t to a bson object.
*
* @param b the bson to append to.
* @param name the key for the bson_oid_t.
* @param oid the bson_oid_t to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid );
/**
* Append a bson_oid_t to a bson.
*
* @param b the bson to append to.
* @param name the key for the bson_oid_t.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_new_oid( bson *b, const char *name );
/**
* Append an int to a bson.
*
* @param b the bson to append to.
* @param name the key for the int.
* @param i the int to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_int( bson *b, const char *name, const int i );
/**
* Append an long to a bson.
*
* @param b the bson to append to.
* @param name the key for the long.
* @param i the long to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_long( bson *b, const char *name, const int64_t i );
/**
* Append an double to a bson.
*
* @param b the bson to append to.
* @param name the key for the double.
* @param d the double to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_double( bson *b, const char *name, const double d );
/**
* Append a string to a bson.
*
* @param b the bson to append to.
* @param name the key for the string.
* @param str the string to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_string( bson *b, const char *name, const char *str );
/**
* Append len bytes of a string to a bson.
*
* @param b the bson to append to.
* @param name the key for the string.
* @param str the string to append.
* @param len the number of bytes from str to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_string_n( bson *b, const char *name, const char *str, int len );
/**
* Append a symbol to a bson.
*
* @param b the bson to append to.
* @param name the key for the symbol.
* @param str the symbol to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_symbol( bson *b, const char *name, const char *str );
/**
* Append len bytes of a symbol to a bson.
*
* @param b the bson to append to.
* @param name the key for the symbol.
* @param str the symbol to append.
* @param len the number of bytes from str to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_symbol_n( bson *b, const char *name, const char *str, int len );
/**
* Append code to a bson.
*
* @param b the bson to append to.
* @param name the key for the code.
* @param str the code to append.
* @param len the number of bytes from str to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_code( bson *b, const char *name, const char *str );
/**
* Append len bytes of code to a bson.
*
* @param b the bson to append to.
* @param name the key for the code.
* @param str the code to append.
* @param len the number of bytes from str to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_code_n( bson *b, const char *name, const char *str, int len );
/**
* Append code to a bson with scope.
*
* @param b the bson to append to.
* @param name the key for the code.
* @param str the string to append.
* @param scope a BSON object containing the scope.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope );
/**
* Append len bytes of code to a bson with scope.
*
* @param b the bson to append to.
* @param name the key for the code.
* @param str the string to append.
* @param len the number of bytes from str to append.
* @param scope a BSON object containing the scope.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_code_w_scope_n( bson *b, const char *name, const char *code, int size, const bson *scope );
/**
* Append binary data to a bson.
*
* @param b the bson to append to.
* @param name the key for the data.
* @param type the binary data type.
* @param str the binary data.
* @param len the length of the data.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_binary( bson *b, const char *name, char type, const char *str, int len );
/**
* Append a bson_bool_t to a bson.
*
* @param b the bson to append to.
* @param name the key for the boolean value.
* @param v the bson_bool_t to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_bool( bson *b, const char *name, const bson_bool_t v );
/**
* Append a null value to a bson.
*
* @param b the bson to append to.
* @param name the key for the null value.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_null( bson *b, const char *name );
/**
* Append an undefined value to a bson.
*
* @param b the bson to append to.
* @param name the key for the undefined value.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_undefined( bson *b, const char *name );
/**
* Append a regex value to a bson.
*
* @param b the bson to append to.
* @param name the key for the regex value.
* @param pattern the regex pattern to append.
* @param the regex options.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts );
/**
* Append bson data to a bson.
*
* @param b the bson to append to.
* @param name the key for the bson data.
* @param bson the bson object to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_bson( bson *b, const char *name, const bson *bson );
/**
* Append a BSON element to a bson from the current point of an iterator.
*
* @param b the bson to append to.
* @param name_or_null the key for the BSON element, or NULL.
* @param elem the bson_iterator.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem );
/**
* Append a bson_timestamp_t value to a bson.
*
* @param b the bson to append to.
* @param name the key for the timestampe value.
* @param ts the bson_timestamp_t value to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts );
/* these both append a bson_date */
/**
* Append a bson_date_t value to a bson.
*
* @param b the bson to append to.
* @param name the key for the date value.
* @param millis the bson_date_t to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_date( bson *b, const char *name, bson_date_t millis );
/**
* Append a time_t value to a bson.
*
* @param b the bson to append to.
* @param name the key for the date value.
* @param secs the time_t to append.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_time_t( bson *b, const char *name, time_t secs );
/**
* Start appending a new object to a bson.
*
* @param b the bson to append to.
* @param name the name of the new object.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_start_object( bson *b, const char *name );
/**
* Start appending a new array to a bson.
*
* @param b the bson to append to.
* @param name the name of the new array.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_start_array( bson *b, const char *name );
/**
* Finish appending a new object or array to a bson.
*
* @param b the bson to append to.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_finish_object( bson *b );
/**
* Finish appending a new object or array to a bson. This
* is simply an alias for bson_append_finish_object.
*
* @param b the bson to append to.
*
* @return BSON_OK or BSON_ERROR.
*/
int bson_append_finish_array( bson *b );
void bson_numstr( char *str, int i );
void bson_incnumstr( char *str );
/* Error handling and stadard library function over-riding. */
/* -------------------------------------------------------- */
/* bson_err_handlers shouldn't return!!! */
typedef void( *bson_err_handler )( const char *errmsg );
typedef int (*bson_printf_func)( const char *, ... );
typedef int (*bson_fprintf_func)( FILE *, const char *, ... );
typedef int (*bson_sprintf_func)( char *, const char *, ... );
extern void *( *bson_malloc_func )( size_t );
extern void *( *bson_realloc_func )( void *, size_t );
extern void ( *bson_free )( void * );
extern bson_printf_func bson_printf;
extern bson_fprintf_func bson_fprintf;
extern bson_sprintf_func bson_sprintf;
extern bson_printf_func bson_errprintf;
/**
* Allocates memory and checks return value, exiting fatally if malloc() fails.
*
* @param size bytes to allocate.
*
* @return a pointer to the allocated memory.
*
* @sa malloc(3)
*/
void *bson_malloc( int size );
/**
* Changes the size of allocated memory and checks return value,
* exiting fatally if realloc() fails.
*
* @param ptr pointer to the space to reallocate.
* @param size bytes to allocate.
*
* @return a pointer to the allocated memory.
*
* @sa realloc()
*/
void *bson_realloc( void *ptr, int size );
/**
* Set a function for error handling.
*
* @param func a bson_err_handler function.
*
* @return the old error handling function, or NULL.
*/
bson_err_handler set_bson_err_handler( bson_err_handler func );
/* does nothing if ok != 0 */
/**
* Exit fatally.
*
* @param ok exits if ok is equal to 0.
*/
void bson_fatal( int ok );
/**
* Exit fatally with an error message.
*
* @param ok exits if ok is equal to 0.
* @param msg prints to stderr before exiting.
*/
void bson_fatal_msg( int ok, const char *msg );
/**
* Invoke the error handler, but do not exit.
*
* @param b the buffer object.
*/
void bson_builder_error( bson *b );
MONGO_EXTERN_C_END
#endif

View File

@ -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 );
}

View File

@ -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

View File

@ -0,0 +1,685 @@
/* gridfs.c */
/* Copyright 2009-2011 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "gridfs.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
static bson *chunk_new( bson_oid_t id, int chunkNumber,
const char *data, int len ) {
bson *b = bson_malloc( sizeof( bson ) );
bson_init( b );
bson_append_oid( b, "files_id", &id );
bson_append_int( b, "n", chunkNumber );
bson_append_binary( b, "data", BSON_BIN_BINARY, data, len );
bson_finish( b );
return b;
}
static void chunk_free( bson *oChunk ) {
bson_destroy( oChunk );
bson_free( oChunk );
}
int gridfs_init( mongo *client, const char *dbname, const char *prefix,
gridfs *gfs ) {
int options;
bson b;
bson_bool_t success;
gfs->client = client;
/* Allocate space to own the dbname */
gfs->dbname = ( const char * )bson_malloc( strlen( dbname )+1 );
strcpy( ( char * )gfs->dbname, dbname );
/* Allocate space to own the prefix */
if ( prefix == NULL ) prefix = "fs";
gfs->prefix = ( const char * )bson_malloc( strlen( prefix )+1 );
strcpy( ( char * )gfs->prefix, prefix );
/* Allocate space to own files_ns */
gfs->files_ns =
( const char * ) bson_malloc ( strlen( prefix )+strlen( dbname )+strlen( ".files" )+2 );
strcpy( ( char * )gfs->files_ns, dbname );
strcat( ( char * )gfs->files_ns, "." );
strcat( ( char * )gfs->files_ns, prefix );
strcat( ( char * )gfs->files_ns, ".files" );
/* Allocate space to own chunks_ns */
gfs->chunks_ns = ( const char * ) bson_malloc( strlen( prefix ) + strlen( dbname )
+ strlen( ".chunks" ) + 2 );
strcpy( ( char * )gfs->chunks_ns, dbname );
strcat( ( char * )gfs->chunks_ns, "." );
strcat( ( char * )gfs->chunks_ns, prefix );
strcat( ( char * )gfs->chunks_ns, ".chunks" );
bson_init( &b );
bson_append_int( &b, "filename", 1 );
bson_finish( &b );
options = 0;
success = ( mongo_create_index( gfs->client, gfs->files_ns, &b, options, NULL ) == MONGO_OK );
bson_destroy( &b );
if ( !success ) {
bson_free( ( char * )gfs->dbname );
bson_free( ( char * )gfs->prefix );
bson_free( ( char * )gfs->files_ns );
bson_free( ( char * )gfs->chunks_ns );
return MONGO_ERROR;
}
bson_init( &b );
bson_append_int( &b, "files_id", 1 );
bson_append_int( &b, "n", 1 );
bson_finish( &b );
options = MONGO_INDEX_UNIQUE;
success = ( mongo_create_index( gfs->client, gfs->chunks_ns, &b, options, NULL ) == MONGO_OK );
bson_destroy( &b );
if ( !success ) {
bson_free( ( char * )gfs->dbname );
bson_free( ( char * )gfs->prefix );
bson_free( ( char * )gfs->files_ns );
bson_free( ( char * )gfs->chunks_ns );
return MONGO_ERROR;
}
return MONGO_OK;
}
void gridfs_destroy( gridfs *gfs ) {
if ( gfs == NULL ) return;
if ( gfs->dbname ) bson_free( ( char * )gfs->dbname );
if ( gfs->prefix ) bson_free( ( char * )gfs->prefix );
if ( gfs->files_ns ) bson_free( ( char * )gfs->files_ns );
if ( gfs->chunks_ns ) bson_free( ( char * )gfs->chunks_ns );
}
static int gridfs_insert_file( gridfs *gfs, const char *name,
const bson_oid_t id, gridfs_offset length,
const char *contenttype ) {
bson command;
bson ret;
bson res;
bson_iterator it;
int result;
/* Check run md5 */
bson_init( &command );
bson_append_oid( &command, "filemd5", &id );
bson_append_string( &command, "root", gfs->prefix );
bson_finish( &command );
assert( mongo_run_command( gfs->client, gfs->dbname, &command, &res ) == MONGO_OK );
bson_destroy( &command );
/* Create and insert BSON for file metadata */
bson_init( &ret );
bson_append_oid( &ret, "_id", &id );
if ( name != NULL && *name != '\0' ) {
bson_append_string( &ret, "filename", name );
}
bson_append_long( &ret, "length", length );
bson_append_int( &ret, "chunkSize", DEFAULT_CHUNK_SIZE );
bson_append_date( &ret, "uploadDate", ( bson_date_t )1000*time( NULL ) );
bson_find( &it, &res, "md5" );
bson_append_string( &ret, "md5", bson_iterator_string( &it ) );
bson_destroy( &res );
if ( contenttype != NULL && *contenttype != '\0' ) {
bson_append_string( &ret, "contentType", contenttype );
}
bson_finish( &ret );
result = mongo_insert( gfs->client, gfs->files_ns, &ret );
bson_destroy( &ret );
return result;
}
int gridfs_store_buffer( gridfs *gfs, const char *data,
gridfs_offset length, const char *remotename,
const char *contenttype ) {
char const *end = data + length;
const char *data_ptr = data;
bson_oid_t id;
int chunkNumber = 0;
int chunkLen;
bson *oChunk;
/* Large files Assertion */
assert( length <= 0xffffffff );
/* Generate and append an oid*/
bson_oid_gen( &id );
/* Insert the file's data chunk by chunk */
while ( data_ptr < end ) {
chunkLen = DEFAULT_CHUNK_SIZE < ( unsigned int )( end - data_ptr ) ?
DEFAULT_CHUNK_SIZE : ( unsigned int )( end - data_ptr );
oChunk = chunk_new( id, chunkNumber, data_ptr, chunkLen );
mongo_insert( gfs->client, gfs->chunks_ns, oChunk );
chunk_free( oChunk );
chunkNumber++;
data_ptr += chunkLen;
}
/* Inserts file's metadata */
return gridfs_insert_file( gfs, remotename, id, length, contenttype );
}
void gridfile_writer_init( gridfile *gfile, gridfs *gfs,
const char *remote_name, const char *content_type ) {
gfile->gfs = gfs;
bson_oid_gen( &( gfile->id ) );
gfile->chunk_num = 0;
gfile->length = 0;
gfile->pending_len = 0;
gfile->pending_data = NULL;
gfile->remote_name = ( char * )bson_malloc( strlen( remote_name ) + 1 );
strcpy( ( char * )gfile->remote_name, remote_name );
gfile->content_type = ( char * )bson_malloc( strlen( content_type ) + 1 );
strcpy( ( char * )gfile->content_type, content_type );
}
void gridfile_write_buffer( gridfile *gfile, const char *data,
gridfs_offset length ) {
int bytes_left = 0;
int data_partial_len = 0;
int chunks_to_write = 0;
char *buffer;
bson *oChunk;
gridfs_offset to_write = length + gfile->pending_len;
if ( to_write < DEFAULT_CHUNK_SIZE ) { /* Less than one chunk to write */
if( gfile->pending_data ) {
gfile->pending_data = ( char * )bson_realloc( ( void * )gfile->pending_data, gfile->pending_len + to_write );
memcpy( gfile->pending_data + gfile->pending_len, data, length );
} else if ( to_write > 0 ) {
gfile->pending_data = ( char * )bson_malloc( to_write );
memcpy( gfile->pending_data, data, length );
}
gfile->pending_len += length;
} else { /* At least one chunk of data to write */
/* If there's a pending chunk to be written, we need to combine
* the buffer provided up to DEFAULT_CHUNK_SIZE.
*/
if ( gfile->pending_len > 0 ) {
chunks_to_write = to_write / DEFAULT_CHUNK_SIZE;
bytes_left = to_write % DEFAULT_CHUNK_SIZE;
data_partial_len = DEFAULT_CHUNK_SIZE - gfile->pending_len;
buffer = ( char * )bson_malloc( DEFAULT_CHUNK_SIZE );
memcpy( buffer, gfile->pending_data, gfile->pending_len );
memcpy( buffer + gfile->pending_len, data, data_partial_len );
oChunk = chunk_new( gfile->id, gfile->chunk_num, buffer, DEFAULT_CHUNK_SIZE );
mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
chunk_free( oChunk );
gfile->chunk_num++;
gfile->length += DEFAULT_CHUNK_SIZE;
data += data_partial_len;
chunks_to_write--;
bson_free( buffer );
}
while( chunks_to_write > 0 ) {
oChunk = chunk_new( gfile->id, gfile->chunk_num, data, DEFAULT_CHUNK_SIZE );
mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
chunk_free( oChunk );
gfile->chunk_num++;
chunks_to_write--;
gfile->length += DEFAULT_CHUNK_SIZE;
data += DEFAULT_CHUNK_SIZE;
}
bson_free( gfile->pending_data );
/* If there are any leftover bytes, store them as pending data. */
if( bytes_left == 0 )
gfile->pending_data = NULL;
else {
gfile->pending_data = ( char * )bson_malloc( bytes_left );
memcpy( gfile->pending_data, data, bytes_left );
}
gfile->pending_len = bytes_left;
}
}
int gridfile_writer_done( gridfile *gfile ) {
/* write any remaining pending chunk data.
* pending data will always take up less than one chunk */
bson *oChunk;
int response;
if( gfile->pending_data ) {
oChunk = chunk_new( gfile->id, gfile->chunk_num, gfile->pending_data, gfile->pending_len );
mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk );
chunk_free( oChunk );
bson_free( gfile->pending_data );
gfile->length += gfile->pending_len;
}
/* insert into files collection */
response = gridfs_insert_file( gfile->gfs, gfile->remote_name, gfile->id,
gfile->length, gfile->content_type );
bson_free( gfile->remote_name );
bson_free( gfile->content_type );
return response;
}
int gridfs_store_file( gridfs *gfs, const char *filename,
const char *remotename, const char *contenttype ) {
char buffer[DEFAULT_CHUNK_SIZE];
FILE *fd;
bson_oid_t id;
int chunkNumber = 0;
gridfs_offset length = 0;
gridfs_offset chunkLen = 0;
bson *oChunk;
/* Open the file and the correct stream */
if ( strcmp( filename, "-" ) == 0 ) fd = stdin;
else fd = fopen( filename, "rb" );
assert( fd != NULL ); /* No such file */
/* Generate and append an oid*/
bson_oid_gen( &id );
/* Insert the file chunk by chunk */
chunkLen = fread( buffer, 1, DEFAULT_CHUNK_SIZE, fd );
do {
oChunk = chunk_new( id, chunkNumber, buffer, chunkLen );
mongo_insert( gfs->client, gfs->chunks_ns, oChunk );
chunk_free( oChunk );
length += chunkLen;
chunkNumber++;
chunkLen = fread( buffer, 1, DEFAULT_CHUNK_SIZE, fd );
} while ( chunkLen != 0 );
/* Close the file stream */
if ( fd != stdin ) fclose( fd );
/* Large files Assertion */
/* assert(length <= 0xffffffff); */
/* Optional Remote Name */
if ( remotename == NULL || *remotename == '\0' ) {
remotename = filename;
}
/* Inserts file's metadata */
return gridfs_insert_file( gfs, remotename, id, length, contenttype );
}
void gridfs_remove_filename( gridfs *gfs, const char *filename ) {
bson query;
mongo_cursor *files;
bson file;
bson_iterator it;
bson_oid_t id;
bson b;
bson_init( &query );
bson_append_string( &query, "filename", filename );
bson_finish( &query );
files = mongo_find( gfs->client, gfs->files_ns, &query, NULL, 0, 0, 0 );
bson_destroy( &query );
/* Remove each file and it's chunks from files named filename */
while ( mongo_cursor_next( files ) == MONGO_OK ) {
file = files->current;
bson_find( &it, &file, "_id" );
id = *bson_iterator_oid( &it );
/* Remove the file with the specified id */
bson_init( &b );
bson_append_oid( &b, "_id", &id );
bson_finish( &b );
mongo_remove( gfs->client, gfs->files_ns, &b );
bson_destroy( &b );
/* Remove all chunks from the file with the specified id */
bson_init( &b );
bson_append_oid( &b, "files_id", &id );
bson_finish( &b );
mongo_remove( gfs->client, gfs->chunks_ns, &b );
bson_destroy( &b );
}
mongo_cursor_destroy( files );
}
int gridfs_find_query( gridfs *gfs, bson *query,
gridfile *gfile ) {
bson uploadDate;
bson finalQuery;
bson out;
int i;
bson_init( &uploadDate );
bson_append_int( &uploadDate, "uploadDate", -1 );
bson_finish( &uploadDate );
bson_init( &finalQuery );
bson_append_bson( &finalQuery, "query", query );
bson_append_bson( &finalQuery, "orderby", &uploadDate );
bson_finish( &finalQuery );
i = ( mongo_find_one( gfs->client, gfs->files_ns,
&finalQuery, NULL, &out ) == MONGO_OK );
bson_destroy( &uploadDate );
bson_destroy( &finalQuery );
if ( !i )
return MONGO_ERROR;
else {
gridfile_init( gfs, &out, gfile );
bson_destroy( &out );
return MONGO_OK;
}
}
int gridfs_find_filename( gridfs *gfs, const char *filename,
gridfile *gfile )
{
bson query;
int i;
bson_init( &query );
bson_append_string( &query, "filename", filename );
bson_finish( &query );
i = gridfs_find_query( gfs, &query, gfile );
bson_destroy( &query );
return i;
}
int gridfile_init( gridfs *gfs, bson *meta, gridfile *gfile )
{
gfile->gfs = gfs;
gfile->pos = 0;
gfile->meta = ( bson * )bson_malloc( sizeof( bson ) );
if ( gfile->meta == NULL ) return MONGO_ERROR;
bson_copy( gfile->meta, meta );
return MONGO_OK;
}
void gridfile_destroy( gridfile *gfile )
{
bson_destroy( gfile->meta );
bson_free( gfile->meta );
}
bson_bool_t gridfile_exists( gridfile *gfile ) {
return ( bson_bool_t )( gfile != NULL || gfile->meta == NULL );
}
const char *gridfile_get_filename( gridfile *gfile ) {
bson_iterator it;
bson_find( &it, gfile->meta, "filename" );
return bson_iterator_string( &it );
}
int gridfile_get_chunksize( gridfile *gfile ) {
bson_iterator it;
bson_find( &it, gfile->meta, "chunkSize" );
return bson_iterator_int( &it );
}
gridfs_offset gridfile_get_contentlength( gridfile *gfile ) {
bson_iterator it;
bson_find( &it, gfile->meta, "length" );
if( bson_iterator_type( &it ) == BSON_INT )
return ( gridfs_offset )bson_iterator_int( &it );
else
return ( gridfs_offset )bson_iterator_long( &it );
}
const char *gridfile_get_contenttype( gridfile *gfile ) {
bson_iterator it;
if ( bson_find( &it, gfile->meta, "contentType" ) )
return bson_iterator_string( &it );
else return NULL;
}
bson_date_t gridfile_get_uploaddate( gridfile *gfile ) {
bson_iterator it;
bson_find( &it, gfile->meta, "uploadDate" );
return bson_iterator_date( &it );
}
const char *gridfile_get_md5( gridfile *gfile ) {
bson_iterator it;
bson_find( &it, gfile->meta, "md5" );
return bson_iterator_string( &it );
}
const char *gridfile_get_field( gridfile *gfile, const char *name ) {
bson_iterator it;
bson_find( &it, gfile->meta, name );
return bson_iterator_value( &it );
}
bson_bool_t gridfile_get_boolean( gridfile *gfile, const char *name ) {
bson_iterator it;
bson_find( &it, gfile->meta, name );
return bson_iterator_bool( &it );
}
bson gridfile_get_metadata( gridfile *gfile ) {
bson sub;
bson_iterator it;
if ( bson_find( &it, gfile->meta, "metadata" ) ) {
bson_iterator_subobject( &it, &sub );
return sub;
} else {
bson_empty( &sub );
return sub;
}
}
int gridfile_get_numchunks( gridfile *gfile ) {
bson_iterator it;
gridfs_offset length;
gridfs_offset chunkSize;
double numchunks;
bson_find( &it, gfile->meta, "length" );
if( bson_iterator_type( &it ) == BSON_INT )
length = ( gridfs_offset )bson_iterator_int( &it );
else
length = ( gridfs_offset )bson_iterator_long( &it );
bson_find( &it, gfile->meta, "chunkSize" );
chunkSize = bson_iterator_int( &it );
numchunks = ( ( double )length/( double )chunkSize );
return ( numchunks - ( int )numchunks > 0 )
? ( int )( numchunks+1 )
: ( int )( numchunks );
}
bson gridfile_get_chunk( gridfile *gfile, int n ) {
bson query;
bson out;
bson_iterator it;
bson_oid_t id;
bson_init( &query );
bson_find( &it, gfile->meta, "_id" );
id = *bson_iterator_oid( &it );
bson_append_oid( &query, "files_id", &id );
bson_append_int( &query, "n", n );
bson_finish( &query );
assert( mongo_find_one( gfile->gfs->client,
gfile->gfs->chunks_ns,
&query, NULL, &out ) == MONGO_OK );
bson_destroy( &query );
return out;
}
mongo_cursor *gridfile_get_chunks( gridfile *gfile, int start, int size ) {
bson_iterator it;
bson_oid_t id;
bson gte;
bson query;
bson orderby;
bson command;
mongo_cursor *cursor;
bson_find( &it, gfile->meta, "_id" );
id = *bson_iterator_oid( &it );
bson_init( &query );
bson_append_oid( &query, "files_id", &id );
if ( size == 1 ) {
bson_append_int( &query, "n", start );
} else {
bson_init( &gte );
bson_append_int( &gte, "$gte", start );
bson_finish( &gte );
bson_append_bson( &query, "n", &gte );
bson_destroy( &gte );
}
bson_finish( &query );
bson_init( &orderby );
bson_append_int( &orderby, "n", 1 );
bson_finish( &orderby );
bson_init( &command );
bson_append_bson( &command, "query", &query );
bson_append_bson( &command, "orderby", &orderby );
bson_finish( &command );
cursor = mongo_find( gfile->gfs->client, gfile->gfs->chunks_ns,
&command, NULL, size, 0, 0 );
bson_destroy( &command );
bson_destroy( &query );
bson_destroy( &orderby );
return cursor;
}
gridfs_offset gridfile_write_file( gridfile *gfile, FILE *stream ) {
int i;
size_t len;
bson chunk;
bson_iterator it;
const char *data;
const int num = gridfile_get_numchunks( gfile );
for ( i=0; i<num; i++ ) {
chunk = gridfile_get_chunk( gfile, i );
bson_find( &it, &chunk, "data" );
len = bson_iterator_bin_len( &it );
data = bson_iterator_bin_data( &it );
fwrite( data , sizeof( char ), len, stream );
bson_destroy( &chunk );
}
return gridfile_get_contentlength( gfile );
}
gridfs_offset gridfile_read( gridfile *gfile, gridfs_offset size, char *buf ) {
mongo_cursor *chunks;
bson chunk;
int first_chunk;
int last_chunk;
int total_chunks;
gridfs_offset chunksize;
gridfs_offset contentlength;
gridfs_offset bytes_left;
int i;
bson_iterator it;
gridfs_offset chunk_len;
const char *chunk_data;
contentlength = gridfile_get_contentlength( gfile );
chunksize = gridfile_get_chunksize( gfile );
size = ( contentlength - gfile->pos < size )
? contentlength - gfile->pos
: size;
bytes_left = size;
first_chunk = ( gfile->pos )/chunksize;
last_chunk = ( gfile->pos+size-1 )/chunksize;
total_chunks = last_chunk - first_chunk + 1;
chunks = gridfile_get_chunks( gfile, first_chunk, total_chunks );
for ( i = 0; i < total_chunks; i++ ) {
mongo_cursor_next( chunks );
chunk = chunks->current;
bson_find( &it, &chunk, "data" );
chunk_len = bson_iterator_bin_len( &it );
chunk_data = bson_iterator_bin_data( &it );
if ( i == 0 ) {
chunk_data += ( gfile->pos )%chunksize;
chunk_len -= ( gfile->pos )%chunksize;
}
if ( bytes_left > chunk_len ) {
memcpy( buf, chunk_data, chunk_len );
bytes_left -= chunk_len;
buf += chunk_len;
} else {
memcpy( buf, chunk_data, bytes_left );
}
}
mongo_cursor_destroy( chunks );
gfile->pos = gfile->pos + size;
return size;
}
gridfs_offset gridfile_seek( gridfile *gfile, gridfs_offset offset ) {
gridfs_offset length;
length = gridfile_get_contentlength( gfile );
gfile->pos = length < offset ? length : offset;
return gfile->pos;
}

View File

@ -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

View File

@ -0,0 +1,381 @@
/*
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
L. Peter Deutsch
ghost@aladdin.com
*/
/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
copyrighted.
The original and principal author of md5.c is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
either statically or dynamically; added missing #include <string.h>
in library.
2002-03-11 lpd Corrected argument list for main(), and added int return
type, in test program and T value program.
2002-02-21 lpd Added missing #include <stdio.h> in test program.
2000-07-03 lpd Patched to eliminate warnings about "constant is
unsigned in ANSI C, signed in traditional"; made test program
self-checking.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
1999-05-03 lpd Original version.
*/
#include "md5.h"
#include <string.h>
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
#ifdef MONGO_BIG_ENDIAN
# define BYTE_ORDER 1
#else
# define BYTE_ORDER -1
#endif
#define T_MASK ((mongo_md5_word_t)~0)
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
#define T3 0x242070db
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
#define T6 0x4787c62a
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
#define T9 0x698098d8
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
#define T13 0x6b901122
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
#define T16 0x49b40821
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
#define T19 0x265e5a51
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
#define T22 0x02441453
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
#define T25 0x21e1cde6
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
#define T28 0x455a14ed
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
#define T31 0x676f02d9
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
#define T35 0x6d9d6122
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
#define T38 0x4bdecfa9
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
#define T41 0x289b7ec6
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
#define T44 0x04881d05
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
#define T47 0x1fa27cf8
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
#define T50 0x432aff97
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
#define T53 0x655b59c3
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
#define T57 0x6fa87e4f
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
#define T60 0x4e0811a1
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
#define T63 0x2ad7d2bb
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
static void
mongo_md5_process(mongo_md5_state_t *pms, const mongo_md5_byte_t *data /*[64]*/)
{
mongo_md5_word_t
a = pms->abcd[0], b = pms->abcd[1],
c = pms->abcd[2], d = pms->abcd[3];
mongo_md5_word_t t;
#if BYTE_ORDER > 0
/* Define storage only for big-endian CPUs. */
mongo_md5_word_t X[16];
#else
/* Define storage for little-endian or both types of CPUs. */
mongo_md5_word_t xbuf[16];
const mongo_md5_word_t *X;
#endif
{
#if BYTE_ORDER == 0
/*
* Determine dynamically whether this is a big-endian or
* little-endian machine, since we can use a more efficient
* algorithm on the latter.
*/
static const int w = 1;
if (*((const mongo_md5_byte_t *)&w)) /* dynamic little-endian */
#endif
#if BYTE_ORDER <= 0 /* little-endian */
{
/*
* On little-endian machines, we can process properly aligned
* data without copying it.
*/
if (!((data - (const mongo_md5_byte_t *)0) & 3)) {
/* data are properly aligned */
X = (const mongo_md5_word_t *)data;
} else {
/* not aligned */
memcpy(xbuf, data, 64);
X = xbuf;
}
}
#endif
#if BYTE_ORDER == 0
else /* dynamic big-endian */
#endif
#if BYTE_ORDER >= 0 /* big-endian */
{
/*
* On big-endian machines, we must arrange the bytes in the
* right order.
*/
const mongo_md5_byte_t *xp = data;
int i;
# if BYTE_ORDER == 0
X = xbuf; /* (dynamic only) */
# else
# define xbuf X /* (static only) */
# endif
for (i = 0; i < 16; ++i, xp += 4)
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
}
#endif
}
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
/* Round 1. */
/* Let [abcd k s i] denote the operation
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + F(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 7, T1);
SET(d, a, b, c, 1, 12, T2);
SET(c, d, a, b, 2, 17, T3);
SET(b, c, d, a, 3, 22, T4);
SET(a, b, c, d, 4, 7, T5);
SET(d, a, b, c, 5, 12, T6);
SET(c, d, a, b, 6, 17, T7);
SET(b, c, d, a, 7, 22, T8);
SET(a, b, c, d, 8, 7, T9);
SET(d, a, b, c, 9, 12, T10);
SET(c, d, a, b, 10, 17, T11);
SET(b, c, d, a, 11, 22, T12);
SET(a, b, c, d, 12, 7, T13);
SET(d, a, b, c, 13, 12, T14);
SET(c, d, a, b, 14, 17, T15);
SET(b, c, d, a, 15, 22, T16);
#undef SET
/* Round 2. */
/* Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + G(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 1, 5, T17);
SET(d, a, b, c, 6, 9, T18);
SET(c, d, a, b, 11, 14, T19);
SET(b, c, d, a, 0, 20, T20);
SET(a, b, c, d, 5, 5, T21);
SET(d, a, b, c, 10, 9, T22);
SET(c, d, a, b, 15, 14, T23);
SET(b, c, d, a, 4, 20, T24);
SET(a, b, c, d, 9, 5, T25);
SET(d, a, b, c, 14, 9, T26);
SET(c, d, a, b, 3, 14, T27);
SET(b, c, d, a, 8, 20, T28);
SET(a, b, c, d, 13, 5, T29);
SET(d, a, b, c, 2, 9, T30);
SET(c, d, a, b, 7, 14, T31);
SET(b, c, d, a, 12, 20, T32);
#undef SET
/* Round 3. */
/* Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define SET(a, b, c, d, k, s, Ti)\
t = a + H(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 5, 4, T33);
SET(d, a, b, c, 8, 11, T34);
SET(c, d, a, b, 11, 16, T35);
SET(b, c, d, a, 14, 23, T36);
SET(a, b, c, d, 1, 4, T37);
SET(d, a, b, c, 4, 11, T38);
SET(c, d, a, b, 7, 16, T39);
SET(b, c, d, a, 10, 23, T40);
SET(a, b, c, d, 13, 4, T41);
SET(d, a, b, c, 0, 11, T42);
SET(c, d, a, b, 3, 16, T43);
SET(b, c, d, a, 6, 23, T44);
SET(a, b, c, d, 9, 4, T45);
SET(d, a, b, c, 12, 11, T46);
SET(c, d, a, b, 15, 16, T47);
SET(b, c, d, a, 2, 23, T48);
#undef SET
/* Round 4. */
/* Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + I(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 6, T49);
SET(d, a, b, c, 7, 10, T50);
SET(c, d, a, b, 14, 15, T51);
SET(b, c, d, a, 5, 21, T52);
SET(a, b, c, d, 12, 6, T53);
SET(d, a, b, c, 3, 10, T54);
SET(c, d, a, b, 10, 15, T55);
SET(b, c, d, a, 1, 21, T56);
SET(a, b, c, d, 8, 6, T57);
SET(d, a, b, c, 15, 10, T58);
SET(c, d, a, b, 6, 15, T59);
SET(b, c, d, a, 13, 21, T60);
SET(a, b, c, d, 4, 6, T61);
SET(d, a, b, c, 11, 10, T62);
SET(c, d, a, b, 2, 15, T63);
SET(b, c, d, a, 9, 21, T64);
#undef SET
/* Then perform the following additions. (That is increment each
of the four registers by the value it had before this block
was started.) */
pms->abcd[0] += a;
pms->abcd[1] += b;
pms->abcd[2] += c;
pms->abcd[3] += d;
}
void
mongo_md5_init(mongo_md5_state_t *pms)
{
pms->count[0] = pms->count[1] = 0;
pms->abcd[0] = 0x67452301;
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
pms->abcd[3] = 0x10325476;
}
void
mongo_md5_append(mongo_md5_state_t *pms, const mongo_md5_byte_t *data, int nbytes)
{
const mongo_md5_byte_t *p = data;
int left = nbytes;
int offset = (pms->count[0] >> 3) & 63;
mongo_md5_word_t nbits = (mongo_md5_word_t)(nbytes << 3);
if (nbytes <= 0)
return;
/* Update the message length. */
pms->count[1] += nbytes >> 29;
pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1]++;
/* Process an initial partial block. */
if (offset) {
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy(pms->buf + offset, p, copy);
if (offset + copy < 64)
return;
p += copy;
left -= copy;
mongo_md5_process(pms, pms->buf);
}
/* Process full blocks. */
for (; left >= 64; p += 64, left -= 64)
mongo_md5_process(pms, p);
/* Process a final partial block. */
if (left)
memcpy(pms->buf, p, left);
}
void
mongo_md5_finish(mongo_md5_state_t *pms, mongo_md5_byte_t digest[16])
{
static const mongo_md5_byte_t pad[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
mongo_md5_byte_t data[8];
int i;
/* Save the length before padding. */
for (i = 0; i < 8; ++i)
data[i] = (mongo_md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
/* Pad to 56 bytes mod 64. */
mongo_md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
/* Append the length. */
mongo_md5_append(pms, data, 8);
for (i = 0; i < 16; ++i)
digest[i] = (mongo_md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
}

View File

@ -0,0 +1,91 @@
/*
Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
L. Peter Deutsch
ghost@aladdin.com
*/
/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
copyrighted.
The original and principal author of md5.h is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
2002-04-13 lpd Removed support for non-ANSI compilers; removed
references to Ghostscript; clarified derivation from RFC 1321;
now handles byte order either statically or dynamically.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
added conditionalization for C++ compilation from Martin
Purschke <purschke@bnl.gov>.
1999-05-03 lpd Original version.
*/
#ifndef md5_INCLUDED
# define md5_INCLUDED
/*
* This package supports both compile-time and run-time determination of CPU
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
* defined as non-zero, the code will be compiled to run only on big-endian
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
* run on either big- or little-endian CPUs, but will run slightly less
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
*/
typedef unsigned char mongo_md5_byte_t; /* 8-bit byte */
typedef unsigned int mongo_md5_word_t; /* 32-bit word */
/* Define the state of the MD5 Algorithm. */
typedef struct mongo_md5_state_s {
mongo_md5_word_t count[2]; /* message length in bits, lsw first */
mongo_md5_word_t abcd[4]; /* digest buffer */
mongo_md5_byte_t buf[64]; /* accumulate block */
} mongo_md5_state_t;
#ifdef __cplusplus
extern "C"
{
#endif
/* Initialize the algorithm. */
void mongo_md5_init(mongo_md5_state_t *pms);
/* Append a string to the message. */
void mongo_md5_append(mongo_md5_state_t *pms, const mongo_md5_byte_t *data, int nbytes);
/* Finish the message and return the digest. */
void mongo_md5_finish(mongo_md5_state_t *pms, mongo_md5_byte_t digest[16]);
#ifdef __cplusplus
} /* end extern "C" */
#endif
#endif /* md5_INCLUDED */

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -0,0 +1,98 @@
/* net.c */
/* Copyright 2009-2011 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Implementation for generic version of net.h */
#include "net.h"
#include <string.h>
int mongo_write_socket( mongo *conn, const void *buf, int len ) {
const char *cbuf = buf;
while ( len ) {
int sent = send( conn->sock, cbuf, len, 0 );
if ( sent == -1 ) {
conn->err = MONGO_IO_ERROR;
return MONGO_ERROR;
}
cbuf += sent;
len -= sent;
}
return MONGO_OK;
}
int mongo_read_socket( mongo *conn, void *buf, int len ) {
char *cbuf = buf;
while ( len ) {
int sent = recv( conn->sock, cbuf, len, 0 );
if ( sent == 0 || sent == -1 ) {
conn->err = MONGO_IO_ERROR;
return MONGO_ERROR;
}
cbuf += sent;
len -= sent;
}
return MONGO_OK;
}
/* This is a no-op in the generic implementation. */
int mongo_set_socket_op_timeout( mongo *conn, int millis ) {
return MONGO_OK;
}
static int mongo_create_socket( mongo *conn ) {
int fd;
if( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) {
conn->err = MONGO_CONN_NO_SOCKET;
return MONGO_ERROR;
}
conn->sock = fd;
return MONGO_OK;
}
int mongo_socket_connect( mongo *conn, const char *host, int port ) {
struct sockaddr_in sa;
socklen_t addressSize;
int flag = 1;
if( mongo_create_socket( conn ) != MONGO_OK )
return MONGO_ERROR;
memset( sa.sin_zero , 0 , sizeof( sa.sin_zero ) );
sa.sin_family = AF_INET;
sa.sin_port = htons( port );
sa.sin_addr.s_addr = inet_addr( host );
addressSize = sizeof( sa );
if ( connect( conn->sock, ( struct sockaddr * )&sa, addressSize ) == -1 ) {
mongo_close_socket( conn->sock );
conn->connected = 0;
conn->sock = 0;
conn->err = MONGO_CONN_FAIL;
return MONGO_ERROR;
}
setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * ) &flag, sizeof( flag ) );
if( conn->op_timeout_ms > 0 )
mongo_set_socket_op_timeout( conn, conn->op_timeout_ms );
conn->connected = 1;
return MONGO_OK;
}

View File

@ -0,0 +1,57 @@
/** @file net.h */
/* Copyright 2009-2011 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Header for generic net.h */
#ifndef _MONGO_NET_H_
#define _MONGO_NET_H_
#include "mongo.h"
#ifdef _WIN32
#include <windows.h>
#include <winsock.h>
#define mongo_close_socket(sock) ( closesocket(sock) )
typedef int socklen_t;
#else
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#define mongo_close_socket(sock) ( close(sock) )
#endif
#ifndef _WIN32
#include <unistd.h>
#endif
#if defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || _POSIX_C_SOURCE >= 1
#define _MONGO_USE_GETADDRINFO
#endif
MONGO_EXTERN_C_START
/* This is a no-op in the generic implementation. */
int mongo_set_socket_op_timeout( mongo *conn, int millis );
int mongo_read_socket( mongo *conn, void *buf, int len );
int mongo_write_socket( mongo *conn, const void *buf, int len );
int mongo_socket_connect( mongo *conn, const char *host, int port );
MONGO_EXTERN_C_END
#endif

View File

@ -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",
};

View File

@ -0,0 +1,94 @@
/** @file platform.h */
/** Copyright 2009-2011 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* all platform-specific ifdefs should go here */
#ifndef _PLATFORM_HACKS_H_
#define _PLATFORM_HACKS_H_
#ifdef __GNUC__
#define MONGO_INLINE static __inline__
#else
#define MONGO_INLINE static
#endif
#ifdef __cplusplus
#define MONGO_EXTERN_C_START extern "C" {
#define MONGO_EXTERN_C_END }
#else
#define MONGO_EXTERN_C_START
#define MONGO_EXTERN_C_END
#endif
#if defined(MONGO_HAVE_STDINT) || __STDC_VERSION__ >= 199901L
#include <stdint.h>
#elif defined(MONGO_HAVE_UNISTD)
#include <unistd.h>
#elif defined(MONGO_USE__INT64)
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#elif defined(MONGO_USE_LONG_LONG_INT)
typedef long long int int64_t;
typedef unsigned long long int uint64_t;
#else
#error must have a 64bit int type
#endif
/* big endian is only used for OID generation. little is used everywhere else */
#ifdef MONGO_BIG_ENDIAN
#define bson_little_endian64(out, in) ( bson_swap_endian64(out, in) )
#define bson_little_endian32(out, in) ( bson_swap_endian32(out, in) )
#define bson_big_endian64(out, in) ( memcpy(out, in, 8) )
#define bson_big_endian32(out, in) ( memcpy(out, in, 4) )
#else
#define bson_little_endian64(out, in) ( memcpy(out, in, 8) )
#define bson_little_endian32(out, in) ( memcpy(out, in, 4) )
#define bson_big_endian64(out, in) ( bson_swap_endian64(out, in) )
#define bson_big_endian32(out, in) ( bson_swap_endian32(out, in) )
#endif
MONGO_EXTERN_C_START
MONGO_INLINE void bson_swap_endian64( void *outp, const void *inp ) {
const char *in = ( const char * )inp;
char *out = ( char * )outp;
out[0] = in[7];
out[1] = in[6];
out[2] = in[5];
out[3] = in[4];
out[4] = in[3];
out[5] = in[2];
out[6] = in[1];
out[7] = in[0];
}
MONGO_INLINE void bson_swap_endian32( void *outp, const void *inp ) {
const char *in = ( const char * )inp;
char *out = ( char * )outp;
out[0] = in[3];
out[1] = in[2];
out[2] = in[1];
out[3] = in[0];
}
MONGO_EXTERN_C_END
#endif

View File

@ -0,0 +1,183 @@
/* net.c */
/* Copyright 2009-2011 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Implementation for Linux version of net.h */
#include "net.h"
#include <string.h>
int mongo_write_socket( mongo *conn, const void *buf, int len ) {
const char *cbuf = buf;
while ( len ) {
int sent = send( conn->sock, cbuf, len, 0 );
if ( sent == -1 ) {
conn->err = MONGO_IO_ERROR;
return MONGO_ERROR;
}
cbuf += sent;
len -= sent;
}
return MONGO_OK;
}
int mongo_read_socket( mongo *conn, void *buf, int len ) {
char *cbuf = buf;
while ( len ) {
int sent = recv( conn->sock, cbuf, len, 0 );
if ( sent == 0 || sent == -1 ) {
conn->err = MONGO_IO_ERROR;
return MONGO_ERROR;
}
cbuf += sent;
len -= sent;
}
return MONGO_OK;
}
static int mongo_create_socket( mongo *conn ) {
int fd;
if( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) {
conn->err = MONGO_CONN_NO_SOCKET;
return MONGO_ERROR;
}
conn->sock = fd;
return MONGO_OK;
}
static int mongo_set_blocking_status( mongo *conn ) {
int flags;
int blocking;
blocking = ( conn->conn_timeout_ms == 0 );
if( blocking )
return MONGO_OK;
else {
if( ( flags = fcntl( conn->sock, F_GETFL ) ) == -1 ) {
conn->err = MONGO_IO_ERROR;
mongo_close_socket( conn->sock );
return MONGO_ERROR;
}
flags |= O_NONBLOCK;
if( ( flags = fcntl( conn->sock, F_SETFL, flags ) ) == -1 ) {
conn->err = MONGO_IO_ERROR;
mongo_close_socket( conn->sock );
return MONGO_ERROR;
}
}
return MONGO_OK;
}
int mongo_set_socket_op_timeout( mongo *conn, int millis ) {
struct timeval tv;
tv.tv_sec = millis / 1000;
tv.tv_usec = ( millis % 1000 ) * 1000;
if ( setsockopt( conn->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof( tv ) ) == -1 ) {
conn->err = MONGO_IO_ERROR;
return MONGO_ERROR;
}
if ( setsockopt( conn->sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof( tv ) ) == -1 ) {
conn->err = MONGO_IO_ERROR;
return MONGO_ERROR;
}
return MONGO_OK;
}
#ifdef _MONGO_USE_GETADDRINFO
int mongo_socket_connect( mongo *conn, const char *host, int port ) {
struct addrinfo *addrs = NULL;
struct addrinfo hints;
int flag = 1;
char port_str[12];
int ret;
conn->sock = 0;
conn->connected = 0;
memset( &hints, 0, sizeof( hints ) );
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
sprintf( port_str, "%d", port );
if( mongo_create_socket( conn ) != MONGO_OK )
return MONGO_ERROR;
if( getaddrinfo( host, port_str, &hints, &addrs ) != 0 ) {
bson_errprintf( "getaddrinfo failed: %s", gai_strerror( ret ) );
conn->err = MONGO_CONN_ADDR_FAIL;
return MONGO_ERROR;
}
if ( connect( conn->sock, addrs->ai_addr, addrs->ai_addrlen ) == -1 ) {
mongo_close_socket( conn->sock );
freeaddrinfo( addrs );
conn->err = MONGO_CONN_FAIL;
return MONGO_ERROR;
}
setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * )&flag, sizeof( flag ) );
if( conn->op_timeout_ms > 0 )
mongo_set_socket_op_timeout( conn, conn->op_timeout_ms );
conn->connected = 1;
freeaddrinfo( addrs );
return MONGO_OK;
}
#else
int mongo_socket_connect( mongo *conn, const char *host, int port ) {
struct sockaddr_in sa;
socklen_t addressSize;
int flag = 1;
if( mongo_create_socket( conn ) != MONGO_OK )
return MONGO_ERROR;
memset( sa.sin_zero , 0 , sizeof( sa.sin_zero ) );
sa.sin_family = AF_INET;
sa.sin_port = htons( port );
sa.sin_addr.s_addr = inet_addr( host );
addressSize = sizeof( sa );
if ( connect( conn->sock, ( struct sockaddr * )&sa, addressSize ) == -1 ) {
mongo_close_socket( conn->sock );
conn->connected = 0;
conn->sock = 0;
conn->err = MONGO_CONN_FAIL;
return MONGO_ERROR;
}
setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * ) &flag, sizeof( flag ) );
if( conn->op_timeout_ms > 0 )
mongo_set_socket_op_timeout( conn, conn->op_timeout_ms );
conn->connected = 1;
return MONGO_OK;
}
#endif

View File

@ -0,0 +1,51 @@
/**
* @file net.h
* @brief Networking.
*/
/* Copyright 2009-2011 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Header for Linux net.h */
#ifndef _MONGO_NET_H_
#define _MONGO_NET_H_
#include "mongo.h"
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#define mongo_close_socket(sock) ( close(sock) )
#if defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || _POSIX_C_SOURCE >= 1
#define _MONGO_USE_GETADDRINFO
#endif
MONGO_EXTERN_C_START
int mongo_set_socket_op_timeout( mongo *conn, int millis );
int mongo_read_socket( mongo *conn, void *buf, int len );
int mongo_write_socket( mongo *conn, const void *buf, int len );
int mongo_socket_connect( mongo *conn, const char *host, int port );
MONGO_EXTERN_C_END
#endif

View File

@ -0,0 +1,413 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Daniel Swarbrick <daniel.swarbrick@seventhsignal.de>
*
* mod_cdr_mongodb.c -- MongoDB CDR Module
*
* Derived from:
* mod_xml_cdr.c -- XML CDR Module to files or curl
*
*/
#include <switch.h>
#include <mongo.h>
static struct {
switch_memory_pool_t *pool;
int shutdown;
char *mongo_host;
uint32_t mongo_port;
char *mongo_namespace;
mongo mongo_conn[1];
switch_mutex_t *mongo_mutex;
switch_bool_t log_b;
} globals;
static switch_xml_config_item_t config_settings[] = {
/* key, flags, ptr, default_value, syntax, helptext */
SWITCH_CONFIG_ITEM_STRING_STRDUP("host", CONFIG_REQUIRED, &globals.mongo_host, "127.0.0.1", NULL, "MongoDB server host address"),
SWITCH_CONFIG_ITEM_STRING_STRDUP("namespace", CONFIG_REQUIRED, &globals.mongo_namespace, NULL, "database.collection", "MongoDB namespace"),
/* key, type, flags, ptr, default_value, data, syntax, helptext */
SWITCH_CONFIG_ITEM("port", SWITCH_CONFIG_INT, CONFIG_REQUIRED, &globals.mongo_port, 27017, NULL, NULL, "MongoDB server TCP port"),
SWITCH_CONFIG_ITEM("log-b-leg", SWITCH_CONFIG_BOOL, CONFIG_RELOADABLE, &globals.log_b, SWITCH_TRUE, NULL, NULL, "Log B-leg in addition to A-leg"),
SWITCH_CONFIG_ITEM_END()
};
SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_mongodb_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_mongodb_shutdown);
SWITCH_MODULE_DEFINITION(mod_cdr_mongodb, mod_cdr_mongodb_load, mod_cdr_mongodb_shutdown, NULL);
static void set_bson_profile_data(bson *b, switch_caller_profile_t *caller_profile)
{
bson_append_string(b, "username", caller_profile->username);
bson_append_string(b, "dialplan", caller_profile->dialplan);
bson_append_string(b, "caller_id_name", caller_profile->caller_id_name);
bson_append_string(b, "ani", caller_profile->ani);
bson_append_string(b, "aniii", caller_profile->aniii);
bson_append_string(b, "caller_id_number", caller_profile->caller_id_number);
bson_append_string(b, "network_addr", caller_profile->network_addr);
bson_append_string(b, "rdnis", caller_profile->rdnis);
bson_append_string(b, "destination_number", caller_profile->destination_number);
bson_append_string(b, "uuid", caller_profile->uuid);
bson_append_string(b, "source", caller_profile->source);
bson_append_string(b, "context", caller_profile->context);
bson_append_string(b, "chan_name", caller_profile->chan_name);
}
static switch_status_t my_on_reporting(switch_core_session_t *session)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_event_header_t *hi;
switch_caller_profile_t *caller_profile;
switch_app_log_t *app_log;
bson cdr;
int is_b;
char *tmp;
if (globals.shutdown) {
return SWITCH_STATUS_SUCCESS;
}
is_b = channel && switch_channel_get_originator_caller_profile(channel);
if (!globals.log_b && is_b) {
const char *force_cdr = switch_channel_get_variable(channel, SWITCH_FORCE_PROCESS_CDR_VARIABLE);
if (!switch_true(force_cdr)) {
return SWITCH_STATUS_SUCCESS;
}
}
bson_init(&cdr);
/* Channel data */
bson_append_start_object(&cdr, "channel_data");
bson_append_string(&cdr, "state", switch_channel_state_name(switch_channel_get_state(channel)));
bson_append_string(&cdr, "direction", switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
bson_append_int(&cdr, "state_number", switch_channel_get_state(channel));
if ((tmp = switch_channel_get_flag_string(channel))) {
bson_append_string(&cdr, "flags", tmp);
free(tmp);
}
if ((tmp = switch_channel_get_cap_string(channel))) {
bson_append_string(&cdr, "caps", tmp);
free(tmp);
}
bson_append_finish_object(&cdr); /* channel_data */
/* Channel variables */
bson_append_start_object(&cdr, "variables");
for (hi = switch_channel_variable_first(channel); hi; hi = hi->next) {
if (!zstr(hi->name) && !zstr(hi->value)) {
bson_append_string(&cdr, hi->name, hi->value);
}
}
bson_append_finish_object(&cdr); /* variables */
/* App log */
if ((app_log = switch_core_session_get_app_log(session))) {
switch_app_log_t *ap;
bson_append_start_object(&cdr, "app_log");
for (ap = app_log; ap; ap = ap->next) {
bson_append_start_object(&cdr, "application");
bson_append_string(&cdr, "app_name", ap->app);
bson_append_string(&cdr, "app_data", ap->arg);
bson_append_long(&cdr, "app_stamp", ap->stamp);
bson_append_finish_object(&cdr); /* application */
}
bson_append_finish_object(&cdr); /* app_log */
}
/* Callflow */
caller_profile = switch_channel_get_caller_profile(channel);
while (caller_profile) {
bson_append_start_object(&cdr, "callflow");
if (!zstr(caller_profile->dialplan)) {
bson_append_string(&cdr, "dialplan", caller_profile->dialplan);
}
if (!zstr(caller_profile->profile_index)) {
bson_append_string(&cdr, "profile_index", caller_profile->profile_index);
}
if (caller_profile->caller_extension) {
switch_caller_application_t *ap;
bson_append_start_object(&cdr, "extension");
bson_append_string(&cdr, "name", caller_profile->caller_extension->extension_name);
bson_append_string(&cdr, "number", caller_profile->caller_extension->extension_number);
if (caller_profile->caller_extension->current_application) {
bson_append_string(&cdr, "current_app", caller_profile->caller_extension->current_application->application_name);
}
for (ap = caller_profile->caller_extension->applications; ap; ap = ap->next) {
bson_append_start_object(&cdr, "application");
if (ap == caller_profile->caller_extension->current_application) {
bson_append_bool(&cdr, "last_executed", 1);
}
bson_append_string(&cdr, "app_name", ap->application_name);
bson_append_string(&cdr, "app_data", ap->application_data);
bson_append_finish_object(&cdr);
}
if (caller_profile->caller_extension->children) {
switch_caller_profile_t *cp = NULL;
for (cp = caller_profile->caller_extension->children; cp; cp = cp->next) {
if (!cp->caller_extension) {
continue;
}
bson_append_start_object(&cdr, "sub_extensions");
bson_append_start_object(&cdr, "extension");
bson_append_string(&cdr, "name", cp->caller_extension->extension_name);
bson_append_string(&cdr, "number", cp->caller_extension->extension_number);
bson_append_string(&cdr, "dialplan", cp->dialplan);
if (cp->caller_extension->current_application) {
bson_append_string(&cdr, "current_app", cp->caller_extension->current_application->application_name);
}
for (ap = cp->caller_extension->applications; ap; ap = ap->next) {
bson_append_start_object(&cdr, "application");
if (ap == cp->caller_extension->current_application) {
bson_append_bool(&cdr, "last_executed", 1);
}
bson_append_string(&cdr, "app_name", ap->application_name);
bson_append_string(&cdr, "app_data", ap->application_data);
bson_append_finish_object(&cdr);
}
bson_append_finish_object(&cdr); /* extension */
bson_append_finish_object(&cdr); /* sub_extensions */
}
}
bson_append_finish_object(&cdr); /* extension */
}
bson_append_start_object(&cdr, "caller_profile");
set_bson_profile_data(&cdr, caller_profile);
if (caller_profile->origination_caller_profile) {
switch_caller_profile_t *cp = NULL;
bson_append_start_object(&cdr, "origination");
for (cp = caller_profile->origination_caller_profile; cp; cp = cp->next) {
bson_append_start_object(&cdr, "origination_caller_profile");
set_bson_profile_data(&cdr, cp);
bson_append_finish_object(&cdr);
}
bson_append_finish_object(&cdr); /* origination */
}
if (caller_profile->originator_caller_profile) {
switch_caller_profile_t *cp = NULL;
bson_append_start_object(&cdr, "originator");
for (cp = caller_profile->originator_caller_profile; cp; cp = cp->next) {
bson_append_start_object(&cdr, "originator_caller_profile");
set_bson_profile_data(&cdr, cp);
bson_append_finish_object(&cdr);
}
bson_append_finish_object(&cdr); /* originator */
}
if (caller_profile->originatee_caller_profile) {
switch_caller_profile_t *cp = NULL;
bson_append_start_object(&cdr, "originatee");
for (cp = caller_profile->originatee_caller_profile; cp; cp = cp->next) {
bson_append_start_object(&cdr, "originatee_caller_profile");
set_bson_profile_data(&cdr, cp);
bson_append_finish_object(&cdr);
}
bson_append_finish_object(&cdr); /* originatee */
}
bson_append_finish_object(&cdr); /* caller_profile */
/* Timestamps */
if (caller_profile->times) {
bson_append_start_object(&cdr, "times");
/* Insert timestamps as long ints (microseconds) to preserve accuracy */
bson_append_long(&cdr, "created_time", caller_profile->times->created);
bson_append_long(&cdr, "profile_created_time", caller_profile->times->profile_created);
bson_append_long(&cdr, "progress_time", caller_profile->times->progress);
bson_append_long(&cdr, "progress_media_time", caller_profile->times->progress_media);
bson_append_long(&cdr, "answered_time", caller_profile->times->answered);
bson_append_long(&cdr, "bridged_time", caller_profile->times->bridged);
bson_append_long(&cdr, "last_hold_time", caller_profile->times->last_hold);
bson_append_long(&cdr, "hold_accum_time", caller_profile->times->hold_accum);
bson_append_long(&cdr, "hangup_time", caller_profile->times->hungup);
bson_append_long(&cdr, "resurrect_time", caller_profile->times->resurrected);
bson_append_long(&cdr, "transfer_time", caller_profile->times->transferred);
bson_append_finish_object(&cdr); /* times */
}
bson_append_finish_object(&cdr); /* callflow */
caller_profile = caller_profile->next;
}
bson_finish(&cdr);
switch_mutex_lock(globals.mongo_mutex);
if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr) != MONGO_OK) {
if (globals.mongo_conn->err == MONGO_IO_ERROR) {
mongo_error_t db_status;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MongoDB connection failed; attempting reconnect...\n");
db_status = mongo_reconnect(globals.mongo_conn);
if (db_status != MONGO_OK) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MongoDB reconnect failed with error code %d\n", db_status);
status = SWITCH_STATUS_FALSE;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "MongoDB connection re-established.\n");
if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr) != MONGO_OK) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_insert: error code %d\n", globals.mongo_conn->err);
status = SWITCH_STATUS_FALSE;
}
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_insert: error code %d\n", globals.mongo_conn->err);
status = SWITCH_STATUS_FALSE;
}
}
switch_mutex_unlock(globals.mongo_mutex);
bson_destroy(&cdr);
return status;
}
static switch_state_handler_table_t state_handlers = {
/*.on_init */ NULL,
/*.on_routing */ NULL,
/*.on_execute */ NULL,
/*.on_hangup */ NULL,
/*.on_exchange_media */ NULL,
/*.on_soft_execute */ NULL,
/*.on_consume_media */ NULL,
/*.on_hibernate */ NULL,
/*.on_reset */ NULL,
/*.on_park */ NULL,
/*.on_reporting */ my_on_reporting
};
static switch_status_t load_config(switch_memory_pool_t *pool)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
if (switch_xml_config_parse_module_settings("cdr_mongodb.conf", SWITCH_FALSE, config_settings) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_FALSE;
}
return status;
}
SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_mongodb_load)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
mongo_error_t db_status;
memset(&globals, 0, sizeof(globals));
globals.pool = pool;
if (load_config(pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Unable to load or parse config!\n");
}
db_status = mongo_connect(globals.mongo_conn, globals.mongo_host, globals.mongo_port);
if (db_status != MONGO_OK) {
switch (globals.mongo_conn->err) {
case MONGO_CONN_NO_SOCKET:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: no socket\n");
break;
case MONGO_CONN_FAIL:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: connection failed\n");
break;
case MONGO_CONN_NOT_MASTER:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: not master\n");
break;
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: unknown error %d\n", db_status);
}
return SWITCH_STATUS_FALSE;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected to MongoDB server %s:%d\n", globals.mongo_host, globals.mongo_port);
}
switch_mutex_init(&globals.mongo_mutex, SWITCH_MUTEX_NESTED, pool);
switch_core_add_state_handler(&state_handlers);
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
return status;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_mongodb_shutdown)
{
globals.shutdown = 1;
switch_core_remove_state_handler(&state_handlers);
switch_mutex_destroy(globals.mongo_mutex);
mongo_destroy(globals.mongo_conn);
return SWITCH_STATUS_SUCCESS;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/