bson_ext 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,91 @@
1
+ /*
2
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
3
+
4
+ This software is provided 'as-is', without any express or implied
5
+ warranty. In no event will the authors be held liable for any damages
6
+ arising from the use of this software.
7
+
8
+ Permission is granted to anyone to use this software for any purpose,
9
+ including commercial applications, and to alter it and redistribute it
10
+ freely, subject to the following restrictions:
11
+
12
+ 1. The origin of this software must not be misrepresented; you must not
13
+ claim that you wrote the original software. If you use this software
14
+ in a product, an acknowledgment in the product documentation would be
15
+ appreciated but is not required.
16
+ 2. Altered source versions must be plainly marked as such, and must not be
17
+ misrepresented as being the original software.
18
+ 3. This notice may not be removed or altered from any source distribution.
19
+
20
+ L. Peter Deutsch
21
+ ghost@aladdin.com
22
+
23
+ */
24
+ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
25
+ /*
26
+ Independent implementation of MD5 (RFC 1321).
27
+
28
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
29
+ text is available at
30
+ http://www.ietf.org/rfc/rfc1321.txt
31
+ The code is derived from the text of the RFC, including the test suite
32
+ (section A.5) but excluding the rest of Appendix A. It does not include
33
+ any code or documentation that is identified in the RFC as being
34
+ copyrighted.
35
+
36
+ The original and principal author of md5.h is L. Peter Deutsch
37
+ <ghost@aladdin.com>. Other authors are noted in the change history
38
+ that follows (in reverse chronological order):
39
+
40
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
41
+ references to Ghostscript; clarified derivation from RFC 1321;
42
+ now handles byte order either statically or dynamically.
43
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
44
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
45
+ added conditionalization for C++ compilation from Martin
46
+ Purschke <purschke@bnl.gov>.
47
+ 1999-05-03 lpd Original version.
48
+ */
49
+
50
+ #ifndef md5_INCLUDED
51
+ # define md5_INCLUDED
52
+
53
+ /*
54
+ * This package supports both compile-time and run-time determination of CPU
55
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
56
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
57
+ * defined as non-zero, the code will be compiled to run only on big-endian
58
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
59
+ * run on either big- or little-endian CPUs, but will run slightly less
60
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
61
+ */
62
+
63
+ typedef unsigned char mongo_md5_byte_t; /* 8-bit byte */
64
+ typedef unsigned int mongo_md5_word_t; /* 32-bit word */
65
+
66
+ /* Define the state of the MD5 Algorithm. */
67
+ typedef struct mongo_md5_state_s {
68
+ mongo_md5_word_t count[2]; /* message length in bits, lsw first */
69
+ mongo_md5_word_t abcd[4]; /* digest buffer */
70
+ mongo_md5_byte_t buf[64]; /* accumulate block */
71
+ } mongo_md5_state_t;
72
+
73
+ #ifdef __cplusplus
74
+ extern "C"
75
+ {
76
+ #endif
77
+
78
+ /* Initialize the algorithm. */
79
+ void mongo_md5_init(mongo_md5_state_t *pms);
80
+
81
+ /* Append a string to the message. */
82
+ void mongo_md5_append(mongo_md5_state_t *pms, const mongo_md5_byte_t *data, int nbytes);
83
+
84
+ /* Finish the message and return the digest. */
85
+ void mongo_md5_finish(mongo_md5_state_t *pms, mongo_md5_byte_t digest[16]);
86
+
87
+ #ifdef __cplusplus
88
+ } /* end extern "C" */
89
+ #endif
90
+
91
+ #endif /* md5_INCLUDED */
@@ -0,0 +1,1020 @@
1
+ /* mongo.c */
2
+
3
+ /* Copyright 2009, 2010 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 <errno.h>
24
+ #include <string.h>
25
+ #include <stdlib.h>
26
+ #include <errno.h>
27
+
28
+ #ifndef _WIN32
29
+ #include <unistd.h>
30
+ #endif
31
+
32
+ /* only need one of these */
33
+ static const int zero = 0;
34
+ static const int one = 1;
35
+
36
+ /* ----------------------------
37
+ message stuff
38
+ ------------------------------ */
39
+
40
+ static void looping_write(mongo_connection * conn, const void* buf, int len){
41
+ const char* cbuf = buf;
42
+ while (len){
43
+ int sent = send(conn->sock, cbuf, len, 0);
44
+ if (sent == -1) MONGO_THROW(MONGO_EXCEPT_NETWORK);
45
+ cbuf += sent;
46
+ len -= sent;
47
+ }
48
+ }
49
+
50
+ static void looping_read(mongo_connection * conn, void* buf, int len){
51
+ char* cbuf = buf;
52
+ while (len){
53
+ int sent = recv(conn->sock, cbuf, len, 0);
54
+ if (sent == 0 || sent == -1) MONGO_THROW(MONGO_EXCEPT_NETWORK);
55
+ cbuf += sent;
56
+ len -= sent;
57
+ }
58
+ }
59
+
60
+ /* Always calls free(mm) */
61
+ void mongo_message_send(mongo_connection * conn, mongo_message* mm){
62
+ mongo_header head; /* little endian */
63
+ bson_little_endian32(&head.len, &mm->head.len);
64
+ bson_little_endian32(&head.id, &mm->head.id);
65
+ bson_little_endian32(&head.responseTo, &mm->head.responseTo);
66
+ bson_little_endian32(&head.op, &mm->head.op);
67
+
68
+ MONGO_TRY{
69
+ looping_write(conn, &head, sizeof(head));
70
+ looping_write(conn, &mm->data, mm->head.len - sizeof(head));
71
+ }MONGO_CATCH{
72
+ free(mm);
73
+ MONGO_RETHROW();
74
+ }
75
+ free(mm);
76
+ }
77
+
78
+ char * mongo_data_append( char * start , const void * data , int len ){
79
+ memcpy( start , data , len );
80
+ return start + len;
81
+ }
82
+
83
+ char * mongo_data_append32( char * start , const void * data){
84
+ bson_little_endian32( start , data );
85
+ return start + 4;
86
+ }
87
+
88
+ char * mongo_data_append64( char * start , const void * data){
89
+ bson_little_endian64( start , data );
90
+ return start + 8;
91
+ }
92
+
93
+ mongo_message * mongo_message_create( int len , int id , int responseTo , int op ){
94
+ mongo_message * mm = (mongo_message*)bson_malloc( len );
95
+
96
+ if (!id)
97
+ id = rand();
98
+
99
+ /* native endian (converted on send) */
100
+ mm->head.len = len;
101
+ mm->head.id = id;
102
+ mm->head.responseTo = responseTo;
103
+ mm->head.op = op;
104
+
105
+ return mm;
106
+ }
107
+
108
+ /* ----------------------------
109
+ connection stuff
110
+ ------------------------------ */
111
+ #ifdef _MONGO_USE_GETADDRINFO
112
+ static int mongo_socket_connect( mongo_connection * conn, const char * host, int port ){
113
+
114
+ struct addrinfo* addrs = NULL;
115
+ struct addrinfo hints;
116
+ char port_str[12];
117
+ int ret;
118
+
119
+ conn->sock = 0;
120
+ conn->connected = 0;
121
+
122
+ memset( &hints, 0, sizeof( hints ) );
123
+ hints.ai_family = AF_INET;
124
+
125
+ sprintf( port_str, "%d", port );
126
+
127
+ conn->sock = socket( AF_INET, SOCK_STREAM, 0 );
128
+ if ( conn->sock <= 0 ){
129
+ mongo_close_socket( conn->sock );
130
+ return mongo_conn_no_socket;
131
+ }
132
+
133
+ ret = getaddrinfo( host, port_str, &hints, &addrs );
134
+ if(ret) {
135
+ fprintf( stderr, "getaddrinfo failed: %s", gai_strerror( ret ) );
136
+ return mongo_conn_fail;
137
+ }
138
+
139
+ if ( connect( conn->sock, addrs->ai_addr, addrs->ai_addrlen ) ){
140
+ mongo_close_socket( conn->sock );
141
+ freeaddrinfo( addrs );
142
+ return mongo_conn_fail;
143
+ }
144
+
145
+ setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one) );
146
+
147
+ conn->connected = 1;
148
+ freeaddrinfo( addrs );
149
+ return 0;
150
+ }
151
+ #else
152
+ static int mongo_socket_connect( mongo_connection * conn, const char * host, int port ){
153
+ struct sockaddr_in sa;
154
+ socklen_t addressSize;
155
+
156
+ memset( sa.sin_zero , 0 , sizeof( sa.sin_zero ) );
157
+ sa.sin_family = AF_INET;
158
+ sa.sin_port = htons( port );
159
+ sa.sin_addr.s_addr = inet_addr( host );
160
+ addressSize = sizeof( sa );
161
+
162
+ conn->sock = socket( AF_INET, SOCK_STREAM, 0 );
163
+ if ( conn->sock <= 0 ){
164
+ mongo_close_socket( conn->sock );
165
+ return mongo_conn_no_socket;
166
+ }
167
+
168
+ if ( connect( conn->sock, (struct sockaddr *)&sa, addressSize ) ){
169
+ return mongo_conn_fail;
170
+ }
171
+
172
+ setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one) );
173
+
174
+ conn->connected = 1;
175
+ return 0;
176
+ }
177
+ #endif
178
+
179
+ mongo_conn_return mongo_connect( mongo_connection * conn , const char * host, int port ){
180
+ MONGO_INIT_EXCEPTION(&conn->exception);
181
+ conn->replset = NULL;
182
+
183
+ conn->primary = bson_malloc( sizeof( mongo_host_port ) );
184
+
185
+ strncpy( conn->primary->host, host, strlen( host ) + 1 );
186
+ conn->primary->port = port;
187
+ conn->primary->next = NULL;
188
+
189
+ return mongo_socket_connect(conn, host, port);
190
+ }
191
+
192
+ void mongo_replset_init_conn( mongo_connection* conn, const char* name ) {
193
+ MONGO_INIT_EXCEPTION(&conn->exception);
194
+ conn->replset = bson_malloc( sizeof( mongo_replset ) );
195
+ conn->replset->primary_connected = 0;
196
+ conn->replset->seeds = NULL;
197
+ conn->replset->hosts = NULL;
198
+ conn->replset->name = (char *)bson_malloc( sizeof( name ) + 1 );
199
+ memcpy( conn->replset->name, name, sizeof( name ) + 1 );
200
+
201
+ conn->primary = bson_malloc( sizeof( mongo_host_port ) );
202
+ conn->primary = NULL;
203
+ }
204
+
205
+ static int mongo_replset_add_node( mongo_host_port** list, const char* host, int port ) {
206
+ mongo_host_port* host_port = bson_malloc( sizeof( mongo_host_port ) );
207
+ host_port->port = port;
208
+ host_port->next = NULL;
209
+ strncpy( host_port->host, host, strlen(host) + 1 );
210
+
211
+ if( *list == NULL )
212
+ *list = host_port;
213
+ else {
214
+ mongo_host_port* p = *list;
215
+ while( p->next != NULL )
216
+ p = p->next;
217
+ p->next = host_port;
218
+ }
219
+
220
+ return 0;
221
+ }
222
+
223
+ static int mongo_replset_free_list( mongo_host_port** list ) {
224
+ mongo_host_port* node = *list;
225
+ mongo_host_port* prev;
226
+
227
+ while( node != NULL ) {
228
+ prev = node;
229
+ node = node->next;
230
+ free(prev);
231
+ }
232
+
233
+ *list = NULL;
234
+ return 0;
235
+ }
236
+
237
+ int mongo_replset_add_seed(mongo_connection* conn, const char* host, int port) {
238
+ return mongo_replset_add_node( &conn->replset->seeds, host, port );
239
+ }
240
+
241
+ static int mongo_replset_check_seed( mongo_connection* conn ) {
242
+ bson out;
243
+ bson hosts;
244
+ const char* data;
245
+ bson_iterator it;
246
+ bson_iterator it_sub;
247
+ const char* host_string;
248
+ char* host;
249
+ int len, idx, port, split;
250
+
251
+ out.data = NULL;
252
+ out.owned = 1;
253
+
254
+ hosts.data = NULL;
255
+ hosts.owned = 1;
256
+
257
+ if (mongo_simple_int_command(conn, "admin", "ismaster", 1, &out)) {
258
+
259
+ if( bson_find( &it, &out, "hosts" ) ) {
260
+ data = bson_iterator_value( &it );
261
+ bson_iterator_init( &it_sub, data );
262
+
263
+ /* Iterate over host list, adding each host to the
264
+ * connection's host list.
265
+ */
266
+ while( bson_iterator_next( &it_sub ) ) {
267
+ host_string = bson_iterator_string( &it_sub );
268
+ len = split = idx = 0;
269
+
270
+ /* Split the host_port string at the ':' */
271
+ while(1) {
272
+ if( *(host_string + len) == 0)
273
+ break;
274
+ if( *(host_string + len) == ':' )
275
+ split = len;
276
+ len++;
277
+ }
278
+
279
+ /* If 'split' is set, we know the that port exists;
280
+ * Otherwise, we set the default port.
281
+ */
282
+ if( len > 0 ) {
283
+ idx = split ? split : len;
284
+ host = (char *)bson_malloc( idx + 1 );
285
+ memcpy( host, host_string, idx );
286
+ memcpy( host + idx, "\0", 1 );
287
+ if( split )
288
+ port = atoi( host_string + idx + 1 );
289
+ else
290
+ port = 27017;
291
+
292
+ mongo_replset_add_node( &conn->replset->hosts, host, port );
293
+ }
294
+ }
295
+ }
296
+ }
297
+
298
+ bson_destroy( &out );
299
+ bson_destroy( &hosts );
300
+ mongo_close_socket( conn->sock );
301
+ conn->sock = 0;
302
+ conn->connected = 0;
303
+
304
+ return 0;
305
+ }
306
+
307
+ /* Find out whether the current connected node is master, and
308
+ * verify that the node's replica set name matched the provided name
309
+ */
310
+ static int mongo_replset_check_host( mongo_connection* conn ) {
311
+
312
+ bson out;
313
+ bson_iterator it;
314
+ bson_bool_t ismaster = 0;
315
+ const char* set_name;
316
+
317
+ out.data = NULL;
318
+ out.owned = 1;
319
+
320
+ if (mongo_simple_int_command(conn, "admin", "ismaster", 1, &out)) {
321
+ if( bson_find(&it, &out, "ismaster") )
322
+ ismaster = bson_iterator_bool( &it );
323
+
324
+ if( bson_find( &it, &out, "setName" ) ) {
325
+ set_name = bson_iterator_string( &it );
326
+ if( strcmp( set_name, conn->replset->name ) != 0 ) {
327
+ return mongo_conn_bad_set_name;
328
+ }
329
+ }
330
+ }
331
+ bson_destroy( &out );
332
+
333
+ if(ismaster) {
334
+ conn->replset->primary_connected = 1;
335
+ }
336
+ else {
337
+ mongo_close_socket( conn->sock );
338
+ }
339
+
340
+ return 0;
341
+ }
342
+
343
+ mongo_conn_return mongo_replset_connect(mongo_connection* conn) {
344
+
345
+ int connect_error = 0;
346
+ mongo_host_port* node;
347
+
348
+ conn->sock = 0;
349
+ conn->connected = 0;
350
+
351
+ /* First iterate over the seed nodes to get the canonical list of hosts
352
+ * from the replica set. Break out once we have a host list.
353
+ */
354
+ node = conn->replset->seeds;
355
+ while( node != NULL ) {
356
+ connect_error = mongo_socket_connect( conn, (const char*)&node->host, node->port );
357
+
358
+ if( connect_error == 0 ) {
359
+ if ( (connect_error = mongo_replset_check_seed( conn )) )
360
+ return connect_error;
361
+ }
362
+
363
+ if( conn->replset->hosts )
364
+ break;
365
+
366
+ node = node->next;
367
+ }
368
+
369
+ /* Iterate over the host list, checking for the primary node. */
370
+ if( !conn->replset->hosts )
371
+ return mongo_conn_cannot_find_primary;
372
+ else {
373
+ node = conn->replset->hosts;
374
+
375
+ while( node != NULL ) {
376
+ connect_error = mongo_socket_connect( conn, (const char*)&node->host, node->port );
377
+
378
+ if( connect_error == 0 ) {
379
+ if ( (connect_error = mongo_replset_check_host( conn )) )
380
+ return connect_error;
381
+
382
+ /* Primary found, so return. */
383
+ else if( conn->replset->primary_connected )
384
+ return 0;
385
+
386
+ /* No primary, so close the connection. */
387
+ else {
388
+ mongo_close_socket( conn->sock );
389
+ conn->sock = 0;
390
+ conn->connected = 0;
391
+ }
392
+ }
393
+
394
+ node = node->next;
395
+ }
396
+ }
397
+
398
+ return mongo_conn_cannot_find_primary;
399
+ }
400
+
401
+ mongo_conn_return mongo_reconnect( mongo_connection * conn ){
402
+ int res;
403
+ mongo_disconnect(conn);
404
+
405
+ if( conn->replset ) {
406
+ conn->replset->primary_connected = 0;
407
+ mongo_replset_free_list( &conn->replset->hosts );
408
+ conn->replset->hosts = NULL;
409
+ res = mongo_replset_connect( conn );
410
+ return res;
411
+ }
412
+ else
413
+ return mongo_socket_connect( conn, conn->primary->host, conn->primary->port );
414
+ }
415
+
416
+ bson_bool_t mongo_disconnect( mongo_connection * conn ){
417
+ if( ! conn->connected )
418
+ return 1;
419
+
420
+ if( conn->replset ) {
421
+ conn->replset->primary_connected = 0;
422
+ mongo_replset_free_list( &conn->replset->hosts );
423
+ conn->replset->hosts = NULL;
424
+ return mongo_replset_connect( conn );
425
+ }
426
+
427
+ mongo_close_socket( conn->sock );
428
+
429
+ conn->sock = 0;
430
+ conn->connected = 0;
431
+
432
+ return 0;
433
+ }
434
+
435
+ bson_bool_t mongo_destroy( mongo_connection * conn ){
436
+ if( conn->replset ) {
437
+ mongo_replset_free_list( &conn->replset->seeds );
438
+ mongo_replset_free_list( &conn->replset->hosts );
439
+ free( conn->replset->name );
440
+ free( conn->replset );
441
+ conn->replset = NULL;
442
+ }
443
+
444
+ free( conn->primary );
445
+ return mongo_disconnect( conn );
446
+ }
447
+
448
+ void mongo_insert_batch( mongo_connection * conn , const char * ns , bson ** bsons, int count){
449
+ int size = 16 + 4 + strlen( ns ) + 1;
450
+ int i;
451
+ mongo_message * mm;
452
+ char* data;
453
+
454
+ for(i=0; i<count; i++){
455
+ size += bson_size(bsons[i]);
456
+ }
457
+
458
+ mm = mongo_message_create( size , 0 , 0 , mongo_op_insert );
459
+
460
+ data = &mm->data;
461
+ data = mongo_data_append32(data, &zero);
462
+ data = mongo_data_append(data, ns, strlen(ns) + 1);
463
+
464
+ for(i=0; i<count; i++){
465
+ data = mongo_data_append(data, bsons[i]->data, bson_size( bsons[i] ) );
466
+ }
467
+
468
+ mongo_message_send(conn, mm);
469
+ }
470
+
471
+ void mongo_insert( mongo_connection * conn , const char * ns , bson * bson ){
472
+ char * data;
473
+ mongo_message * mm = mongo_message_create( 16 /* header */
474
+ + 4 /* ZERO */
475
+ + strlen(ns)
476
+ + 1 + bson_size(bson)
477
+ , 0, 0, mongo_op_insert);
478
+
479
+ data = &mm->data;
480
+ data = mongo_data_append32(data, &zero);
481
+ data = mongo_data_append(data, ns, strlen(ns) + 1);
482
+ data = mongo_data_append(data, bson->data, bson_size(bson));
483
+
484
+ mongo_message_send(conn, mm);
485
+ }
486
+
487
+ void mongo_update(mongo_connection* conn, const char* ns, const bson* cond, const bson* op, int flags){
488
+ char * data;
489
+ mongo_message * mm = mongo_message_create( 16 /* header */
490
+ + 4 /* ZERO */
491
+ + strlen(ns) + 1
492
+ + 4 /* flags */
493
+ + bson_size(cond)
494
+ + bson_size(op)
495
+ , 0 , 0 , mongo_op_update );
496
+
497
+ data = &mm->data;
498
+ data = mongo_data_append32(data, &zero);
499
+ data = mongo_data_append(data, ns, strlen(ns) + 1);
500
+ data = mongo_data_append32(data, &flags);
501
+ data = mongo_data_append(data, cond->data, bson_size(cond));
502
+ data = mongo_data_append(data, op->data, bson_size(op));
503
+
504
+ mongo_message_send(conn, mm);
505
+ }
506
+
507
+ void mongo_remove(mongo_connection* conn, const char* ns, const bson* cond){
508
+ char * data;
509
+ mongo_message * mm = mongo_message_create( 16 /* header */
510
+ + 4 /* ZERO */
511
+ + strlen(ns) + 1
512
+ + 4 /* ZERO */
513
+ + bson_size(cond)
514
+ , 0 , 0 , mongo_op_delete );
515
+
516
+ data = &mm->data;
517
+ data = mongo_data_append32(data, &zero);
518
+ data = mongo_data_append(data, ns, strlen(ns) + 1);
519
+ data = mongo_data_append32(data, &zero);
520
+ data = mongo_data_append(data, cond->data, bson_size(cond));
521
+
522
+ mongo_message_send(conn, mm);
523
+ }
524
+
525
+ mongo_reply * mongo_read_response( mongo_connection * conn ){
526
+ mongo_header head; /* header from network */
527
+ mongo_reply_fields fields; /* header from network */
528
+ mongo_reply * out; /* native endian */
529
+ unsigned int len;
530
+
531
+ looping_read(conn, &head, sizeof(head));
532
+ looping_read(conn, &fields, sizeof(fields));
533
+
534
+ bson_little_endian32(&len, &head.len);
535
+
536
+ if (len < sizeof(head)+sizeof(fields) || len > 64*1024*1024)
537
+ MONGO_THROW(MONGO_EXCEPT_NETWORK); /* most likely corruption */
538
+
539
+ out = (mongo_reply*)bson_malloc(len);
540
+
541
+ out->head.len = len;
542
+ bson_little_endian32(&out->head.id, &head.id);
543
+ bson_little_endian32(&out->head.responseTo, &head.responseTo);
544
+ bson_little_endian32(&out->head.op, &head.op);
545
+
546
+ bson_little_endian32(&out->fields.flag, &fields.flag);
547
+ bson_little_endian64(&out->fields.cursorID, &fields.cursorID);
548
+ bson_little_endian32(&out->fields.start, &fields.start);
549
+ bson_little_endian32(&out->fields.num, &fields.num);
550
+
551
+ MONGO_TRY{
552
+ looping_read(conn, &out->objs, len-sizeof(head)-sizeof(fields));
553
+ }MONGO_CATCH{
554
+ free(out);
555
+ MONGO_RETHROW();
556
+ }
557
+
558
+ return out;
559
+ }
560
+
561
+ mongo_cursor* mongo_find(mongo_connection* conn, const char* ns, bson* query, bson* fields, int nToReturn, int nToSkip, int options){
562
+ int sl;
563
+ volatile mongo_cursor * cursor; /* volatile due to longjmp in mongo exception handler */
564
+ char * data;
565
+ mongo_message * mm = mongo_message_create( 16 + /* header */
566
+ 4 + /* options */
567
+ strlen( ns ) + 1 + /* ns */
568
+ 4 + 4 + /* skip,return */
569
+ bson_size( query ) +
570
+ bson_size( fields ) ,
571
+ 0 , 0 , mongo_op_query );
572
+
573
+
574
+ data = &mm->data;
575
+ data = mongo_data_append32( data , &options );
576
+ data = mongo_data_append( data , ns , strlen( ns ) + 1 );
577
+ data = mongo_data_append32( data , &nToSkip );
578
+ data = mongo_data_append32( data , &nToReturn );
579
+ data = mongo_data_append( data , query->data , bson_size( query ) );
580
+ if ( fields )
581
+ data = mongo_data_append( data , fields->data , bson_size( fields ) );
582
+
583
+ bson_fatal_msg( (data == ((char*)mm) + mm->head.len), "query building fail!" );
584
+
585
+ mongo_message_send( conn , mm );
586
+
587
+ cursor = (mongo_cursor*)bson_malloc(sizeof(mongo_cursor));
588
+
589
+ MONGO_TRY{
590
+ cursor->mm = mongo_read_response(conn);
591
+ }MONGO_CATCH{
592
+ free((mongo_cursor*)cursor); /* cast away volatile, not changing type */
593
+ MONGO_RETHROW();
594
+ }
595
+
596
+ sl = strlen(ns)+1;
597
+ cursor->ns = bson_malloc(sl);
598
+ if (!cursor->ns){
599
+ free(cursor->mm);
600
+ free((mongo_cursor*)cursor); /* cast away volatile, not changing type */
601
+ return 0;
602
+ }
603
+ memcpy((void*)cursor->ns, ns, sl); /* cast needed to silence GCC warning */
604
+ cursor->conn = conn;
605
+ cursor->current.data = NULL;
606
+ return (mongo_cursor*)cursor;
607
+ }
608
+
609
+ bson_bool_t mongo_find_one(mongo_connection* conn, const char* ns, bson* query, bson* fields, bson* out){
610
+ mongo_cursor* cursor = mongo_find(conn, ns, query, fields, 1, 0, 0);
611
+
612
+ if (cursor && mongo_cursor_next(cursor)){
613
+ bson_copy(out, &cursor->current);
614
+ mongo_cursor_destroy(cursor);
615
+ return 1;
616
+ }else{
617
+ mongo_cursor_destroy(cursor);
618
+ return 0;
619
+ }
620
+ }
621
+
622
+ int64_t mongo_count(mongo_connection* conn, const char* db, const char* ns, bson* query){
623
+ bson_buffer bb;
624
+ bson cmd;
625
+ bson out;
626
+ int64_t count = -1;
627
+
628
+ bson_buffer_init(&bb);
629
+ bson_append_string(&bb, "count", ns);
630
+ if (query && bson_size(query) > 5) /* not empty */
631
+ bson_append_bson(&bb, "query", query);
632
+ bson_from_buffer(&cmd, &bb);
633
+
634
+ MONGO_TRY{
635
+ if(mongo_run_command(conn, db, &cmd, &out)){
636
+ bson_iterator it;
637
+ if(bson_find(&it, &out, "n"))
638
+ count = bson_iterator_long(&it);
639
+ }
640
+ }MONGO_CATCH{
641
+ bson_destroy(&cmd);
642
+ MONGO_RETHROW();
643
+ }
644
+
645
+ bson_destroy(&cmd);
646
+ bson_destroy(&out);
647
+ return count;
648
+ }
649
+
650
+
651
+
652
+ bson_bool_t mongo_cursor_get_more(mongo_cursor* cursor){
653
+ if (cursor->mm && cursor->mm->fields.cursorID){
654
+ mongo_connection* conn = cursor->conn;
655
+ char* data;
656
+ int sl = strlen(cursor->ns)+1;
657
+ mongo_message * mm = mongo_message_create(16 /*header*/
658
+ +4 /*ZERO*/
659
+ +sl
660
+ +4 /*numToReturn*/
661
+ +8 /*cursorID*/
662
+ , 0, 0, mongo_op_get_more);
663
+ data = &mm->data;
664
+ data = mongo_data_append32(data, &zero);
665
+ data = mongo_data_append(data, cursor->ns, sl);
666
+ data = mongo_data_append32(data, &zero);
667
+ data = mongo_data_append64(data, &cursor->mm->fields.cursorID);
668
+ mongo_message_send(conn, mm);
669
+
670
+ free(cursor->mm);
671
+
672
+ MONGO_TRY{
673
+ cursor->mm = mongo_read_response(cursor->conn);
674
+ }MONGO_CATCH{
675
+ cursor->mm = NULL;
676
+ mongo_cursor_destroy(cursor);
677
+ MONGO_RETHROW();
678
+ }
679
+
680
+ return cursor->mm && cursor->mm->fields.num;
681
+ } else{
682
+ return 0;
683
+ }
684
+ }
685
+
686
+ bson_bool_t mongo_cursor_next(mongo_cursor* cursor){
687
+ char* bson_addr;
688
+
689
+ /* no data */
690
+ if (!cursor->mm || cursor->mm->fields.num == 0)
691
+ return 0;
692
+
693
+ /* first */
694
+ if (cursor->current.data == NULL){
695
+ bson_init(&cursor->current, &cursor->mm->objs, 0);
696
+ return 1;
697
+ }
698
+
699
+ bson_addr = cursor->current.data + bson_size(&cursor->current);
700
+ if (bson_addr >= ((char*)cursor->mm + cursor->mm->head.len)){
701
+ if (!mongo_cursor_get_more(cursor))
702
+ return 0;
703
+ bson_init(&cursor->current, &cursor->mm->objs, 0);
704
+ } else {
705
+ bson_init(&cursor->current, bson_addr, 0);
706
+ }
707
+
708
+ return 1;
709
+ }
710
+
711
+ void mongo_cursor_destroy(mongo_cursor* cursor){
712
+ if (!cursor) return;
713
+
714
+ if (cursor->mm && cursor->mm->fields.cursorID){
715
+ mongo_connection* conn = cursor->conn;
716
+ mongo_message * mm = mongo_message_create(16 /*header*/
717
+ +4 /*ZERO*/
718
+ +4 /*numCursors*/
719
+ +8 /*cursorID*/
720
+ , 0, 0, mongo_op_kill_cursors);
721
+ char* data = &mm->data;
722
+ data = mongo_data_append32(data, &zero);
723
+ data = mongo_data_append32(data, &one);
724
+ data = mongo_data_append64(data, &cursor->mm->fields.cursorID);
725
+
726
+ MONGO_TRY{
727
+ mongo_message_send(conn, mm);
728
+ }MONGO_CATCH{
729
+ free(cursor->mm);
730
+ free((void*)cursor->ns);
731
+ free(cursor);
732
+ MONGO_RETHROW();
733
+ }
734
+ }
735
+
736
+ free(cursor->mm);
737
+ free((void*)cursor->ns);
738
+ free(cursor);
739
+ }
740
+
741
+ bson_bool_t mongo_create_index(mongo_connection * conn, const char * ns, bson * key, int options, bson * out){
742
+ bson_buffer bb;
743
+ bson b;
744
+ bson_iterator it;
745
+ char name[255] = {'_'};
746
+ int i = 1;
747
+ char idxns[1024];
748
+
749
+ bson_iterator_init(&it, key->data);
750
+ while(i < 255 && bson_iterator_next(&it)){
751
+ strncpy(name + i, bson_iterator_key(&it), 255 - i);
752
+ i += strlen(bson_iterator_key(&it));
753
+ }
754
+ name[254] = '\0';
755
+
756
+ bson_buffer_init(&bb);
757
+ bson_append_bson(&bb, "key", key);
758
+ bson_append_string(&bb, "ns", ns);
759
+ bson_append_string(&bb, "name", name);
760
+ if (options & MONGO_INDEX_UNIQUE)
761
+ bson_append_bool(&bb, "unique", 1);
762
+ if (options & MONGO_INDEX_DROP_DUPS)
763
+ bson_append_bool(&bb, "dropDups", 1);
764
+
765
+ bson_from_buffer(&b, &bb);
766
+
767
+ strncpy(idxns, ns, 1024-16);
768
+ strcpy(strchr(idxns, '.'), ".system.indexes");
769
+ mongo_insert(conn, idxns, &b);
770
+ bson_destroy(&b);
771
+
772
+ *strchr(idxns, '.') = '\0'; /* just db not ns */
773
+ return !mongo_cmd_get_last_error(conn, idxns, out);
774
+ }
775
+ bson_bool_t mongo_create_simple_index(mongo_connection * conn, const char * ns, const char* field, int options, bson * out){
776
+ bson_buffer bb;
777
+ bson b;
778
+ bson_bool_t success;
779
+
780
+ bson_buffer_init(&bb);
781
+ bson_append_int(&bb, field, 1);
782
+ bson_from_buffer(&b, &bb);
783
+
784
+ success = mongo_create_index(conn, ns, &b, options, out);
785
+ bson_destroy(&b);
786
+ return success;
787
+ }
788
+
789
+ bson_bool_t mongo_run_command(mongo_connection * conn, const char * db, bson * command, bson * out){
790
+ bson fields;
791
+ int sl = strlen(db);
792
+ char* ns = bson_malloc(sl + 5 + 1); /* ".$cmd" + nul */
793
+ bson_bool_t success;
794
+
795
+ strcpy(ns, db);
796
+ strcpy(ns+sl, ".$cmd");
797
+
798
+ success = mongo_find_one(conn, ns, command, bson_empty(&fields), out);
799
+ free(ns);
800
+ return success;
801
+ }
802
+
803
+ bson_bool_t mongo_simple_int_command(mongo_connection * conn, const char * db, const char* cmdstr, int arg, bson * realout){
804
+ bson out;
805
+ bson cmd;
806
+ bson_buffer bb;
807
+ bson_bool_t success = 0;
808
+
809
+ bson_buffer_init(&bb);
810
+ bson_append_int(&bb, cmdstr, arg);
811
+ bson_from_buffer(&cmd, &bb);
812
+
813
+ if(mongo_run_command(conn, db, &cmd, &out)){
814
+ bson_iterator it;
815
+ if(bson_find(&it, &out, "ok"))
816
+ success = bson_iterator_bool(&it);
817
+ }
818
+
819
+ bson_destroy(&cmd);
820
+
821
+ if (realout)
822
+ *realout = out;
823
+ else
824
+ bson_destroy(&out);
825
+
826
+ return success;
827
+ }
828
+
829
+ bson_bool_t mongo_simple_str_command(mongo_connection * conn, const char * db, const char* cmdstr, const char* arg, bson * realout){
830
+ bson out;
831
+ bson cmd;
832
+ bson_buffer bb;
833
+ bson_bool_t success = 0;
834
+
835
+ bson_buffer_init(&bb);
836
+ bson_append_string(&bb, cmdstr, arg);
837
+ bson_from_buffer(&cmd, &bb);
838
+
839
+ if(mongo_run_command(conn, db, &cmd, &out)){
840
+ bson_iterator it;
841
+ if(bson_find(&it, &out, "ok"))
842
+ success = bson_iterator_bool(&it);
843
+ }
844
+
845
+ bson_destroy(&cmd);
846
+
847
+ if (realout)
848
+ *realout = out;
849
+ else
850
+ bson_destroy(&out);
851
+
852
+ return success;
853
+ }
854
+
855
+ bson_bool_t mongo_cmd_drop_db(mongo_connection * conn, const char * db){
856
+ return mongo_simple_int_command(conn, db, "dropDatabase", 1, NULL);
857
+ }
858
+
859
+ bson_bool_t mongo_cmd_drop_collection(mongo_connection * conn, const char * db, const char * collection, bson * out){
860
+ return mongo_simple_str_command(conn, db, "drop", collection, out);
861
+ }
862
+
863
+ void mongo_cmd_reset_error(mongo_connection * conn, const char * db){
864
+ mongo_simple_int_command(conn, db, "reseterror", 1, NULL);
865
+ }
866
+
867
+ static bson_bool_t mongo_cmd_get_error_helper(mongo_connection * conn, const char * db, bson * realout, const char * cmdtype){
868
+ bson out = {NULL,0};
869
+ bson_bool_t haserror = 1;
870
+
871
+
872
+ if(mongo_simple_int_command(conn, db, cmdtype, 1, &out)){
873
+ bson_iterator it;
874
+ haserror = (bson_find(&it, &out, "err") != bson_null);
875
+ }
876
+
877
+ if(realout)
878
+ *realout = out; /* transfer of ownership */
879
+ else
880
+ bson_destroy(&out);
881
+
882
+ return haserror;
883
+ }
884
+
885
+ bson_bool_t mongo_cmd_get_prev_error(mongo_connection * conn, const char * db, bson * out){
886
+ return mongo_cmd_get_error_helper(conn, db, out, "getpreverror");
887
+ }
888
+ bson_bool_t mongo_cmd_get_last_error(mongo_connection * conn, const char * db, bson * out){
889
+ return mongo_cmd_get_error_helper(conn, db, out, "getlasterror");
890
+ }
891
+
892
+ bson_bool_t mongo_cmd_ismaster(mongo_connection * conn, bson * realout){
893
+ bson out = {NULL,0};
894
+ bson_bool_t ismaster = 0;
895
+
896
+ if (mongo_simple_int_command(conn, "admin", "ismaster", 1, &out)){
897
+ bson_iterator it;
898
+ bson_find(&it, &out, "ismaster");
899
+ ismaster = bson_iterator_bool(&it);
900
+ }
901
+
902
+ if(realout)
903
+ *realout = out; /* transfer of ownership */
904
+ else
905
+ bson_destroy(&out);
906
+
907
+ return ismaster;
908
+ }
909
+
910
+ static void digest2hex(mongo_md5_byte_t digest[16], char hex_digest[33]){
911
+ static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
912
+ int i;
913
+ for (i=0; i<16; i++){
914
+ hex_digest[2*i] = hex[(digest[i] & 0xf0) >> 4];
915
+ hex_digest[2*i + 1] = hex[ digest[i] & 0x0f ];
916
+ }
917
+ hex_digest[32] = '\0';
918
+ }
919
+
920
+ static void mongo_pass_digest(const char* user, const char* pass, char hex_digest[33]){
921
+ mongo_md5_state_t st;
922
+ mongo_md5_byte_t digest[16];
923
+
924
+ mongo_md5_init(&st);
925
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)user, strlen(user));
926
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)":mongo:", 7);
927
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)pass, strlen(pass));
928
+ mongo_md5_finish(&st, digest);
929
+ digest2hex(digest, hex_digest);
930
+ }
931
+
932
+ void mongo_cmd_add_user(mongo_connection* conn, const char* db, const char* user, const char* pass){
933
+ bson_buffer bb;
934
+ bson user_obj;
935
+ bson pass_obj;
936
+ char hex_digest[33];
937
+ char* ns = bson_malloc(strlen(db) + strlen(".system.users") + 1);
938
+
939
+ strcpy(ns, db);
940
+ strcpy(ns+strlen(db), ".system.users");
941
+
942
+ mongo_pass_digest(user, pass, hex_digest);
943
+
944
+ bson_buffer_init(&bb);
945
+ bson_append_string(&bb, "user", user);
946
+ bson_from_buffer(&user_obj, &bb);
947
+
948
+ bson_buffer_init(&bb);
949
+ bson_append_start_object(&bb, "$set");
950
+ bson_append_string(&bb, "pwd", hex_digest);
951
+ bson_append_finish_object(&bb);
952
+ bson_from_buffer(&pass_obj, &bb);
953
+
954
+
955
+ MONGO_TRY{
956
+ mongo_update(conn, ns, &user_obj, &pass_obj, MONGO_UPDATE_UPSERT);
957
+ }MONGO_CATCH{
958
+ free(ns);
959
+ bson_destroy(&user_obj);
960
+ bson_destroy(&pass_obj);
961
+ MONGO_RETHROW();
962
+ }
963
+
964
+ free(ns);
965
+ bson_destroy(&user_obj);
966
+ bson_destroy(&pass_obj);
967
+ }
968
+
969
+ bson_bool_t mongo_cmd_authenticate(mongo_connection* conn, const char* db, const char* user, const char* pass){
970
+ bson_buffer bb;
971
+ bson from_db, auth_cmd;
972
+ const char* nonce;
973
+ bson_bool_t success = 0;
974
+
975
+ mongo_md5_state_t st;
976
+ mongo_md5_byte_t digest[16];
977
+ char hex_digest[33];
978
+
979
+ if (mongo_simple_int_command(conn, db, "getnonce", 1, &from_db)){
980
+ bson_iterator it;
981
+ bson_find(&it, &from_db, "nonce");
982
+ nonce = bson_iterator_string(&it);
983
+ }else{
984
+ return 0;
985
+ }
986
+
987
+ mongo_pass_digest(user, pass, hex_digest);
988
+
989
+ mongo_md5_init(&st);
990
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)nonce, strlen(nonce));
991
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)user, strlen(user));
992
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)hex_digest, 32);
993
+ mongo_md5_finish(&st, digest);
994
+ digest2hex(digest, hex_digest);
995
+
996
+ bson_buffer_init(&bb);
997
+ bson_append_int(&bb, "authenticate", 1);
998
+ bson_append_string(&bb, "user", user);
999
+ bson_append_string(&bb, "nonce", nonce);
1000
+ bson_append_string(&bb, "key", hex_digest);
1001
+ bson_from_buffer(&auth_cmd, &bb);
1002
+
1003
+ bson_destroy(&from_db);
1004
+
1005
+ MONGO_TRY{
1006
+ if(mongo_run_command(conn, db, &auth_cmd, &from_db)){
1007
+ bson_iterator it;
1008
+ if(bson_find(&it, &from_db, "ok"))
1009
+ success = bson_iterator_bool(&it);
1010
+ }
1011
+ }MONGO_CATCH{
1012
+ bson_destroy(&auth_cmd);
1013
+ MONGO_RETHROW();
1014
+ }
1015
+
1016
+ bson_destroy(&from_db);
1017
+ bson_destroy(&auth_cmd);
1018
+
1019
+ return success;
1020
+ }