bson_ext 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }