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