1 /* mongo.c */
2
3 /* Copyright 2009-2011 10gen Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include "mongo.h"
19 #include "md5.h"
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25
26 #ifdef _USE_LINUX_SYSTEM
27 #include "platform/linux/net.h"
28 #elif defined _USE_CUSTOM_SYSTEM
29 #include "platform/custom/net.h"
30 #else
31 #include "net.h"
32 #endif
33
34
35
mongo_create()36 MONGO_EXPORT mongo* mongo_create() {
37 return (mongo*)bson_malloc(sizeof(mongo));
38 }
39
40
mongo_dispose(mongo * conn)41 MONGO_EXPORT void mongo_dispose(mongo* conn) {
42 free(conn);
43 }
44
mongo_get_err(mongo * conn)45 MONGO_EXPORT int mongo_get_err(mongo* conn) {
46 return conn->err;
47 }
48
49
mongo_is_connected(mongo * conn)50 MONGO_EXPORT int mongo_is_connected(mongo* conn) {
51 return conn->connected != 0;
52 }
53
54
mongo_get_op_timeout(mongo * conn)55 MONGO_EXPORT int mongo_get_op_timeout(mongo* conn) {
56 return conn->op_timeout_ms;
57 }
58
59
_get_host_port(mongo_host_port * hp)60 const char* _get_host_port(mongo_host_port* hp) {
61 static char _hp[sizeof(hp->host)+12];
62 sprintf(_hp, "%s:%d", hp->host, hp->port);
63 return _hp;
64 }
65
66
mongo_get_primary(mongo * conn)67 MONGO_EXPORT const char* mongo_get_primary(mongo* conn) {
68 mongo* conn_ = (mongo*)conn;
69 return _get_host_port(conn_->primary);
70 }
71
72
mongo_get_socket(mongo * conn)73 MONGO_EXPORT int mongo_get_socket(mongo* conn) {
74 mongo* conn_ = (mongo*)conn;
75 return conn_->sock;
76 }
77
78
mongo_get_host_count(mongo * conn)79 MONGO_EXPORT int mongo_get_host_count(mongo* conn) {
80 mongo_replset* r = conn->replset;
81 mongo_host_port* hp;
82 int count = 0;
83 if (!r) return 0;
84 for (hp = r->hosts; hp; hp = hp->next)
85 ++count;
86 return count;
87 }
88
89
mongo_get_host(mongo * conn,int i)90 MONGO_EXPORT const char* mongo_get_host(mongo* conn, int i) {
91 mongo_replset* r = conn->replset;
92 mongo_host_port* hp;
93 int count = 0;
94 if (!r) return 0;
95 for (hp = r->hosts; hp; hp = hp->next) {
96 if (count == i)
97 return _get_host_port(hp);
98 ++count;
99 }
100 return 0;
101 }
102
103
mongo_cursor_create()104 MONGO_EXPORT mongo_cursor* mongo_cursor_create() {
105 return (mongo_cursor*)bson_malloc(sizeof(mongo_cursor));
106 }
107
108
mongo_cursor_dispose(mongo_cursor * cursor)109 MONGO_EXPORT void mongo_cursor_dispose(mongo_cursor* cursor) {
110 free(cursor);
111 }
112
113
mongo_get_server_err(mongo * conn)114 MONGO_EXPORT int mongo_get_server_err(mongo* conn) {
115 return conn->lasterrcode;
116 }
117
118
mongo_get_server_err_string(mongo * conn)119 MONGO_EXPORT const char* mongo_get_server_err_string(mongo* conn) {
120 return conn->lasterrstr;
121 }
122
123
124 static const int ZERO = 0;
125 static const int ONE = 1;
mongo_message_create(int len,int id,int responseTo,int op)126 mongo_message *mongo_message_create( int len , int id , int responseTo , int op ) {
127 mongo_message *mm = ( mongo_message * )bson_malloc( len );
128
129 if ( !id )
130 id = rand();
131
132 /* native endian (converted on send) */
133 mm->head.len = len;
134 mm->head.id = id;
135 mm->head.responseTo = responseTo;
136 mm->head.op = op;
137
138 return mm;
139 }
140
141 /* Always calls bson_free(mm) */
mongo_message_send(mongo * conn,mongo_message * mm)142 int mongo_message_send( mongo *conn, mongo_message *mm ) {
143 mongo_header head; /* little endian */
144 int res;
145 bson_little_endian32( &head.len, &mm->head.len );
146 bson_little_endian32( &head.id, &mm->head.id );
147 bson_little_endian32( &head.responseTo, &mm->head.responseTo );
148 bson_little_endian32( &head.op, &mm->head.op );
149
150 res = mongo_write_socket( conn, &head, sizeof( head ) );
151 if( res != MONGO_OK ) {
152 bson_free( mm );
153 return res;
154 }
155
156 res = mongo_write_socket( conn, &mm->data, mm->head.len - sizeof( head ) );
157 if( res != MONGO_OK ) {
158 bson_free( mm );
159 return res;
160 }
161
162 bson_free( mm );
163 return MONGO_OK;
164 }
165
mongo_read_response(mongo * conn,mongo_reply ** reply)166 int mongo_read_response( mongo *conn, mongo_reply **reply ) {
167 mongo_header head; /* header from network */
168 mongo_reply_fields fields; /* header from network */
169 mongo_reply *out; /* native endian */
170 unsigned int len;
171 int res;
172
173 mongo_read_socket( conn, &head, sizeof( head ) );
174 mongo_read_socket( conn, &fields, sizeof( fields ) );
175
176 bson_little_endian32( &len, &head.len );
177
178 if ( len < sizeof( head )+sizeof( fields ) || len > 64*1024*1024 )
179 return MONGO_READ_SIZE_ERROR; /* most likely corruption */
180
181 out = ( mongo_reply * )bson_malloc( len );
182
183 out->head.len = len;
184 bson_little_endian32( &out->head.id, &head.id );
185 bson_little_endian32( &out->head.responseTo, &head.responseTo );
186 bson_little_endian32( &out->head.op, &head.op );
187
188 bson_little_endian32( &out->fields.flag, &fields.flag );
189 bson_little_endian64( &out->fields.cursorID, &fields.cursorID );
190 bson_little_endian32( &out->fields.start, &fields.start );
191 bson_little_endian32( &out->fields.num, &fields.num );
192
193 res = mongo_read_socket( conn, &out->objs, len-sizeof( head )-sizeof( fields ) );
194 if( res != MONGO_OK ) {
195 bson_free( out );
196 return res;
197 }
198
199 *reply = out;
200
201 return MONGO_OK;
202 }
203
204
mongo_data_append(char * start,const void * data,int len)205 char *mongo_data_append( char *start , const void *data , int len ) {
206 memcpy( start , data , len );
207 return start + len;
208 }
209
mongo_data_append32(char * start,const void * data)210 char *mongo_data_append32( char *start , const void *data ) {
211 bson_little_endian32( start , data );
212 return start + 4;
213 }
214
mongo_data_append64(char * start,const void * data)215 char *mongo_data_append64( char *start , const void *data ) {
216 bson_little_endian64( start , data );
217 return start + 8;
218 }
219
220 /* Connection API */
221
mongo_check_is_master(mongo * conn)222 static int mongo_check_is_master( mongo *conn ) {
223 bson out;
224 bson_iterator it;
225 bson_bool_t ismaster = 0;
226
227 out.data = NULL;
228
229 if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) {
230 if( bson_find( &it, &out, "ismaster" ) )
231 ismaster = bson_iterator_bool( &it );
232 } else {
233 return MONGO_ERROR;
234 }
235
236 bson_destroy( &out );
237
238 if( ismaster )
239 return MONGO_OK;
240 else {
241 conn->err = MONGO_CONN_NOT_MASTER;
242 return MONGO_ERROR;
243 }
244 }
245
mongo_init(mongo * conn)246 void mongo_init( mongo *conn ) {
247 memset( conn, 0, sizeof( mongo ) );
248 }
249
mongo_connect(mongo * conn,const char * host,int port)250 MONGO_EXPORT int mongo_connect( mongo *conn , const char *host, int port ) {
251 mongo_init( conn );
252
253 conn->primary = bson_malloc( sizeof( mongo_host_port ) );
254 strncpy( conn->primary->host, host, strlen( host ) + 1 );
255 conn->primary->port = port;
256 conn->primary->next = NULL;
257
258 if( mongo_socket_connect( conn, host, port ) != MONGO_OK )
259 return MONGO_ERROR;
260
261 if( mongo_check_is_master( conn ) != MONGO_OK )
262 return MONGO_ERROR;
263 else
264 return MONGO_OK;
265 }
266
mongo_replset_init(mongo * conn,const char * name)267 MONGO_EXPORT void mongo_replset_init( mongo *conn, const char *name ) {
268 mongo_init( conn );
269
270 conn->replset = bson_malloc( sizeof( mongo_replset ) );
271 conn->replset->primary_connected = 0;
272 conn->replset->seeds = NULL;
273 conn->replset->hosts = NULL;
274 conn->replset->name = ( char * )bson_malloc( strlen( name ) + 1 );
275 memcpy( conn->replset->name, name, strlen( name ) + 1 );
276
277 conn->primary = bson_malloc( sizeof( mongo_host_port ) );
278 }
279
mongo_replset_add_node(mongo_host_port ** list,const char * host,int port)280 static void mongo_replset_add_node( mongo_host_port **list, const char *host, int port ) {
281 mongo_host_port *host_port = bson_malloc( sizeof( mongo_host_port ) );
282 host_port->port = port;
283 host_port->next = NULL;
284 strncpy( host_port->host, host, strlen( host ) + 1 );
285
286 if( *list == NULL )
287 *list = host_port;
288 else {
289 mongo_host_port *p = *list;
290 while( p->next != NULL )
291 p = p->next;
292 p->next = host_port;
293 }
294 }
295
mongo_replset_free_list(mongo_host_port ** list)296 static void mongo_replset_free_list( mongo_host_port **list ) {
297 mongo_host_port *node = *list;
298 mongo_host_port *prev;
299
300 while( node != NULL ) {
301 prev = node;
302 node = node->next;
303 bson_free( prev );
304 }
305
306 *list = NULL;
307 }
308
mongo_replset_add_seed(mongo * conn,const char * host,int port)309 MONGO_EXPORT void mongo_replset_add_seed( mongo *conn, const char *host, int port ) {
310 mongo_replset_add_node( &conn->replset->seeds, host, port );
311 }
312
mongo_parse_host(const char * host_string,mongo_host_port * host_port)313 void mongo_parse_host( const char *host_string, mongo_host_port *host_port ) {
314 int len, idx, split;
315 len = split = idx = 0;
316
317 /* Split the host_port string at the ':' */
318 while( 1 ) {
319 if( *( host_string + len ) == '\0' )
320 break;
321 if( *( host_string + len ) == ':' )
322 split = len;
323
324 len++;
325 }
326
327 /* If 'split' is set, we know the that port exists;
328 * Otherwise, we set the default port. */
329 idx = split ? split : len;
330 memcpy( host_port->host, host_string, idx );
331 memcpy( host_port->host + idx, "\0", 1 );
332 if( split )
333 host_port->port = atoi( host_string + idx + 1 );
334 else
335 host_port->port = MONGO_DEFAULT_PORT;
336 }
337
mongo_replset_check_seed(mongo * conn)338 static void mongo_replset_check_seed( mongo *conn ) {
339 bson out;
340 bson hosts;
341 const char *data;
342 bson_iterator it;
343 bson_iterator it_sub;
344 const char *host_string;
345 mongo_host_port *host_port = NULL;
346
347 out.data = NULL;
348
349 hosts.data = NULL;
350
351 if( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) {
352
353 if( bson_find( &it, &out, "hosts" ) ) {
354 data = bson_iterator_value( &it );
355 bson_iterator_from_buffer( &it_sub, data );
356
357 /* Iterate over host list, adding each host to the
358 * connection's host list. */
359 while( bson_iterator_next( &it_sub ) ) {
360 host_string = bson_iterator_string( &it_sub );
361
362 host_port = bson_malloc( sizeof( mongo_host_port ) );
363 mongo_parse_host( host_string, host_port );
364
365 if( host_port ) {
366 mongo_replset_add_node( &conn->replset->hosts,
367 host_port->host, host_port->port );
368
369 bson_free( host_port );
370 host_port = NULL;
371 }
372 }
373 }
374 }
375
376 bson_destroy( &out );
377 bson_destroy( &hosts );
378 mongo_close_socket( conn->sock );
379 conn->sock = 0;
380 conn->connected = 0;
381
382 }
383
384 /* Find out whether the current connected node is master, and
385 * verify that the node's replica set name matched the provided name
386 */
mongo_replset_check_host(mongo * conn)387 static int mongo_replset_check_host( mongo *conn ) {
388
389 bson out;
390 bson_iterator it;
391 bson_bool_t ismaster = 0;
392 const char *set_name;
393
394 out.data = NULL;
395
396 if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) {
397 if( bson_find( &it, &out, "ismaster" ) )
398 ismaster = bson_iterator_bool( &it );
399
400 if( bson_find( &it, &out, "setName" ) ) {
401 set_name = bson_iterator_string( &it );
402 if( strcmp( set_name, conn->replset->name ) != 0 ) {
403 bson_destroy( &out );
404 conn->err = MONGO_CONN_BAD_SET_NAME;
405 return MONGO_ERROR;
406 }
407 }
408 }
409
410 bson_destroy( &out );
411
412 if( ismaster ) {
413 conn->replset->primary_connected = 1;
414 } else {
415 mongo_close_socket( conn->sock );
416 }
417
418 return MONGO_OK;
419 }
420
mongo_replset_connect(mongo * conn)421 MONGO_EXPORT int mongo_replset_connect( mongo *conn ) {
422
423 int res = 0;
424 mongo_host_port *node;
425
426 conn->sock = 0;
427 conn->connected = 0;
428
429 /* First iterate over the seed nodes to get the canonical list of hosts
430 * from the replica set. Break out once we have a host list.
431 */
432 node = conn->replset->seeds;
433 while( node != NULL ) {
434 res = mongo_socket_connect( conn, ( const char * )&node->host, node->port );
435 if( res == MONGO_OK ) {
436 mongo_replset_check_seed( conn );
437 if( conn->replset->hosts )
438 break;
439 }
440 node = node->next;
441 }
442
443 /* Iterate over the host list, checking for the primary node. */
444 if( !conn->replset->hosts ) {
445 conn->err = MONGO_CONN_NO_PRIMARY;
446 return MONGO_ERROR;
447 } else {
448 node = conn->replset->hosts;
449
450 while( node != NULL ) {
451 res = mongo_socket_connect( conn, ( const char * )&node->host, node->port );
452
453 if( res == MONGO_OK ) {
454 if( mongo_replset_check_host( conn ) != MONGO_OK )
455 return MONGO_ERROR;
456
457 /* Primary found, so return. */
458 else if( conn->replset->primary_connected ) {
459 strncpy( conn->primary->host, node->host, strlen( node->host ) + 1 );
460 conn->primary->port = node->port;
461 return MONGO_OK;
462 }
463
464 /* No primary, so close the connection. */
465 else {
466 mongo_close_socket( conn->sock );
467 conn->sock = 0;
468 conn->connected = 0;
469 }
470 }
471
472 node = node->next;
473 }
474 }
475
476
477 conn->err = MONGO_CONN_NO_PRIMARY;
478 return MONGO_ERROR;
479 }
480
mongo_set_op_timeout(mongo * conn,int millis)481 MONGO_EXPORT int mongo_set_op_timeout( mongo *conn, int millis ) {
482 conn->op_timeout_ms = millis;
483 if( conn->sock && conn->connected )
484 mongo_set_socket_op_timeout( conn, millis );
485
486 return MONGO_OK;
487 }
488
mongo_reconnect(mongo * conn)489 MONGO_EXPORT int mongo_reconnect( mongo *conn ) {
490 int res;
491 mongo_disconnect( conn );
492
493 if( conn->replset ) {
494 conn->replset->primary_connected = 0;
495 mongo_replset_free_list( &conn->replset->hosts );
496 conn->replset->hosts = NULL;
497 res = mongo_replset_connect( conn );
498 return res;
499 } else
500 return mongo_socket_connect( conn, conn->primary->host, conn->primary->port );
501 }
502
mongo_check_connection(mongo * conn)503 MONGO_EXPORT int mongo_check_connection( mongo *conn ) {
504 if( ! conn->connected )
505 return MONGO_ERROR;
506
507 if( mongo_simple_int_command( conn, "admin", "ping", 1, NULL ) == MONGO_OK )
508 return MONGO_OK;
509 else
510 return MONGO_ERROR;
511 }
512
mongo_disconnect(mongo * conn)513 MONGO_EXPORT void mongo_disconnect( mongo *conn ) {
514 if( ! conn->connected )
515 return;
516
517 if( conn->replset ) {
518 conn->replset->primary_connected = 0;
519 mongo_replset_free_list( &conn->replset->hosts );
520 conn->replset->hosts = NULL;
521 }
522
523 mongo_close_socket( conn->sock );
524
525 conn->sock = 0;
526 conn->connected = 0;
527 }
528
mongo_destroy(mongo * conn)529 MONGO_EXPORT void mongo_destroy( mongo *conn ) {
530 mongo_disconnect( conn );
531
532 if( conn->replset ) {
533 mongo_replset_free_list( &conn->replset->seeds );
534 mongo_replset_free_list( &conn->replset->hosts );
535 bson_free( conn->replset->name );
536 bson_free( conn->replset );
537 conn->replset = NULL;
538 }
539
540 bson_free( conn->primary );
541 bson_free( conn->errstr );
542 bson_free( conn->lasterrstr );
543
544 conn->err = 0;
545 conn->errstr = NULL;
546 conn->lasterrcode = 0;
547 conn->lasterrstr = NULL;
548 }
549
550 /* Determine whether this BSON object is valid for the given operation. */
mongo_bson_valid(mongo * conn,bson * bson,int write)551 static int mongo_bson_valid( mongo *conn, bson *bson, int write ) {
552 if( ! bson->finished ) {
553 conn->err = MONGO_BSON_NOT_FINISHED;
554 return MONGO_ERROR;
555 }
556
557 if( bson->err & BSON_NOT_UTF8 ) {
558 conn->err = MONGO_BSON_INVALID;
559 return MONGO_ERROR;
560 }
561
562 if( write ) {
563 if( ( bson->err & BSON_FIELD_HAS_DOT ) ||
564 ( bson->err & BSON_FIELD_INIT_DOLLAR ) ) {
565
566 conn->err = MONGO_BSON_INVALID;
567 return MONGO_ERROR;
568
569 }
570 }
571
572 conn->err = 0;
573 conn->errstr = NULL;
574
575 return MONGO_OK;
576 }
577
578 /* Determine whether this BSON object is valid for the given operation. */
mongo_cursor_bson_valid(mongo_cursor * cursor,bson * bson)579 static int mongo_cursor_bson_valid( mongo_cursor *cursor, bson *bson ) {
580 if( ! bson->finished ) {
581 cursor->err = MONGO_CURSOR_BSON_ERROR;
582 cursor->conn->err = MONGO_BSON_NOT_FINISHED;
583 return MONGO_ERROR;
584 }
585
586 if( bson->err & BSON_NOT_UTF8 ) {
587 cursor->err = MONGO_CURSOR_BSON_ERROR;
588 cursor->conn->err = MONGO_BSON_INVALID;
589 return MONGO_ERROR;
590 }
591
592 return MONGO_OK;
593 }
594
595 /* MongoDB CRUD API */
596
mongo_insert_batch(mongo * conn,const char * ns,bson ** bsons,int count)597 MONGO_EXPORT int mongo_insert_batch( mongo *conn, const char *ns,
598 bson **bsons, int count ) {
599
600 int size = 16 + 4 + strlen( ns ) + 1;
601 int i;
602 mongo_message *mm;
603 char *data;
604
605 for( i=0; i<count; i++ ) {
606 size += bson_size( bsons[i] );
607 if( mongo_bson_valid( conn, bsons[i], 1 ) != MONGO_OK )
608 return MONGO_ERROR;
609 }
610
611 mm = mongo_message_create( size , 0 , 0 , MONGO_OP_INSERT );
612
613 data = &mm->data;
614 data = mongo_data_append32( data, &ZERO );
615 data = mongo_data_append( data, ns, strlen( ns ) + 1 );
616
617 for( i=0; i<count; i++ ) {
618 data = mongo_data_append( data, bsons[i]->data, bson_size( bsons[i] ) );
619 }
620
621 return mongo_message_send( conn, mm );
622 }
623
mongo_insert(mongo * conn,const char * ns,bson * bson)624 MONGO_EXPORT int mongo_insert( mongo *conn , const char *ns , bson *bson ) {
625
626 char *data;
627 mongo_message *mm;
628
629 /* Make sure that BSON is valid for insert. */
630 if( mongo_bson_valid( conn, bson, 1 ) != MONGO_OK ) {
631 return MONGO_ERROR;
632 }
633
634 mm = mongo_message_create( 16 /* header */
635 + 4 /* ZERO */
636 + strlen( ns )
637 + 1 + bson_size( bson )
638 , 0, 0, MONGO_OP_INSERT );
639
640 data = &mm->data;
641 data = mongo_data_append32( data, &ZERO );
642 data = mongo_data_append( data, ns, strlen( ns ) + 1 );
643 data = mongo_data_append( data, bson->data, bson_size( bson ) );
644
645 return mongo_message_send( conn, mm );
646 }
647
mongo_update(mongo * conn,const char * ns,const bson * cond,const bson * op,int flags)648 MONGO_EXPORT int mongo_update( mongo *conn, const char *ns, const bson *cond,
649 const bson *op, int flags ) {
650
651 char *data;
652 mongo_message *mm;
653
654 /* Make sure that the op BSON is valid UTF-8.
655 * TODO: decide whether to check cond as well.
656 * */
657 if( mongo_bson_valid( conn, ( bson * )op, 0 ) != MONGO_OK ) {
658 return MONGO_ERROR;
659 }
660
661 mm = mongo_message_create( 16 /* header */
662 + 4 /* ZERO */
663 + strlen( ns ) + 1
664 + 4 /* flags */
665 + bson_size( cond )
666 + bson_size( op )
667 , 0 , 0 , MONGO_OP_UPDATE );
668
669 data = &mm->data;
670 data = mongo_data_append32( data, &ZERO );
671 data = mongo_data_append( data, ns, strlen( ns ) + 1 );
672 data = mongo_data_append32( data, &flags );
673 data = mongo_data_append( data, cond->data, bson_size( cond ) );
674 data = mongo_data_append( data, op->data, bson_size( op ) );
675
676 return mongo_message_send( conn, mm );
677 }
678
mongo_remove(mongo * conn,const char * ns,const bson * cond)679 MONGO_EXPORT int mongo_remove( mongo *conn, const char *ns, const bson *cond ) {
680 char *data;
681 mongo_message *mm;
682
683 /* Make sure that the BSON is valid UTF-8.
684 * TODO: decide whether to check cond as well.
685 * */
686 if( mongo_bson_valid( conn, ( bson * )cond, 0 ) != MONGO_OK ) {
687 return MONGO_ERROR;
688 }
689
690 mm = mongo_message_create( 16 /* header */
691 + 4 /* ZERO */
692 + strlen( ns ) + 1
693 + 4 /* ZERO */
694 + bson_size( cond )
695 , 0 , 0 , MONGO_OP_DELETE );
696
697 data = &mm->data;
698 data = mongo_data_append32( data, &ZERO );
699 data = mongo_data_append( data, ns, strlen( ns ) + 1 );
700 data = mongo_data_append32( data, &ZERO );
701 data = mongo_data_append( data, cond->data, bson_size( cond ) );
702
703 return mongo_message_send( conn, mm );
704 }
705
706
mongo_cursor_op_query(mongo_cursor * cursor)707 static int mongo_cursor_op_query( mongo_cursor *cursor ) {
708 int res;
709 bson empty;
710 char *data;
711 mongo_message *mm;
712 bson temp;
713 bson_iterator it;
714
715 /* Clear any errors. */
716 bson_free( cursor->conn->lasterrstr );
717 cursor->conn->lasterrstr = NULL;
718 cursor->conn->lasterrcode = 0;
719 cursor->conn->err = 0;
720 cursor->err = 0;
721
722 /* Set up default values for query and fields, if necessary. */
723 if( ! cursor->query )
724 cursor->query = bson_empty( &empty );
725 else if( mongo_cursor_bson_valid( cursor, cursor->query ) != MONGO_OK )
726 return MONGO_ERROR;
727
728 if( ! cursor->fields )
729 cursor->fields = bson_empty( &empty );
730 else if( mongo_cursor_bson_valid( cursor, cursor->fields ) != MONGO_OK )
731 return MONGO_ERROR;
732
733 mm = mongo_message_create( 16 + /* header */
734 4 + /* options */
735 strlen( cursor->ns ) + 1 + /* ns */
736 4 + 4 + /* skip,return */
737 bson_size( cursor->query ) +
738 bson_size( cursor->fields ) ,
739 0 , 0 , MONGO_OP_QUERY );
740
741 data = &mm->data;
742 data = mongo_data_append32( data , &cursor->options );
743 data = mongo_data_append( data , cursor->ns , strlen( cursor->ns ) + 1 );
744 data = mongo_data_append32( data , &cursor->skip );
745 data = mongo_data_append32( data , &cursor->limit );
746 data = mongo_data_append( data , cursor->query->data , bson_size( cursor->query ) );
747 if ( cursor->fields )
748 data = mongo_data_append( data , cursor->fields->data , bson_size( cursor->fields ) );
749
750 bson_fatal_msg( ( data == ( ( char * )mm ) + mm->head.len ), "query building fail!" );
751
752 res = mongo_message_send( cursor->conn , mm );
753 if( res != MONGO_OK ) {
754 return MONGO_ERROR;
755 }
756
757 res = mongo_read_response( cursor->conn, ( mongo_reply ** )&( cursor->reply ) );
758 if( res != MONGO_OK ) {
759 return MONGO_ERROR;
760 }
761
762 if( cursor->reply->fields.num == 1 ) {
763 bson_init_data( &temp, &cursor->reply->objs );
764 if( bson_find( &it, &temp, "$err" ) ) {
765 cursor->conn->lasterrstr =
766 (char *)bson_malloc( bson_iterator_string_len( &it ) );
767 strcpy( cursor->conn->lasterrstr, bson_iterator_string( &it ) );
768 bson_find( &it, &temp, "code" );
769 cursor->conn->lasterrcode = bson_iterator_int( &it );
770 cursor->err = MONGO_CURSOR_QUERY_FAIL;
771 return MONGO_ERROR;
772 }
773 }
774
775 cursor->seen += cursor->reply->fields.num;
776 cursor->flags |= MONGO_CURSOR_QUERY_SENT;
777 return MONGO_OK;
778 }
779
mongo_cursor_get_more(mongo_cursor * cursor)780 static int mongo_cursor_get_more( mongo_cursor *cursor ) {
781 int res;
782
783 if( cursor->limit > 0 && cursor->seen >= cursor->limit ) {
784 cursor->err = MONGO_CURSOR_EXHAUSTED;
785 return MONGO_ERROR;
786 } else if( ! cursor->reply ) {
787 cursor->err = MONGO_CURSOR_INVALID;
788 return MONGO_ERROR;
789 } else if( ! cursor->reply->fields.cursorID ) {
790 cursor->err = MONGO_CURSOR_EXHAUSTED;
791 return MONGO_ERROR;
792 } else {
793 char *data;
794 int sl = strlen( cursor->ns )+1;
795 int limit = 0;
796 mongo_message *mm;
797
798 if( cursor->limit > 0 )
799 limit = cursor->limit - cursor->seen;
800
801 mm = mongo_message_create( 16 /*header*/
802 +4 /*ZERO*/
803 +sl
804 +4 /*numToReturn*/
805 +8 /*cursorID*/
806 , 0, 0, MONGO_OP_GET_MORE );
807 data = &mm->data;
808 data = mongo_data_append32( data, &ZERO );
809 data = mongo_data_append( data, cursor->ns, sl );
810 data = mongo_data_append32( data, &limit );
811 data = mongo_data_append64( data, &cursor->reply->fields.cursorID );
812
813 bson_free( cursor->reply );
814 res = mongo_message_send( cursor->conn, mm );
815 if( res != MONGO_OK ) {
816 mongo_cursor_destroy( cursor );
817 return MONGO_ERROR;
818 }
819
820 res = mongo_read_response( cursor->conn, &( cursor->reply ) );
821 if( res != MONGO_OK ) {
822 mongo_cursor_destroy( cursor );
823 return MONGO_ERROR;
824 }
825 cursor->current.data = NULL;
826 cursor->seen += cursor->reply->fields.num;
827
828 return MONGO_OK;
829 }
830 }
831
mongo_find(mongo * conn,const char * ns,bson * query,bson * fields,int limit,int skip,int options)832 MONGO_EXPORT mongo_cursor *mongo_find( mongo *conn, const char *ns, bson *query,
833 bson *fields, int limit, int skip, int options ) {
834
835 mongo_cursor *cursor = ( mongo_cursor * )bson_malloc( sizeof( mongo_cursor ) );
836 mongo_cursor_init( cursor, conn, ns );
837 cursor->flags |= MONGO_CURSOR_MUST_FREE;
838
839 mongo_cursor_set_query( cursor, query );
840 mongo_cursor_set_fields( cursor, fields );
841 mongo_cursor_set_limit( cursor, limit );
842 mongo_cursor_set_skip( cursor, skip );
843 mongo_cursor_set_options( cursor, options );
844
845 if( mongo_cursor_op_query( cursor ) == MONGO_OK )
846 return cursor;
847 else {
848 mongo_cursor_destroy( cursor );
849 return NULL;
850 }
851 }
852
mongo_find_one(mongo * conn,const char * ns,bson * query,bson * fields,bson * out)853 MONGO_EXPORT int mongo_find_one( mongo *conn, const char *ns, bson *query,
854 bson *fields, bson *out ) {
855
856 mongo_cursor cursor[1];
857 mongo_cursor_init( cursor, conn, ns );
858 mongo_cursor_set_query( cursor, query );
859 mongo_cursor_set_fields( cursor, fields );
860 mongo_cursor_set_limit( cursor, 1 );
861
862 if ( mongo_cursor_next( cursor ) == MONGO_OK ) {
863 bson_init_size( out, bson_size( (bson *)&cursor->current ) );
864 memcpy( out->data, cursor->current.data,
865 bson_size( (bson *)&cursor->current ) );
866 out->finished = 1;
867 mongo_cursor_destroy( cursor );
868 return MONGO_OK;
869 } else {
870 mongo_cursor_destroy( cursor );
871 return MONGO_ERROR;
872 }
873 }
874
mongo_cursor_init(mongo_cursor * cursor,mongo * conn,const char * ns)875 void mongo_cursor_init( mongo_cursor *cursor, mongo *conn, const char *ns ) {
876 memset( cursor, 0, sizeof( mongo_cursor ) );
877 cursor->conn = conn;
878 cursor->ns = ( const char * )bson_malloc( strlen( ns ) + 1 );
879 strncpy( ( char * )cursor->ns, ns, strlen( ns ) + 1 );
880 cursor->current.data = NULL;
881 }
882
mongo_cursor_set_query(mongo_cursor * cursor,bson * query)883 void mongo_cursor_set_query( mongo_cursor *cursor, bson *query ) {
884 cursor->query = query;
885 }
886
mongo_cursor_set_fields(mongo_cursor * cursor,bson * fields)887 void mongo_cursor_set_fields( mongo_cursor *cursor, bson *fields ) {
888 cursor->fields = fields;
889 }
890
mongo_cursor_set_skip(mongo_cursor * cursor,int skip)891 void mongo_cursor_set_skip( mongo_cursor *cursor, int skip ) {
892 cursor->skip = skip;
893 }
894
mongo_cursor_set_limit(mongo_cursor * cursor,int limit)895 void mongo_cursor_set_limit( mongo_cursor *cursor, int limit ) {
896 cursor->limit = limit;
897 }
898
mongo_cursor_set_options(mongo_cursor * cursor,int options)899 void mongo_cursor_set_options( mongo_cursor *cursor, int options ) {
900 cursor->options = options;
901 }
902
mongo_cursor_data(mongo_cursor * cursor)903 const char *mongo_cursor_data( mongo_cursor *cursor ) {
904 return cursor->current.data;
905 }
906
mongo_cursor_bson(mongo_cursor * cursor)907 MONGO_EXPORT const bson *mongo_cursor_bson( mongo_cursor *cursor ) {
908 return (const bson *)&(cursor->current);
909 }
910
mongo_cursor_next(mongo_cursor * cursor)911 MONGO_EXPORT int mongo_cursor_next( mongo_cursor *cursor ) {
912 char *next_object;
913 char *message_end;
914
915 if( ! ( cursor->flags & MONGO_CURSOR_QUERY_SENT ) )
916 if( mongo_cursor_op_query( cursor ) != MONGO_OK )
917 return MONGO_ERROR;
918
919 if( !cursor->reply )
920 return MONGO_ERROR;
921
922 /* no data */
923 if ( cursor->reply->fields.num == 0 ) {
924
925 /* Special case for tailable cursors. */
926 if( cursor->reply->fields.cursorID ) {
927 if( ( mongo_cursor_get_more( cursor ) != MONGO_OK ) ||
928 cursor->reply->fields.num == 0 ) {
929 return MONGO_ERROR;
930 }
931 }
932
933 else
934 return MONGO_ERROR;
935 }
936
937 /* first */
938 if ( cursor->current.data == NULL ) {
939 bson_init_finished_data( &cursor->current, &cursor->reply->objs );
940 return MONGO_OK;
941 }
942
943 next_object = cursor->current.data + bson_size( &cursor->current );
944 message_end = ( char * )cursor->reply + cursor->reply->head.len;
945
946 if ( next_object >= message_end ) {
947 if( mongo_cursor_get_more( cursor ) != MONGO_OK )
948 return MONGO_ERROR;
949
950 /* If there's still a cursor id, then the message should be pending. */
951 if( cursor->reply->fields.num == 0 && cursor->reply->fields.cursorID ) {
952 cursor->err = MONGO_CURSOR_PENDING;
953 return MONGO_ERROR;
954 }
955
956 bson_init_finished_data( &cursor->current, &cursor->reply->objs );
957 } else {
958 bson_init_finished_data( &cursor->current, next_object );
959 }
960
961 return MONGO_OK;
962 }
963
mongo_cursor_destroy(mongo_cursor * cursor)964 MONGO_EXPORT int mongo_cursor_destroy( mongo_cursor *cursor ) {
965 int result = MONGO_OK;
966
967 if ( !cursor ) return result;
968
969 /* Kill cursor if live. */
970 if ( cursor->reply && cursor->reply->fields.cursorID ) {
971 mongo *conn = cursor->conn;
972 mongo_message *mm = mongo_message_create( 16 /*header*/
973 +4 /*ZERO*/
974 +4 /*numCursors*/
975 +8 /*cursorID*/
976 , 0, 0, MONGO_OP_KILL_CURSORS );
977 char *data = &mm->data;
978 data = mongo_data_append32( data, &ZERO );
979 data = mongo_data_append32( data, &ONE );
980 data = mongo_data_append64( data, &cursor->reply->fields.cursorID );
981
982 result = mongo_message_send( conn, mm );
983 }
984
985 bson_free( cursor->reply );
986 bson_free( ( void * )cursor->ns );
987
988 if( cursor->flags & MONGO_CURSOR_MUST_FREE )
989 bson_free( cursor );
990
991 return result;
992 }
993
994 /* MongoDB Helper Functions */
995
mongo_create_index(mongo * conn,const char * ns,bson * key,int options,bson * out)996 MONGO_EXPORT int mongo_create_index( mongo *conn, const char *ns, bson *key, int options, bson *out ) {
997 bson b;
998 bson_iterator it;
999 char name[255] = {'_'};
1000 int i = 1;
1001 char idxns[1024];
1002
1003 bson_iterator_init( &it, key );
1004 while( i < 255 && bson_iterator_next( &it ) ) {
1005 strncpy( name + i, bson_iterator_key( &it ), 255 - i );
1006 i += strlen( bson_iterator_key( &it ) );
1007 }
1008 name[254] = '\0';
1009
1010 bson_init( &b );
1011 bson_append_bson( &b, "key", key );
1012 bson_append_string( &b, "ns", ns );
1013 bson_append_string( &b, "name", name );
1014 if ( options & MONGO_INDEX_UNIQUE )
1015 bson_append_bool( &b, "unique", 1 );
1016 if ( options & MONGO_INDEX_DROP_DUPS )
1017 bson_append_bool( &b, "dropDups", 1 );
1018 if ( options & MONGO_INDEX_BACKGROUND )
1019 bson_append_bool( &b, "background", 1 );
1020 if ( options & MONGO_INDEX_SPARSE )
1021 bson_append_bool( &b, "sparse", 1 );
1022 bson_finish( &b );
1023
1024 strncpy( idxns, ns, 1024-16 );
1025 strcpy( strchr( idxns, '.' ), ".system.indexes" );
1026 mongo_insert( conn, idxns, &b );
1027 bson_destroy( &b );
1028
1029 *strchr( idxns, '.' ) = '\0'; /* just db not ns */
1030 return mongo_cmd_get_last_error( conn, idxns, out );
1031 }
1032
mongo_create_simple_index(mongo * conn,const char * ns,const char * field,int options,bson * out)1033 bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char *field, int options, bson *out ) {
1034 bson b;
1035 bson_bool_t success;
1036
1037 bson_init( &b );
1038 bson_append_int( &b, field, 1 );
1039 bson_finish( &b );
1040
1041 success = mongo_create_index( conn, ns, &b, options, out );
1042 bson_destroy( &b );
1043 return success;
1044 }
1045
mongo_count(mongo * conn,const char * db,const char * ns,bson * query)1046 MONGO_EXPORT double mongo_count( mongo *conn, const char *db, const char *ns, bson *query ) {
1047 bson cmd;
1048 bson out = {NULL, 0};
1049 double count = -1;
1050
1051 bson_init( &cmd );
1052 bson_append_string( &cmd, "count", ns );
1053 if ( query && bson_size( query ) > 5 ) /* not empty */
1054 bson_append_bson( &cmd, "query", query );
1055 bson_finish( &cmd );
1056
1057 if( mongo_run_command( conn, db, &cmd, &out ) == MONGO_OK ) {
1058 bson_iterator it;
1059 if( bson_find( &it, &out, "n" ) )
1060 count = bson_iterator_double( &it );
1061 bson_destroy( &cmd );
1062 bson_destroy( &out );
1063 return count;
1064 } else {
1065 bson_destroy( &out );
1066 bson_destroy( &cmd );
1067 return MONGO_ERROR;
1068 }
1069 }
1070
mongo_run_command(mongo * conn,const char * db,bson * command,bson * out)1071 MONGO_EXPORT int mongo_run_command( mongo *conn, const char *db, bson *command,
1072 bson *out ) {
1073
1074 bson response = {NULL, 0};
1075 bson fields;
1076 int sl = strlen( db );
1077 char *ns = bson_malloc( sl + 5 + 1 ); /* ".$cmd" + nul */
1078 int res, success = 0;
1079
1080 strcpy( ns, db );
1081 strcpy( ns+sl, ".$cmd" );
1082
1083 res = mongo_find_one( conn, ns, command, bson_empty( &fields ), &response );
1084 bson_free( ns );
1085
1086 if( res != MONGO_OK )
1087 return MONGO_ERROR;
1088 else {
1089 bson_iterator it;
1090 if( bson_find( &it, &response, "ok" ) )
1091 success = bson_iterator_bool( &it );
1092
1093 if( !success ) {
1094 conn->err = MONGO_COMMAND_FAILED;
1095 return MONGO_ERROR;
1096 } else {
1097 if( out )
1098 *out = response;
1099 return MONGO_OK;
1100 }
1101 }
1102 }
1103
mongo_simple_int_command(mongo * conn,const char * db,const char * cmdstr,int arg,bson * realout)1104 int mongo_simple_int_command( mongo *conn, const char *db,
1105 const char *cmdstr, int arg, bson *realout ) {
1106
1107 bson out = {NULL, 0};
1108 bson cmd;
1109 int result;
1110
1111 bson_init( &cmd );
1112 bson_append_int( &cmd, cmdstr, arg );
1113 bson_finish( &cmd );
1114
1115 result = mongo_run_command( conn, db, &cmd, &out );
1116
1117 bson_destroy( &cmd );
1118
1119 if ( realout )
1120 *realout = out;
1121 else
1122 bson_destroy( &out );
1123
1124 return result;
1125 }
1126
mongo_simple_str_command(mongo * conn,const char * db,const char * cmdstr,const char * arg,bson * realout)1127 int mongo_simple_str_command( mongo *conn, const char *db,
1128 const char *cmdstr, const char *arg, bson *realout ) {
1129
1130 bson out = {NULL, 0};
1131 int result;
1132
1133 bson cmd;
1134 bson_init( &cmd );
1135 bson_append_string( &cmd, cmdstr, arg );
1136 bson_finish( &cmd );
1137
1138 result = mongo_run_command( conn, db, &cmd, &out );
1139
1140 bson_destroy( &cmd );
1141
1142 if ( realout )
1143 *realout = out;
1144 else
1145 bson_destroy( &out );
1146
1147 return result;
1148 }
1149
mongo_cmd_drop_db(mongo * conn,const char * db)1150 MONGO_EXPORT int mongo_cmd_drop_db( mongo *conn, const char *db ) {
1151 return mongo_simple_int_command( conn, db, "dropDatabase", 1, NULL );
1152 }
1153
mongo_cmd_drop_collection(mongo * conn,const char * db,const char * collection,bson * out)1154 MONGO_EXPORT int mongo_cmd_drop_collection( mongo *conn, const char *db, const char *collection, bson *out ) {
1155 return mongo_simple_str_command( conn, db, "drop", collection, out );
1156 }
1157
mongo_cmd_reset_error(mongo * conn,const char * db)1158 void mongo_cmd_reset_error( mongo *conn, const char *db ) {
1159 mongo_simple_int_command( conn, db, "reseterror", 1, NULL );
1160 }
1161
mongo_cmd_get_error_helper(mongo * conn,const char * db,bson * realout,const char * cmdtype)1162 static int mongo_cmd_get_error_helper( mongo *conn, const char *db,
1163 bson *realout, const char *cmdtype ) {
1164
1165 bson out = {NULL,0};
1166 bson_bool_t haserror = 0;
1167
1168 /* Reset last error codes. */
1169 conn->lasterrcode = 0;
1170 bson_free( conn->lasterrstr );
1171 conn->lasterrstr = NULL;
1172
1173 /* If there's an error, store its code and string in the connection object. */
1174 if( mongo_simple_int_command( conn, db, cmdtype, 1, &out ) == MONGO_OK ) {
1175 bson_iterator it;
1176 haserror = ( bson_find( &it, &out, "err" ) != BSON_NULL );
1177 if( haserror ) {
1178 conn->lasterrstr = ( char * )bson_malloc( bson_iterator_string_len( &it ) );
1179 if( conn->lasterrstr ) {
1180 strcpy( conn->lasterrstr, bson_iterator_string( &it ) );
1181 }
1182
1183 if( bson_find( &it, &out, "code" ) != BSON_NULL )
1184 conn->lasterrcode = bson_iterator_int( &it );
1185 }
1186 }
1187
1188 if( realout )
1189 *realout = out; /* transfer of ownership */
1190 else
1191 bson_destroy( &out );
1192
1193 if( haserror )
1194 return MONGO_ERROR;
1195 else
1196 return MONGO_OK;
1197 }
1198
mongo_cmd_get_prev_error(mongo * conn,const char * db,bson * out)1199 MONGO_EXPORT int mongo_cmd_get_prev_error( mongo *conn, const char *db, bson *out ) {
1200 return mongo_cmd_get_error_helper( conn, db, out, "getpreverror" );
1201 }
1202
mongo_cmd_get_last_error(mongo * conn,const char * db,bson * out)1203 MONGO_EXPORT int mongo_cmd_get_last_error( mongo *conn, const char *db, bson *out ) {
1204 return mongo_cmd_get_error_helper( conn, db, out, "getlasterror" );
1205 }
1206
mongo_cmd_ismaster(mongo * conn,bson * realout)1207 MONGO_EXPORT bson_bool_t mongo_cmd_ismaster( mongo *conn, bson *realout ) {
1208 bson out = {NULL,0};
1209 bson_bool_t ismaster = 0;
1210
1211 if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) {
1212 bson_iterator it;
1213 bson_find( &it, &out, "ismaster" );
1214 ismaster = bson_iterator_bool( &it );
1215 }
1216
1217 if( realout )
1218 *realout = out; /* transfer of ownership */
1219 else
1220 bson_destroy( &out );
1221
1222 return ismaster;
1223 }
1224
digest2hex(mongo_md5_byte_t digest[16],char hex_digest[33])1225 static void digest2hex( mongo_md5_byte_t digest[16], char hex_digest[33] ) {
1226 static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1227 int i;
1228 for ( i=0; i<16; i++ ) {
1229 hex_digest[2*i] = hex[( digest[i] & 0xf0 ) >> 4];
1230 hex_digest[2*i + 1] = hex[ digest[i] & 0x0f ];
1231 }
1232 hex_digest[32] = '\0';
1233 }
1234
mongo_pass_digest(const char * user,const char * pass,char hex_digest[33])1235 static void mongo_pass_digest( const char *user, const char *pass, char hex_digest[33] ) {
1236 mongo_md5_state_t st;
1237 mongo_md5_byte_t digest[16];
1238
1239 mongo_md5_init( &st );
1240 mongo_md5_append( &st, ( const mongo_md5_byte_t * )user, strlen( user ) );
1241 mongo_md5_append( &st, ( const mongo_md5_byte_t * )":mongo:", 7 );
1242 mongo_md5_append( &st, ( const mongo_md5_byte_t * )pass, strlen( pass ) );
1243 mongo_md5_finish( &st, digest );
1244 digest2hex( digest, hex_digest );
1245 }
1246
mongo_cmd_add_user(mongo * conn,const char * db,const char * user,const char * pass)1247 MONGO_EXPORT int mongo_cmd_add_user( mongo *conn, const char *db, const char *user, const char *pass ) {
1248 bson user_obj;
1249 bson pass_obj;
1250 char hex_digest[33];
1251 char *ns = bson_malloc( strlen( db ) + strlen( ".system.users" ) + 1 );
1252 int res;
1253
1254 strcpy( ns, db );
1255 strcpy( ns+strlen( db ), ".system.users" );
1256
1257 mongo_pass_digest( user, pass, hex_digest );
1258
1259 bson_init( &user_obj );
1260 bson_append_string( &user_obj, "user", user );
1261 bson_finish( &user_obj );
1262
1263 bson_init( &pass_obj );
1264 bson_append_start_object( &pass_obj, "$set" );
1265 bson_append_string( &pass_obj, "pwd", hex_digest );
1266 bson_append_finish_object( &pass_obj );
1267 bson_finish( &pass_obj );
1268
1269 res = mongo_update( conn, ns, &user_obj, &pass_obj, MONGO_UPDATE_UPSERT );
1270
1271 bson_free( ns );
1272 bson_destroy( &user_obj );
1273 bson_destroy( &pass_obj );
1274
1275 return res;
1276 }
1277
mongo_cmd_authenticate(mongo * conn,const char * db,const char * user,const char * pass)1278 MONGO_EXPORT bson_bool_t mongo_cmd_authenticate( mongo *conn, const char *db, const char *user, const char *pass ) {
1279 bson from_db;
1280 bson cmd;
1281 bson out;
1282 const char *nonce;
1283 int result;
1284
1285 mongo_md5_state_t st;
1286 mongo_md5_byte_t digest[16];
1287 char hex_digest[33];
1288
1289 if( mongo_simple_int_command( conn, db, "getnonce", 1, &from_db ) == MONGO_OK ) {
1290 bson_iterator it;
1291 bson_find( &it, &from_db, "nonce" );
1292 nonce = bson_iterator_string( &it );
1293 } else {
1294 return MONGO_ERROR;
1295 }
1296
1297 mongo_pass_digest( user, pass, hex_digest );
1298
1299 mongo_md5_init( &st );
1300 mongo_md5_append( &st, ( const mongo_md5_byte_t * )nonce, strlen( nonce ) );
1301 mongo_md5_append( &st, ( const mongo_md5_byte_t * )user, strlen( user ) );
1302 mongo_md5_append( &st, ( const mongo_md5_byte_t * )hex_digest, 32 );
1303 mongo_md5_finish( &st, digest );
1304 digest2hex( digest, hex_digest );
1305
1306 bson_init( &cmd );
1307 bson_append_int( &cmd, "authenticate", 1 );
1308 bson_append_string( &cmd, "user", user );
1309 bson_append_string( &cmd, "nonce", nonce );
1310 bson_append_string( &cmd, "key", hex_digest );
1311 bson_finish( &cmd );
1312
1313 bson_destroy( &from_db );
1314
1315 result = mongo_run_command( conn, db, &cmd, &out );
1316
1317 bson_destroy( &from_db );
1318 bson_destroy( &cmd );
1319
1320 return result;
1321 }
1322