803 lines
24 KiB
C
803 lines
24 KiB
C
/* mongo.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 "mongo.h"
|
|
#include "md5.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifndef _WIN32
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
/* only need one of these */
|
|
static const int zero = 0;
|
|
static const int one = 1;
|
|
|
|
/* ----------------------------
|
|
message stuff
|
|
------------------------------ */
|
|
|
|
static void looping_write(mongo_connection * conn, const void* buf, int len){
|
|
const char* cbuf = buf;
|
|
while (len){
|
|
int sent = send(conn->sock, cbuf, len, 0);
|
|
if (sent == -1) MONGO_THROW(MONGO_EXCEPT_NETWORK);
|
|
cbuf += sent;
|
|
len -= sent;
|
|
}
|
|
}
|
|
|
|
static void looping_read(mongo_connection * conn, void* buf, int len){
|
|
char* cbuf = buf;
|
|
while (len){
|
|
int sent = recv(conn->sock, cbuf, len, 0);
|
|
if (sent == 0 || sent == -1) MONGO_THROW(MONGO_EXCEPT_NETWORK);
|
|
cbuf += sent;
|
|
len -= sent;
|
|
}
|
|
}
|
|
|
|
/* Always calls free(mm) */
|
|
void mongo_message_send(mongo_connection * conn, mongo_message* mm){
|
|
mongo_header head; /* little endian */
|
|
bson_little_endian32(&head.len, &mm->head.len);
|
|
bson_little_endian32(&head.id, &mm->head.id);
|
|
bson_little_endian32(&head.responseTo, &mm->head.responseTo);
|
|
bson_little_endian32(&head.op, &mm->head.op);
|
|
|
|
MONGO_TRY{
|
|
looping_write(conn, &head, sizeof(head));
|
|
looping_write(conn, &mm->data, mm->head.len - sizeof(head));
|
|
}MONGO_CATCH{
|
|
free(mm);
|
|
MONGO_RETHROW();
|
|
}
|
|
free(mm);
|
|
}
|
|
|
|
char * mongo_data_append( char * start , const void * data , int len ){
|
|
memcpy( start , data , len );
|
|
return start + len;
|
|
}
|
|
|
|
char * mongo_data_append32( char * start , const void * data){
|
|
bson_little_endian32( start , data );
|
|
return start + 4;
|
|
}
|
|
|
|
char * mongo_data_append64( char * start , const void * data){
|
|
bson_little_endian64( start , data );
|
|
return start + 8;
|
|
}
|
|
|
|
mongo_message * mongo_message_create( int len , int id , int responseTo , int op ){
|
|
mongo_message * mm = (mongo_message*)bson_malloc( len );
|
|
|
|
if (!id)
|
|
id = rand();
|
|
|
|
/* native endian (converted on send) */
|
|
mm->head.len = len;
|
|
mm->head.id = id;
|
|
mm->head.responseTo = responseTo;
|
|
mm->head.op = op;
|
|
|
|
return mm;
|
|
}
|
|
|
|
/* ----------------------------
|
|
connection stuff
|
|
------------------------------ */
|
|
static int mongo_connect_helper( mongo_connection * conn ){
|
|
/* setup */
|
|
conn->sock = 0;
|
|
conn->connected = 0;
|
|
|
|
memset( conn->sa.sin_zero , 0 , sizeof(conn->sa.sin_zero) );
|
|
conn->sa.sin_family = AF_INET;
|
|
conn->sa.sin_port = htons(conn->left_opts->port);
|
|
conn->sa.sin_addr.s_addr = inet_addr( conn->left_opts->host );
|
|
conn->addressSize = sizeof(conn->sa);
|
|
|
|
/* connect */
|
|
conn->sock = socket( AF_INET, SOCK_STREAM, 0 );
|
|
if ( conn->sock <= 0 ){
|
|
return mongo_conn_no_socket;
|
|
}
|
|
|
|
if ( connect( conn->sock , (struct sockaddr*)&conn->sa , conn->addressSize ) ){
|
|
return mongo_conn_fail;
|
|
}
|
|
|
|
/* nagle */
|
|
setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one) );
|
|
|
|
/* TODO signals */
|
|
|
|
conn->connected = 1;
|
|
return 0;
|
|
}
|
|
|
|
mongo_conn_return mongo_connect( mongo_connection * conn , mongo_connection_options * options ){
|
|
MONGO_INIT_EXCEPTION(&conn->exception);
|
|
|
|
conn->left_opts = bson_malloc(sizeof(mongo_connection_options));
|
|
conn->right_opts = NULL;
|
|
|
|
if ( options ){
|
|
memcpy( conn->left_opts , options , sizeof( mongo_connection_options ) );
|
|
} else {
|
|
strcpy( conn->left_opts->host , "127.0.0.1" );
|
|
conn->left_opts->port = 27017;
|
|
}
|
|
|
|
return mongo_connect_helper(conn);
|
|
}
|
|
|
|
static void swap_repl_pair(mongo_connection * conn){
|
|
mongo_connection_options * tmp = conn->left_opts;
|
|
conn->left_opts = conn->right_opts;
|
|
conn->right_opts = tmp;
|
|
}
|
|
|
|
mongo_conn_return mongo_connect_pair( mongo_connection * conn , mongo_connection_options * left, mongo_connection_options * right ){
|
|
conn->connected = 0;
|
|
MONGO_INIT_EXCEPTION(&conn->exception);
|
|
|
|
conn->left_opts = NULL;
|
|
conn->right_opts = NULL;
|
|
|
|
if ( !left || !right )
|
|
return mongo_conn_bad_arg;
|
|
|
|
conn->left_opts = bson_malloc(sizeof(mongo_connection_options));
|
|
conn->right_opts = bson_malloc(sizeof(mongo_connection_options));
|
|
|
|
memcpy( conn->left_opts, left, sizeof( mongo_connection_options ) );
|
|
memcpy( conn->right_opts, right, sizeof( mongo_connection_options ) );
|
|
|
|
return mongo_reconnect(conn);
|
|
}
|
|
|
|
mongo_conn_return mongo_reconnect( mongo_connection * conn ){
|
|
mongo_conn_return ret;
|
|
mongo_disconnect(conn);
|
|
|
|
/* single server */
|
|
if(conn->right_opts == NULL)
|
|
return mongo_connect_helper(conn);
|
|
|
|
/* repl pair */
|
|
ret = mongo_connect_helper(conn);
|
|
if (ret == mongo_conn_success && mongo_cmd_ismaster(conn, NULL)){
|
|
return mongo_conn_success;
|
|
}
|
|
|
|
swap_repl_pair(conn);
|
|
|
|
ret = mongo_connect_helper(conn);
|
|
if (ret == mongo_conn_success){
|
|
if(mongo_cmd_ismaster(conn, NULL))
|
|
return mongo_conn_success;
|
|
else
|
|
return mongo_conn_not_master;
|
|
}
|
|
|
|
/* failed to connect to both servers */
|
|
return ret;
|
|
}
|
|
|
|
void mongo_insert_batch( mongo_connection * conn , const char * ns , bson ** bsons, int count){
|
|
int size = 16 + 4 + strlen( ns ) + 1;
|
|
int i;
|
|
mongo_message * mm;
|
|
char* data;
|
|
|
|
for(i=0; i<count; i++){
|
|
size += bson_size(bsons[i]);
|
|
}
|
|
|
|
mm = mongo_message_create( size , 0 , 0 , mongo_op_insert );
|
|
|
|
data = &mm->data;
|
|
data = mongo_data_append32(data, &zero);
|
|
data = mongo_data_append(data, ns, strlen(ns) + 1);
|
|
|
|
for(i=0; i<count; i++){
|
|
data = mongo_data_append(data, bsons[i]->data, bson_size( bsons[i] ) );
|
|
}
|
|
|
|
mongo_message_send(conn, mm);
|
|
}
|
|
|
|
void mongo_insert( mongo_connection * conn , const char * ns , bson * bson ){
|
|
char * data;
|
|
mongo_message * mm = mongo_message_create( 16 /* header */
|
|
+ 4 /* ZERO */
|
|
+ strlen(ns)
|
|
+ 1 + bson_size(bson)
|
|
, 0, 0, mongo_op_insert);
|
|
|
|
data = &mm->data;
|
|
data = mongo_data_append32(data, &zero);
|
|
data = mongo_data_append(data, ns, strlen(ns) + 1);
|
|
data = mongo_data_append(data, bson->data, bson_size(bson));
|
|
|
|
mongo_message_send(conn, mm);
|
|
}
|
|
|
|
void mongo_update(mongo_connection* conn, const char* ns, const bson* cond, const bson* op, int flags){
|
|
char * data;
|
|
mongo_message * mm = mongo_message_create( 16 /* header */
|
|
+ 4 /* ZERO */
|
|
+ strlen(ns) + 1
|
|
+ 4 /* flags */
|
|
+ bson_size(cond)
|
|
+ bson_size(op)
|
|
, 0 , 0 , mongo_op_update );
|
|
|
|
data = &mm->data;
|
|
data = mongo_data_append32(data, &zero);
|
|
data = mongo_data_append(data, ns, strlen(ns) + 1);
|
|
data = mongo_data_append32(data, &flags);
|
|
data = mongo_data_append(data, cond->data, bson_size(cond));
|
|
data = mongo_data_append(data, op->data, bson_size(op));
|
|
|
|
mongo_message_send(conn, mm);
|
|
}
|
|
|
|
void mongo_remove(mongo_connection* conn, const char* ns, const bson* cond){
|
|
char * data;
|
|
mongo_message * mm = mongo_message_create( 16 /* header */
|
|
+ 4 /* ZERO */
|
|
+ strlen(ns) + 1
|
|
+ 4 /* ZERO */
|
|
+ bson_size(cond)
|
|
, 0 , 0 , mongo_op_delete );
|
|
|
|
data = &mm->data;
|
|
data = mongo_data_append32(data, &zero);
|
|
data = mongo_data_append(data, ns, strlen(ns) + 1);
|
|
data = mongo_data_append32(data, &zero);
|
|
data = mongo_data_append(data, cond->data, bson_size(cond));
|
|
|
|
mongo_message_send(conn, mm);
|
|
}
|
|
|
|
mongo_reply * mongo_read_response( mongo_connection * conn ){
|
|
mongo_header head; /* header from network */
|
|
mongo_reply_fields fields; /* header from network */
|
|
mongo_reply * out; /* native endian */
|
|
int len;
|
|
|
|
looping_read(conn, &head, sizeof(head));
|
|
looping_read(conn, &fields, sizeof(fields));
|
|
|
|
bson_little_endian32(&len, &head.len);
|
|
|
|
if (len < sizeof(head)+sizeof(fields) || len > 64*1024*1024)
|
|
MONGO_THROW(MONGO_EXCEPT_NETWORK); /* most likely corruption */
|
|
|
|
out = (mongo_reply*)bson_malloc(len);
|
|
|
|
out->head.len = len;
|
|
bson_little_endian32(&out->head.id, &head.id);
|
|
bson_little_endian32(&out->head.responseTo, &head.responseTo);
|
|
bson_little_endian32(&out->head.op, &head.op);
|
|
|
|
bson_little_endian32(&out->fields.flag, &fields.flag);
|
|
bson_little_endian64(&out->fields.cursorID, &fields.cursorID);
|
|
bson_little_endian32(&out->fields.start, &fields.start);
|
|
bson_little_endian32(&out->fields.num, &fields.num);
|
|
|
|
MONGO_TRY{
|
|
looping_read(conn, &out->objs, len-sizeof(head)-sizeof(fields));
|
|
}MONGO_CATCH{
|
|
free(out);
|
|
MONGO_RETHROW();
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
mongo_cursor* mongo_find(mongo_connection* conn, const char* ns, bson* query, bson* fields, int nToReturn, int nToSkip, int options){
|
|
int sl;
|
|
mongo_cursor * cursor;
|
|
char * data;
|
|
mongo_message * mm = mongo_message_create( 16 + /* header */
|
|
4 + /* options */
|
|
strlen( ns ) + 1 + /* ns */
|
|
4 + 4 + /* skip,return */
|
|
bson_size( query ) +
|
|
bson_size( fields ) ,
|
|
0 , 0 , mongo_op_query );
|
|
|
|
|
|
data = &mm->data;
|
|
data = mongo_data_append32( data , &options );
|
|
data = mongo_data_append( data , ns , strlen( ns ) + 1 );
|
|
data = mongo_data_append32( data , &nToSkip );
|
|
data = mongo_data_append32( data , &nToReturn );
|
|
data = mongo_data_append( data , query->data , bson_size( query ) );
|
|
if ( fields )
|
|
data = mongo_data_append( data , fields->data , bson_size( fields ) );
|
|
|
|
bson_fatal_msg( (data == ((char*)mm) + mm->head.len), "query building fail!" );
|
|
|
|
mongo_message_send( conn , mm );
|
|
|
|
cursor = (mongo_cursor*)bson_malloc(sizeof(mongo_cursor));
|
|
|
|
MONGO_TRY{
|
|
cursor->mm = mongo_read_response(conn);
|
|
}MONGO_CATCH{
|
|
free(cursor);
|
|
MONGO_RETHROW();
|
|
}
|
|
|
|
sl = strlen(ns)+1;
|
|
cursor->ns = bson_malloc(sl);
|
|
if (!cursor->ns){
|
|
free(cursor->mm);
|
|
free(cursor);
|
|
return 0;
|
|
}
|
|
memcpy((void*)cursor->ns, ns, sl); /* cast needed to silence GCC warning */
|
|
cursor->conn = conn;
|
|
cursor->current.data = NULL;
|
|
return cursor;
|
|
}
|
|
|
|
bson_bool_t mongo_find_one(mongo_connection* conn, const char* ns, bson* query, bson* fields, bson* out){
|
|
mongo_cursor* cursor = mongo_find(conn, ns, query, fields, 1, 0, 0);
|
|
|
|
if (cursor && mongo_cursor_next(cursor)){
|
|
bson_copy(out, &cursor->current);
|
|
mongo_cursor_destroy(cursor);
|
|
return 1;
|
|
}else{
|
|
mongo_cursor_destroy(cursor);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int64_t mongo_count(mongo_connection* conn, const char* db, const char* ns, bson* query){
|
|
bson_buffer bb;
|
|
bson cmd;
|
|
bson out;
|
|
int64_t count = -1;
|
|
|
|
bson_buffer_init(&bb);
|
|
bson_append_string(&bb, "count", ns);
|
|
if (query && bson_size(query) > 5) /* not empty */
|
|
bson_append_bson(&bb, "query", query);
|
|
bson_from_buffer(&cmd, &bb);
|
|
|
|
MONGO_TRY{
|
|
if(mongo_run_command(conn, db, &cmd, &out)){
|
|
bson_iterator it;
|
|
if(bson_find(&it, &out, "n"))
|
|
count = bson_iterator_long(&it);
|
|
}
|
|
}MONGO_CATCH{
|
|
bson_destroy(&cmd);
|
|
MONGO_RETHROW();
|
|
}
|
|
|
|
bson_destroy(&cmd);
|
|
bson_destroy(&out);
|
|
return count;
|
|
}
|
|
|
|
bson_bool_t mongo_disconnect( mongo_connection * conn ){
|
|
if ( ! conn->connected )
|
|
return 1;
|
|
|
|
#ifdef _WIN32
|
|
closesocket( conn->sock );
|
|
#else
|
|
close( conn->sock );
|
|
#endif
|
|
|
|
conn->sock = 0;
|
|
conn->connected = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
bson_bool_t mongo_destroy( mongo_connection * conn ){
|
|
free(conn->left_opts);
|
|
free(conn->right_opts);
|
|
conn->left_opts = NULL;
|
|
conn->right_opts = NULL;
|
|
|
|
return mongo_disconnect( conn );
|
|
}
|
|
|
|
bson_bool_t mongo_cursor_get_more(mongo_cursor* cursor){
|
|
if (cursor->mm && cursor->mm->fields.cursorID){
|
|
mongo_connection* conn = cursor->conn;
|
|
char* data;
|
|
int sl = strlen(cursor->ns)+1;
|
|
mongo_message * mm = mongo_message_create(16 /*header*/
|
|
+4 /*ZERO*/
|
|
+sl
|
|
+4 /*numToReturn*/
|
|
+8 /*cursorID*/
|
|
, 0, 0, mongo_op_get_more);
|
|
data = &mm->data;
|
|
data = mongo_data_append32(data, &zero);
|
|
data = mongo_data_append(data, cursor->ns, sl);
|
|
data = mongo_data_append32(data, &zero);
|
|
data = mongo_data_append64(data, &cursor->mm->fields.cursorID);
|
|
mongo_message_send(conn, mm);
|
|
|
|
free(cursor->mm);
|
|
|
|
MONGO_TRY{
|
|
cursor->mm = mongo_read_response(cursor->conn);
|
|
}MONGO_CATCH{
|
|
cursor->mm = NULL;
|
|
mongo_cursor_destroy(cursor);
|
|
MONGO_RETHROW();
|
|
}
|
|
|
|
return cursor->mm && cursor->mm->fields.num;
|
|
} else{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bson_bool_t mongo_cursor_next(mongo_cursor* cursor){
|
|
char* bson_addr;
|
|
|
|
/* no data */
|
|
if (!cursor->mm || cursor->mm->fields.num == 0)
|
|
return 0;
|
|
|
|
/* first */
|
|
if (cursor->current.data == NULL){
|
|
bson_init(&cursor->current, &cursor->mm->objs, 0);
|
|
return 1;
|
|
}
|
|
|
|
bson_addr = cursor->current.data + bson_size(&cursor->current);
|
|
if (bson_addr >= ((char*)cursor->mm + cursor->mm->head.len)){
|
|
if (!mongo_cursor_get_more(cursor))
|
|
return 0;
|
|
bson_init(&cursor->current, &cursor->mm->objs, 0);
|
|
} else {
|
|
bson_init(&cursor->current, bson_addr, 0);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void mongo_cursor_destroy(mongo_cursor* cursor){
|
|
if (!cursor) return;
|
|
|
|
if (cursor->mm && cursor->mm->fields.cursorID){
|
|
mongo_connection* conn = cursor->conn;
|
|
mongo_message * mm = mongo_message_create(16 /*header*/
|
|
+4 /*ZERO*/
|
|
+4 /*numCursors*/
|
|
+8 /*cursorID*/
|
|
, 0, 0, mongo_op_kill_cursors);
|
|
char* data = &mm->data;
|
|
data = mongo_data_append32(data, &zero);
|
|
data = mongo_data_append32(data, &one);
|
|
data = mongo_data_append64(data, &cursor->mm->fields.cursorID);
|
|
|
|
MONGO_TRY{
|
|
mongo_message_send(conn, mm);
|
|
}MONGO_CATCH{
|
|
free(cursor->mm);
|
|
free((void*)cursor->ns);
|
|
free(cursor);
|
|
MONGO_RETHROW();
|
|
}
|
|
}
|
|
|
|
free(cursor->mm);
|
|
free((void*)cursor->ns);
|
|
free(cursor);
|
|
}
|
|
|
|
bson_bool_t mongo_create_index(mongo_connection * conn, const char * ns, bson * key, int options, bson * out){
|
|
bson_buffer bb;
|
|
bson b;
|
|
bson_iterator it;
|
|
char name[255] = {'_'};
|
|
int i = 1;
|
|
char idxns[1024];
|
|
|
|
bson_iterator_init(&it, key->data);
|
|
while(i < 255 && bson_iterator_next(&it)){
|
|
strncpy(name + i, bson_iterator_key(&it), 255 - i);
|
|
i += strlen(bson_iterator_key(&it));
|
|
}
|
|
name[254] = '\0';
|
|
|
|
bson_buffer_init(&bb);
|
|
bson_append_bson(&bb, "key", key);
|
|
bson_append_string(&bb, "ns", ns);
|
|
bson_append_string(&bb, "name", name);
|
|
if (options & MONGO_INDEX_UNIQUE)
|
|
bson_append_bool(&bb, "unique", 1);
|
|
if (options & MONGO_INDEX_DROP_DUPS)
|
|
bson_append_bool(&bb, "dropDups", 1);
|
|
|
|
bson_from_buffer(&b, &bb);
|
|
|
|
strncpy(idxns, ns, 1024-16);
|
|
strcpy(strchr(idxns, '.'), ".system.indexes");
|
|
mongo_insert(conn, idxns, &b);
|
|
bson_destroy(&b);
|
|
|
|
*strchr(idxns, '.') = '\0'; /* just db not ns */
|
|
return !mongo_cmd_get_last_error(conn, idxns, out);
|
|
}
|
|
bson_bool_t mongo_create_simple_index(mongo_connection * conn, const char * ns, const char* field, int options, bson * out){
|
|
bson_buffer bb;
|
|
bson b;
|
|
bson_bool_t success;
|
|
|
|
bson_buffer_init(&bb);
|
|
bson_append_int(&bb, field, 1);
|
|
bson_from_buffer(&b, &bb);
|
|
|
|
success = mongo_create_index(conn, ns, &b, options, out);
|
|
bson_destroy(&b);
|
|
return success;
|
|
}
|
|
|
|
bson_bool_t mongo_run_command(mongo_connection * conn, const char * db, bson * command, bson * out){
|
|
bson fields;
|
|
int sl = strlen(db);
|
|
char* ns = bson_malloc(sl + 5 + 1); /* ".$cmd" + nul */
|
|
bson_bool_t success;
|
|
|
|
strcpy(ns, db);
|
|
strcpy(ns+sl, ".$cmd");
|
|
|
|
success = mongo_find_one(conn, ns, command, bson_empty(&fields), out);
|
|
free(ns);
|
|
return success;
|
|
}
|
|
bson_bool_t mongo_simple_int_command(mongo_connection * conn, const char * db, const char* cmdstr, int arg, bson * realout){
|
|
bson out;
|
|
bson cmd;
|
|
bson_buffer bb;
|
|
bson_bool_t success = 0;
|
|
|
|
bson_buffer_init(&bb);
|
|
bson_append_int(&bb, cmdstr, arg);
|
|
bson_from_buffer(&cmd, &bb);
|
|
|
|
if(mongo_run_command(conn, db, &cmd, &out)){
|
|
bson_iterator it;
|
|
if(bson_find(&it, &out, "ok"))
|
|
success = bson_iterator_bool(&it);
|
|
}
|
|
|
|
bson_destroy(&cmd);
|
|
|
|
if (realout)
|
|
*realout = out;
|
|
else
|
|
bson_destroy(&out);
|
|
|
|
return success;
|
|
}
|
|
|
|
bson_bool_t mongo_simple_str_command(mongo_connection * conn, const char * db, const char* cmdstr, const char* arg, bson * realout){
|
|
bson out;
|
|
bson cmd;
|
|
bson_buffer bb;
|
|
bson_bool_t success = 0;
|
|
|
|
bson_buffer_init(&bb);
|
|
bson_append_string(&bb, cmdstr, arg);
|
|
bson_from_buffer(&cmd, &bb);
|
|
|
|
if(mongo_run_command(conn, db, &cmd, &out)){
|
|
bson_iterator it;
|
|
if(bson_find(&it, &out, "ok"))
|
|
success = bson_iterator_bool(&it);
|
|
}
|
|
|
|
bson_destroy(&cmd);
|
|
|
|
if (realout)
|
|
*realout = out;
|
|
else
|
|
bson_destroy(&out);
|
|
|
|
return success;
|
|
}
|
|
|
|
bson_bool_t mongo_cmd_drop_db(mongo_connection * conn, const char * db){
|
|
return mongo_simple_int_command(conn, db, "dropDatabase", 1, NULL);
|
|
}
|
|
|
|
bson_bool_t mongo_cmd_drop_collection(mongo_connection * conn, const char * db, const char * collection, bson * out){
|
|
return mongo_simple_str_command(conn, db, "drop", collection, out);
|
|
}
|
|
|
|
void mongo_cmd_reset_error(mongo_connection * conn, const char * db){
|
|
mongo_simple_int_command(conn, db, "reseterror", 1, NULL);
|
|
}
|
|
|
|
static bson_bool_t mongo_cmd_get_error_helper(mongo_connection * conn, const char * db, bson * realout, const char * cmdtype){
|
|
bson out = {NULL,0};
|
|
bson_bool_t haserror = 1;
|
|
|
|
|
|
if(mongo_simple_int_command(conn, db, cmdtype, 1, &out)){
|
|
bson_iterator it;
|
|
haserror = (bson_find(&it, &out, "err") != bson_null);
|
|
}
|
|
|
|
if(realout)
|
|
*realout = out; /* transfer of ownership */
|
|
else
|
|
bson_destroy(&out);
|
|
|
|
return haserror;
|
|
}
|
|
|
|
bson_bool_t mongo_cmd_get_prev_error(mongo_connection * conn, const char * db, bson * out){
|
|
return mongo_cmd_get_error_helper(conn, db, out, "getpreverror");
|
|
}
|
|
bson_bool_t mongo_cmd_get_last_error(mongo_connection * conn, const char * db, bson * out){
|
|
return mongo_cmd_get_error_helper(conn, db, out, "getlasterror");
|
|
}
|
|
|
|
bson_bool_t mongo_cmd_ismaster(mongo_connection * conn, bson * realout){
|
|
bson out = {NULL,0};
|
|
bson_bool_t ismaster = 0;
|
|
|
|
if (mongo_simple_int_command(conn, "admin", "ismaster", 1, &out)){
|
|
bson_iterator it;
|
|
bson_find(&it, &out, "ismaster");
|
|
ismaster = bson_iterator_bool(&it);
|
|
}
|
|
|
|
if(realout)
|
|
*realout = out; /* transfer of ownership */
|
|
else
|
|
bson_destroy(&out);
|
|
|
|
return ismaster;
|
|
}
|
|
|
|
static void digest2hex(mongo_md5_byte_t digest[16], char hex_digest[33]){
|
|
static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
|
|
int i;
|
|
for (i=0; i<16; i++){
|
|
hex_digest[2*i] = hex[(digest[i] & 0xf0) >> 4];
|
|
hex_digest[2*i + 1] = hex[ digest[i] & 0x0f ];
|
|
}
|
|
hex_digest[32] = '\0';
|
|
}
|
|
|
|
static void mongo_pass_digest(const char* user, const char* pass, char hex_digest[33]){
|
|
mongo_md5_state_t st;
|
|
mongo_md5_byte_t digest[16];
|
|
|
|
mongo_md5_init(&st);
|
|
mongo_md5_append(&st, (const mongo_md5_byte_t*)user, strlen(user));
|
|
mongo_md5_append(&st, (const mongo_md5_byte_t*)":mongo:", 7);
|
|
mongo_md5_append(&st, (const mongo_md5_byte_t*)pass, strlen(pass));
|
|
mongo_md5_finish(&st, digest);
|
|
digest2hex(digest, hex_digest);
|
|
}
|
|
|
|
void mongo_cmd_add_user(mongo_connection* conn, const char* db, const char* user, const char* pass){
|
|
bson_buffer bb;
|
|
bson user_obj;
|
|
bson pass_obj;
|
|
char hex_digest[33];
|
|
char* ns = malloc(strlen(db) + strlen(".system.users") + 1);
|
|
|
|
strcpy(ns, db);
|
|
strcpy(ns+strlen(db), ".system.users");
|
|
|
|
mongo_pass_digest(user, pass, hex_digest);
|
|
|
|
bson_buffer_init(&bb);
|
|
bson_append_string(&bb, "user", user);
|
|
bson_from_buffer(&user_obj, &bb);
|
|
|
|
bson_buffer_init(&bb);
|
|
bson_append_start_object(&bb, "$set");
|
|
bson_append_string(&bb, "pwd", hex_digest);
|
|
bson_append_finish_object(&bb);
|
|
bson_from_buffer(&pass_obj, &bb);
|
|
|
|
|
|
MONGO_TRY{
|
|
mongo_update(conn, ns, &user_obj, &pass_obj, MONGO_UPDATE_UPSERT);
|
|
}MONGO_CATCH{
|
|
free(ns);
|
|
bson_destroy(&user_obj);
|
|
bson_destroy(&pass_obj);
|
|
MONGO_RETHROW();
|
|
}
|
|
|
|
free(ns);
|
|
bson_destroy(&user_obj);
|
|
bson_destroy(&pass_obj);
|
|
}
|
|
|
|
bson_bool_t mongo_cmd_authenticate(mongo_connection* conn, const char* db, const char* user, const char* pass){
|
|
bson_buffer bb;
|
|
bson from_db, auth_cmd;
|
|
const char* nonce;
|
|
bson_bool_t success = 0;
|
|
|
|
mongo_md5_state_t st;
|
|
mongo_md5_byte_t digest[16];
|
|
char hex_digest[33];
|
|
|
|
if (mongo_simple_int_command(conn, db, "getnonce", 1, &from_db)){
|
|
bson_iterator it;
|
|
bson_find(&it, &from_db, "nonce");
|
|
nonce = bson_iterator_string(&it);
|
|
}else{
|
|
return 0;
|
|
}
|
|
|
|
mongo_pass_digest(user, pass, hex_digest);
|
|
|
|
mongo_md5_init(&st);
|
|
mongo_md5_append(&st, (const mongo_md5_byte_t*)nonce, strlen(nonce));
|
|
mongo_md5_append(&st, (const mongo_md5_byte_t*)user, strlen(user));
|
|
mongo_md5_append(&st, (const mongo_md5_byte_t*)hex_digest, 32);
|
|
mongo_md5_finish(&st, digest);
|
|
digest2hex(digest, hex_digest);
|
|
|
|
bson_buffer_init(&bb);
|
|
bson_append_int(&bb, "authenticate", 1);
|
|
bson_append_string(&bb, "user", user);
|
|
bson_append_string(&bb, "nonce", nonce);
|
|
bson_append_string(&bb, "key", hex_digest);
|
|
bson_from_buffer(&auth_cmd, &bb);
|
|
|
|
bson_destroy(&from_db);
|
|
|
|
MONGO_TRY{
|
|
if(mongo_run_command(conn, db, &auth_cmd, &from_db)){
|
|
bson_iterator it;
|
|
if(bson_find(&it, &from_db, "ok"))
|
|
success = bson_iterator_bool(&it);
|
|
}
|
|
}MONGO_CATCH{
|
|
bson_destroy(&auth_cmd);
|
|
MONGO_RETHROW();
|
|
}
|
|
|
|
bson_destroy(&from_db);
|
|
bson_destroy(&auth_cmd);
|
|
|
|
return success;
|
|
}
|