faststep 0.0.1

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.
Files changed (47) hide show
  1. data/.gitignore +6 -0
  2. data/Gemfile +4 -0
  3. data/Rakefile +15 -0
  4. data/bench/standard_benchmark +178 -0
  5. data/ext/faststep/bson.c +687 -0
  6. data/ext/faststep/bson.h +225 -0
  7. data/ext/faststep/bson_ruby_conversion.c +44 -0
  8. data/ext/faststep/bson_ruby_conversion.h +10 -0
  9. data/ext/faststep/collection.c +187 -0
  10. data/ext/faststep/collection.h +24 -0
  11. data/ext/faststep/connection.c +85 -0
  12. data/ext/faststep/connection.h +17 -0
  13. data/ext/faststep/cursor.c +61 -0
  14. data/ext/faststep/cursor.h +10 -0
  15. data/ext/faststep/db.c +56 -0
  16. data/ext/faststep/db.h +8 -0
  17. data/ext/faststep/exceptions.c +7 -0
  18. data/ext/faststep/exceptions.h +5 -0
  19. data/ext/faststep/extconf.rb +3 -0
  20. data/ext/faststep/faststep.c +30 -0
  21. data/ext/faststep/faststep.h +4 -0
  22. data/ext/faststep/faststep_defines.h +11 -0
  23. data/ext/faststep/gridfs.c +799 -0
  24. data/ext/faststep/gridfs.h +278 -0
  25. data/ext/faststep/md5.c +381 -0
  26. data/ext/faststep/md5.h +91 -0
  27. data/ext/faststep/mongo.c +801 -0
  28. data/ext/faststep/mongo.h +188 -0
  29. data/ext/faststep/mongo_except.h +143 -0
  30. data/ext/faststep/numbers.c +127 -0
  31. data/ext/faststep/platform_hacks.h +93 -0
  32. data/ext/faststep/support.c +21 -0
  33. data/ext/faststep/support.h +6 -0
  34. data/faststep.gemspec +26 -0
  35. data/lib/faststep/collection.rb +21 -0
  36. data/lib/faststep/connection.rb +13 -0
  37. data/lib/faststep/cursor.rb +7 -0
  38. data/lib/faststep/db.rb +25 -0
  39. data/lib/faststep/version.rb +3 -0
  40. data/lib/faststep.rb +10 -0
  41. data/spec/collection_spec.rb +116 -0
  42. data/spec/connection_spec.rb +34 -0
  43. data/spec/cursor_spec.rb +24 -0
  44. data/spec/db_spec.rb +28 -0
  45. data/spec/spec_helper.rb +13 -0
  46. data/spec/support_spec.rb +14 -0
  47. metadata +181 -0
@@ -0,0 +1,801 @@
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
+
27
+ #ifndef _WIN32
28
+ #include <unistd.h>
29
+ #endif
30
+
31
+ /* only need one of these */
32
+ static const int zero = 0;
33
+ static const int one = 1;
34
+
35
+ /* ----------------------------
36
+ message stuff
37
+ ------------------------------ */
38
+
39
+ static void looping_write(mongo_connection * conn, const void* buf, int len){
40
+ const char* cbuf = buf;
41
+ while (len){
42
+ int sent = send(conn->sock, cbuf, len, 0);
43
+ if (sent == -1) MONGO_THROW(MONGO_EXCEPT_NETWORK);
44
+ cbuf += sent;
45
+ len -= sent;
46
+ }
47
+ }
48
+
49
+ static void looping_read(mongo_connection * conn, void* buf, int len){
50
+ char* cbuf = buf;
51
+ while (len){
52
+ int sent = recv(conn->sock, cbuf, len, 0);
53
+ if (sent == 0 || sent == -1) MONGO_THROW(MONGO_EXCEPT_NETWORK);
54
+ cbuf += sent;
55
+ len -= sent;
56
+ }
57
+ }
58
+
59
+ /* Always calls free(mm) */
60
+ void mongo_message_send(mongo_connection * conn, mongo_message* mm){
61
+ mongo_header head; /* little endian */
62
+ bson_little_endian32(&head.len, &mm->head.len);
63
+ bson_little_endian32(&head.id, &mm->head.id);
64
+ bson_little_endian32(&head.responseTo, &mm->head.responseTo);
65
+ bson_little_endian32(&head.op, &mm->head.op);
66
+
67
+ MONGO_TRY{
68
+ looping_write(conn, &head, sizeof(head));
69
+ looping_write(conn, &mm->data, mm->head.len - sizeof(head));
70
+ }MONGO_CATCH{
71
+ free(mm);
72
+ MONGO_RETHROW();
73
+ }
74
+ free(mm);
75
+ }
76
+
77
+ char * mongo_data_append( char * start , const void * data , int len ){
78
+ memcpy( start , data , len );
79
+ return start + len;
80
+ }
81
+
82
+ char * mongo_data_append32( char * start , const void * data){
83
+ bson_little_endian32( start , data );
84
+ return start + 4;
85
+ }
86
+
87
+ char * mongo_data_append64( char * start , const void * data){
88
+ bson_little_endian64( start , data );
89
+ return start + 8;
90
+ }
91
+
92
+ mongo_message * mongo_message_create( int len , int id , int responseTo , int op ){
93
+ mongo_message * mm = (mongo_message*)bson_malloc( len );
94
+
95
+ if (!id)
96
+ id = rand();
97
+
98
+ /* native endian (converted on send) */
99
+ mm->head.len = len;
100
+ mm->head.id = id;
101
+ mm->head.responseTo = responseTo;
102
+ mm->head.op = op;
103
+
104
+ return mm;
105
+ }
106
+
107
+ /* ----------------------------
108
+ connection stuff
109
+ ------------------------------ */
110
+ static int mongo_connect_helper( mongo_connection * conn ){
111
+ /* setup */
112
+ conn->sock = 0;
113
+ conn->connected = 0;
114
+
115
+ memset( conn->sa.sin_zero , 0 , sizeof(conn->sa.sin_zero) );
116
+ conn->sa.sin_family = AF_INET;
117
+ conn->sa.sin_port = htons(conn->left_opts->port);
118
+ conn->sa.sin_addr.s_addr = inet_addr( conn->left_opts->host );
119
+ conn->addressSize = sizeof(conn->sa);
120
+
121
+ /* connect */
122
+ conn->sock = socket( AF_INET, SOCK_STREAM, 0 );
123
+ if ( conn->sock <= 0 ){
124
+ mongo_close_socket( conn->sock );
125
+ return mongo_conn_no_socket;
126
+ }
127
+
128
+ if ( connect( conn->sock , (struct sockaddr*)&conn->sa , conn->addressSize ) ){
129
+ mongo_close_socket( conn->sock );
130
+ return mongo_conn_fail;
131
+ }
132
+
133
+ /* nagle */
134
+ setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one) );
135
+
136
+ /* TODO signals */
137
+
138
+ conn->connected = 1;
139
+ return 0;
140
+ }
141
+
142
+ mongo_conn_return mongo_connect( mongo_connection * conn , mongo_connection_options * options ){
143
+ MONGO_INIT_EXCEPTION(&conn->exception);
144
+
145
+ conn->left_opts = bson_malloc(sizeof(mongo_connection_options));
146
+ conn->right_opts = NULL;
147
+
148
+ if ( options ){
149
+ memcpy( conn->left_opts , options , sizeof( mongo_connection_options ) );
150
+ } else {
151
+ strcpy( conn->left_opts->host , "127.0.0.1" );
152
+ conn->left_opts->port = 27017;
153
+ }
154
+
155
+ return mongo_connect_helper(conn);
156
+ }
157
+
158
+ static void swap_repl_pair(mongo_connection * conn){
159
+ mongo_connection_options * tmp = conn->left_opts;
160
+ conn->left_opts = conn->right_opts;
161
+ conn->right_opts = tmp;
162
+ }
163
+
164
+ mongo_conn_return mongo_connect_pair( mongo_connection * conn , mongo_connection_options * left, mongo_connection_options * right ){
165
+ conn->connected = 0;
166
+ MONGO_INIT_EXCEPTION(&conn->exception);
167
+
168
+ conn->left_opts = NULL;
169
+ conn->right_opts = NULL;
170
+
171
+ if ( !left || !right )
172
+ return mongo_conn_bad_arg;
173
+
174
+ conn->left_opts = bson_malloc(sizeof(mongo_connection_options));
175
+ conn->right_opts = bson_malloc(sizeof(mongo_connection_options));
176
+
177
+ memcpy( conn->left_opts, left, sizeof( mongo_connection_options ) );
178
+ memcpy( conn->right_opts, right, sizeof( mongo_connection_options ) );
179
+
180
+ return mongo_reconnect(conn);
181
+ }
182
+
183
+ mongo_conn_return mongo_reconnect( mongo_connection * conn ){
184
+ mongo_conn_return ret;
185
+ mongo_disconnect(conn);
186
+
187
+ /* single server */
188
+ if(conn->right_opts == NULL)
189
+ return mongo_connect_helper(conn);
190
+
191
+ /* repl pair */
192
+ ret = mongo_connect_helper(conn);
193
+ if (ret == mongo_conn_success && mongo_cmd_ismaster(conn, NULL)){
194
+ return mongo_conn_success;
195
+ }
196
+
197
+ swap_repl_pair(conn);
198
+
199
+ ret = mongo_connect_helper(conn);
200
+ if (ret == mongo_conn_success){
201
+ if(mongo_cmd_ismaster(conn, NULL))
202
+ return mongo_conn_success;
203
+ else
204
+ return mongo_conn_not_master;
205
+ }
206
+
207
+ /* failed to connect to both servers */
208
+ return ret;
209
+ }
210
+
211
+ void mongo_insert_batch( mongo_connection * conn , const char * ns , bson ** bsons, int count){
212
+ int size = 16 + 4 + strlen( ns ) + 1;
213
+ int i;
214
+ mongo_message * mm;
215
+ char* data;
216
+
217
+ for(i=0; i<count; i++){
218
+ size += bson_size(bsons[i]);
219
+ }
220
+
221
+ mm = mongo_message_create( size , 0 , 0 , mongo_op_insert );
222
+
223
+ data = &mm->data;
224
+ data = mongo_data_append32(data, &zero);
225
+ data = mongo_data_append(data, ns, strlen(ns) + 1);
226
+
227
+ for(i=0; i<count; i++){
228
+ data = mongo_data_append(data, bsons[i]->data, bson_size( bsons[i] ) );
229
+ }
230
+
231
+ mongo_message_send(conn, mm);
232
+ }
233
+
234
+ void mongo_insert( mongo_connection * conn , const char * ns , bson * bson ){
235
+ char * data;
236
+ mongo_message * mm = mongo_message_create( 16 /* header */
237
+ + 4 /* ZERO */
238
+ + strlen(ns)
239
+ + 1 + bson_size(bson)
240
+ , 0, 0, mongo_op_insert);
241
+
242
+ data = &mm->data;
243
+ data = mongo_data_append32(data, &zero);
244
+ data = mongo_data_append(data, ns, strlen(ns) + 1);
245
+ data = mongo_data_append(data, bson->data, bson_size(bson));
246
+
247
+ mongo_message_send(conn, mm);
248
+ }
249
+
250
+ void mongo_update(mongo_connection* conn, const char* ns, const bson* cond, const bson* op, int flags){
251
+ char * data;
252
+ mongo_message * mm = mongo_message_create( 16 /* header */
253
+ + 4 /* ZERO */
254
+ + strlen(ns) + 1
255
+ + 4 /* flags */
256
+ + bson_size(cond)
257
+ + bson_size(op)
258
+ , 0 , 0 , mongo_op_update );
259
+
260
+ data = &mm->data;
261
+ data = mongo_data_append32(data, &zero);
262
+ data = mongo_data_append(data, ns, strlen(ns) + 1);
263
+ data = mongo_data_append32(data, &flags);
264
+ data = mongo_data_append(data, cond->data, bson_size(cond));
265
+ data = mongo_data_append(data, op->data, bson_size(op));
266
+
267
+ mongo_message_send(conn, mm);
268
+ }
269
+
270
+ void mongo_remove(mongo_connection* conn, const char* ns, const bson* cond){
271
+ char * data;
272
+ mongo_message * mm = mongo_message_create( 16 /* header */
273
+ + 4 /* ZERO */
274
+ + strlen(ns) + 1
275
+ + 4 /* ZERO */
276
+ + bson_size(cond)
277
+ , 0 , 0 , mongo_op_delete );
278
+
279
+ data = &mm->data;
280
+ data = mongo_data_append32(data, &zero);
281
+ data = mongo_data_append(data, ns, strlen(ns) + 1);
282
+ data = mongo_data_append32(data, &zero);
283
+ data = mongo_data_append(data, cond->data, bson_size(cond));
284
+
285
+ mongo_message_send(conn, mm);
286
+ }
287
+
288
+ mongo_reply * mongo_read_response( mongo_connection * conn ){
289
+ mongo_header head; /* header from network */
290
+ mongo_reply_fields fields; /* header from network */
291
+ mongo_reply * out; /* native endian */
292
+ int len;
293
+
294
+ looping_read(conn, &head, sizeof(head));
295
+ looping_read(conn, &fields, sizeof(fields));
296
+
297
+ bson_little_endian32(&len, &head.len);
298
+
299
+ if (len < sizeof(head)+sizeof(fields) || len > 64*1024*1024)
300
+ MONGO_THROW(MONGO_EXCEPT_NETWORK); /* most likely corruption */
301
+
302
+ out = (mongo_reply*)bson_malloc(len);
303
+
304
+ out->head.len = len;
305
+ bson_little_endian32(&out->head.id, &head.id);
306
+ bson_little_endian32(&out->head.responseTo, &head.responseTo);
307
+ bson_little_endian32(&out->head.op, &head.op);
308
+
309
+ bson_little_endian32(&out->fields.flag, &fields.flag);
310
+ bson_little_endian64(&out->fields.cursorID, &fields.cursorID);
311
+ bson_little_endian32(&out->fields.start, &fields.start);
312
+ bson_little_endian32(&out->fields.num, &fields.num);
313
+
314
+ MONGO_TRY{
315
+ looping_read(conn, &out->objs, len-sizeof(head)-sizeof(fields));
316
+ }MONGO_CATCH{
317
+ free(out);
318
+ MONGO_RETHROW();
319
+ }
320
+
321
+ return out;
322
+ }
323
+
324
+ mongo_cursor* mongo_find(mongo_connection* conn, const char* ns, bson* query, bson* fields, int nToReturn, int nToSkip, int options){
325
+ int sl;
326
+ volatile mongo_cursor * cursor; /* volatile due to longjmp in mongo exception handler */
327
+ char * data;
328
+ mongo_message * mm = mongo_message_create( 16 + /* header */
329
+ 4 + /* options */
330
+ strlen( ns ) + 1 + /* ns */
331
+ 4 + 4 + /* skip,return */
332
+ bson_size( query ) +
333
+ bson_size( fields ) ,
334
+ 0 , 0 , mongo_op_query );
335
+
336
+
337
+ data = &mm->data;
338
+ data = mongo_data_append32( data , &options );
339
+ data = mongo_data_append( data , ns , strlen( ns ) + 1 );
340
+ data = mongo_data_append32( data , &nToSkip );
341
+ data = mongo_data_append32( data , &nToReturn );
342
+ data = mongo_data_append( data , query->data , bson_size( query ) );
343
+ if ( fields )
344
+ data = mongo_data_append( data , fields->data , bson_size( fields ) );
345
+
346
+ bson_fatal_msg( (data == ((char*)mm) + mm->head.len), "query building fail!" );
347
+
348
+ mongo_message_send( conn , mm );
349
+
350
+ cursor = (mongo_cursor*)bson_malloc(sizeof(mongo_cursor));
351
+
352
+ MONGO_TRY{
353
+ cursor->mm = mongo_read_response(conn);
354
+ }MONGO_CATCH{
355
+ free((mongo_cursor*)cursor); /* cast away volatile, not changing type */
356
+ MONGO_RETHROW();
357
+ }
358
+
359
+ sl = strlen(ns)+1;
360
+ cursor->ns = bson_malloc(sl);
361
+ if (!cursor->ns){
362
+ free(cursor->mm);
363
+ free((mongo_cursor*)cursor); /* cast away volatile, not changing type */
364
+ return 0;
365
+ }
366
+ memcpy((void*)cursor->ns, ns, sl); /* cast needed to silence GCC warning */
367
+ cursor->conn = conn;
368
+ cursor->current.data = NULL;
369
+ return (mongo_cursor*)cursor;
370
+ }
371
+
372
+ bson_bool_t mongo_find_one(mongo_connection* conn, const char* ns, bson* query, bson* fields, bson* out){
373
+ mongo_cursor* cursor = mongo_find(conn, ns, query, fields, 1, 0, 0);
374
+
375
+ if (cursor && mongo_cursor_next(cursor)){
376
+ bson_copy(out, &cursor->current);
377
+ mongo_cursor_destroy(cursor);
378
+ return 1;
379
+ }else{
380
+ mongo_cursor_destroy(cursor);
381
+ return 0;
382
+ }
383
+ }
384
+
385
+ int64_t mongo_count(mongo_connection* conn, const char* db, const char* ns, bson* query){
386
+ bson_buffer bb;
387
+ bson cmd;
388
+ bson out;
389
+ int64_t count = -1;
390
+
391
+ bson_buffer_init(&bb);
392
+ bson_append_string(&bb, "count", ns);
393
+ if (query && bson_size(query) > 5) /* not empty */
394
+ bson_append_bson(&bb, "query", query);
395
+ bson_from_buffer(&cmd, &bb);
396
+
397
+ MONGO_TRY{
398
+ if(mongo_run_command(conn, db, &cmd, &out)){
399
+ bson_iterator it;
400
+ if(bson_find(&it, &out, "n"))
401
+ count = bson_iterator_long(&it);
402
+ }
403
+ }MONGO_CATCH{
404
+ bson_destroy(&cmd);
405
+ MONGO_RETHROW();
406
+ }
407
+
408
+ bson_destroy(&cmd);
409
+ bson_destroy(&out);
410
+ return count;
411
+ }
412
+
413
+ bson_bool_t mongo_disconnect( mongo_connection * conn ){
414
+ if ( ! conn->connected )
415
+ return 1;
416
+
417
+ mongo_close_socket( conn->sock );
418
+
419
+ conn->sock = 0;
420
+ conn->connected = 0;
421
+
422
+ return 0;
423
+ }
424
+
425
+ bson_bool_t mongo_destroy( mongo_connection * conn ){
426
+ free(conn->left_opts);
427
+ free(conn->right_opts);
428
+ conn->left_opts = NULL;
429
+ conn->right_opts = NULL;
430
+
431
+ return mongo_disconnect( conn );
432
+ }
433
+
434
+ bson_bool_t mongo_cursor_get_more(mongo_cursor* cursor){
435
+ if (cursor->mm && cursor->mm->fields.cursorID){
436
+ mongo_connection* conn = cursor->conn;
437
+ char* data;
438
+ int sl = strlen(cursor->ns)+1;
439
+ mongo_message * mm = mongo_message_create(16 /*header*/
440
+ +4 /*ZERO*/
441
+ +sl
442
+ +4 /*numToReturn*/
443
+ +8 /*cursorID*/
444
+ , 0, 0, mongo_op_get_more);
445
+ data = &mm->data;
446
+ data = mongo_data_append32(data, &zero);
447
+ data = mongo_data_append(data, cursor->ns, sl);
448
+ data = mongo_data_append32(data, &zero);
449
+ data = mongo_data_append64(data, &cursor->mm->fields.cursorID);
450
+ mongo_message_send(conn, mm);
451
+
452
+ free(cursor->mm);
453
+
454
+ MONGO_TRY{
455
+ cursor->mm = mongo_read_response(cursor->conn);
456
+ }MONGO_CATCH{
457
+ cursor->mm = NULL;
458
+ mongo_cursor_destroy(cursor);
459
+ MONGO_RETHROW();
460
+ }
461
+
462
+ return cursor->mm && cursor->mm->fields.num;
463
+ } else{
464
+ return 0;
465
+ }
466
+ }
467
+
468
+ bson_bool_t mongo_cursor_next(mongo_cursor* cursor){
469
+ char* bson_addr;
470
+
471
+ /* no data */
472
+ if (!cursor->mm || cursor->mm->fields.num == 0)
473
+ return 0;
474
+
475
+ /* first */
476
+ if (cursor->current.data == NULL){
477
+ bson_init(&cursor->current, &cursor->mm->objs, 0);
478
+ return 1;
479
+ }
480
+
481
+ bson_addr = cursor->current.data + bson_size(&cursor->current);
482
+ if (bson_addr >= ((char*)cursor->mm + cursor->mm->head.len)){
483
+ if (!mongo_cursor_get_more(cursor))
484
+ return 0;
485
+ bson_init(&cursor->current, &cursor->mm->objs, 0);
486
+ } else {
487
+ bson_init(&cursor->current, bson_addr, 0);
488
+ }
489
+
490
+ return 1;
491
+ }
492
+
493
+ void mongo_cursor_destroy(mongo_cursor* cursor){
494
+ if (!cursor) return;
495
+
496
+ if (cursor->mm && cursor->mm->fields.cursorID){
497
+ mongo_connection* conn = cursor->conn;
498
+ mongo_message * mm = mongo_message_create(16 /*header*/
499
+ +4 /*ZERO*/
500
+ +4 /*numCursors*/
501
+ +8 /*cursorID*/
502
+ , 0, 0, mongo_op_kill_cursors);
503
+ char* data = &mm->data;
504
+ data = mongo_data_append32(data, &zero);
505
+ data = mongo_data_append32(data, &one);
506
+ data = mongo_data_append64(data, &cursor->mm->fields.cursorID);
507
+
508
+ MONGO_TRY{
509
+ mongo_message_send(conn, mm);
510
+ }MONGO_CATCH{
511
+ free(cursor->mm);
512
+ free((void*)cursor->ns);
513
+ free(cursor);
514
+ MONGO_RETHROW();
515
+ }
516
+ }
517
+
518
+ free(cursor->mm);
519
+ free((void*)cursor->ns);
520
+ free(cursor);
521
+ }
522
+
523
+ bson_bool_t mongo_create_index(mongo_connection * conn, const char * ns, bson * key, int options, bson * out){
524
+ bson_buffer bb;
525
+ bson b;
526
+ bson_iterator it;
527
+ char name[255] = {'_'};
528
+ int i = 1;
529
+ char idxns[1024];
530
+
531
+ bson_iterator_init(&it, key->data);
532
+ while(i < 255 && bson_iterator_next(&it)){
533
+ strncpy(name + i, bson_iterator_key(&it), 255 - i);
534
+ i += strlen(bson_iterator_key(&it));
535
+ }
536
+ name[254] = '\0';
537
+
538
+ bson_buffer_init(&bb);
539
+ bson_append_bson(&bb, "key", key);
540
+ bson_append_string(&bb, "ns", ns);
541
+ bson_append_string(&bb, "name", name);
542
+ if (options & MONGO_INDEX_UNIQUE)
543
+ bson_append_bool(&bb, "unique", 1);
544
+ if (options & MONGO_INDEX_DROP_DUPS)
545
+ bson_append_bool(&bb, "dropDups", 1);
546
+
547
+ bson_from_buffer(&b, &bb);
548
+
549
+ strncpy(idxns, ns, 1024-16);
550
+ strcpy(strchr(idxns, '.'), ".system.indexes");
551
+ mongo_insert(conn, idxns, &b);
552
+ bson_destroy(&b);
553
+
554
+ *strchr(idxns, '.') = '\0'; /* just db not ns */
555
+ return !mongo_cmd_get_last_error(conn, idxns, out);
556
+ }
557
+ bson_bool_t mongo_create_simple_index(mongo_connection * conn, const char * ns, const char* field, int options, bson * out){
558
+ bson_buffer bb;
559
+ bson b;
560
+ bson_bool_t success;
561
+
562
+ bson_buffer_init(&bb);
563
+ bson_append_int(&bb, field, 1);
564
+ bson_from_buffer(&b, &bb);
565
+
566
+ success = mongo_create_index(conn, ns, &b, options, out);
567
+ bson_destroy(&b);
568
+ return success;
569
+ }
570
+
571
+ bson_bool_t mongo_run_command(mongo_connection * conn, const char * db, bson * command, bson * out){
572
+ bson fields;
573
+ int sl = strlen(db);
574
+ char* ns = bson_malloc(sl + 5 + 1); /* ".$cmd" + nul */
575
+ bson_bool_t success;
576
+
577
+ strcpy(ns, db);
578
+ strcpy(ns+sl, ".$cmd");
579
+
580
+ success = mongo_find_one(conn, ns, command, bson_empty(&fields), out);
581
+ free(ns);
582
+ return success;
583
+ }
584
+ bson_bool_t mongo_simple_int_command(mongo_connection * conn, const char * db, const char* cmdstr, int arg, bson * realout){
585
+ bson out;
586
+ bson cmd;
587
+ bson_buffer bb;
588
+ bson_bool_t success = 0;
589
+
590
+ bson_buffer_init(&bb);
591
+ bson_append_int(&bb, cmdstr, arg);
592
+ bson_from_buffer(&cmd, &bb);
593
+
594
+ if(mongo_run_command(conn, db, &cmd, &out)){
595
+ bson_iterator it;
596
+ if(bson_find(&it, &out, "ok"))
597
+ success = bson_iterator_bool(&it);
598
+ }
599
+
600
+ bson_destroy(&cmd);
601
+
602
+ if (realout)
603
+ *realout = out;
604
+ else
605
+ bson_destroy(&out);
606
+
607
+ return success;
608
+ }
609
+
610
+ bson_bool_t mongo_simple_str_command(mongo_connection * conn, const char * db, const char* cmdstr, const char* arg, bson * realout){
611
+ bson out;
612
+ bson cmd;
613
+ bson_buffer bb;
614
+ bson_bool_t success = 0;
615
+
616
+ bson_buffer_init(&bb);
617
+ bson_append_string(&bb, cmdstr, arg);
618
+ bson_from_buffer(&cmd, &bb);
619
+
620
+ if(mongo_run_command(conn, db, &cmd, &out)){
621
+ bson_iterator it;
622
+ if(bson_find(&it, &out, "ok"))
623
+ success = bson_iterator_bool(&it);
624
+ }
625
+
626
+ bson_destroy(&cmd);
627
+
628
+ if (realout)
629
+ *realout = out;
630
+ else
631
+ bson_destroy(&out);
632
+
633
+ return success;
634
+ }
635
+
636
+ bson_bool_t mongo_cmd_drop_db(mongo_connection * conn, const char * db){
637
+ return mongo_simple_int_command(conn, db, "dropDatabase", 1, NULL);
638
+ }
639
+
640
+ bson_bool_t mongo_cmd_drop_collection(mongo_connection * conn, const char * db, const char * collection, bson * out){
641
+ return mongo_simple_str_command(conn, db, "drop", collection, out);
642
+ }
643
+
644
+ void mongo_cmd_reset_error(mongo_connection * conn, const char * db){
645
+ mongo_simple_int_command(conn, db, "reseterror", 1, NULL);
646
+ }
647
+
648
+ static bson_bool_t mongo_cmd_get_error_helper(mongo_connection * conn, const char * db, bson * realout, const char * cmdtype){
649
+ bson out = {NULL,0};
650
+ bson_bool_t haserror = 1;
651
+
652
+
653
+ if(mongo_simple_int_command(conn, db, cmdtype, 1, &out)){
654
+ bson_iterator it;
655
+ haserror = (bson_find(&it, &out, "err") != bson_null);
656
+ }
657
+
658
+ if(realout)
659
+ *realout = out; /* transfer of ownership */
660
+ else
661
+ bson_destroy(&out);
662
+
663
+ return haserror;
664
+ }
665
+
666
+ bson_bool_t mongo_cmd_get_prev_error(mongo_connection * conn, const char * db, bson * out){
667
+ return mongo_cmd_get_error_helper(conn, db, out, "getpreverror");
668
+ }
669
+ bson_bool_t mongo_cmd_get_last_error(mongo_connection * conn, const char * db, bson * out){
670
+ return mongo_cmd_get_error_helper(conn, db, out, "getlasterror");
671
+ }
672
+
673
+ bson_bool_t mongo_cmd_ismaster(mongo_connection * conn, bson * realout){
674
+ bson out = {NULL,0};
675
+ bson_bool_t ismaster = 0;
676
+
677
+ if (mongo_simple_int_command(conn, "admin", "ismaster", 1, &out)){
678
+ bson_iterator it;
679
+ bson_find(&it, &out, "ismaster");
680
+ ismaster = bson_iterator_bool(&it);
681
+ }
682
+
683
+ if(realout)
684
+ *realout = out; /* transfer of ownership */
685
+ else
686
+ bson_destroy(&out);
687
+
688
+ return ismaster;
689
+ }
690
+
691
+ static void digest2hex(mongo_md5_byte_t digest[16], char hex_digest[33]){
692
+ static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
693
+ int i;
694
+ for (i=0; i<16; i++){
695
+ hex_digest[2*i] = hex[(digest[i] & 0xf0) >> 4];
696
+ hex_digest[2*i + 1] = hex[ digest[i] & 0x0f ];
697
+ }
698
+ hex_digest[32] = '\0';
699
+ }
700
+
701
+ static void mongo_pass_digest(const char* user, const char* pass, char hex_digest[33]){
702
+ mongo_md5_state_t st;
703
+ mongo_md5_byte_t digest[16];
704
+
705
+ mongo_md5_init(&st);
706
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)user, strlen(user));
707
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)":mongo:", 7);
708
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)pass, strlen(pass));
709
+ mongo_md5_finish(&st, digest);
710
+ digest2hex(digest, hex_digest);
711
+ }
712
+
713
+ void mongo_cmd_add_user(mongo_connection* conn, const char* db, const char* user, const char* pass){
714
+ bson_buffer bb;
715
+ bson user_obj;
716
+ bson pass_obj;
717
+ char hex_digest[33];
718
+ char* ns = bson_malloc(strlen(db) + strlen(".system.users") + 1);
719
+
720
+ strcpy(ns, db);
721
+ strcpy(ns+strlen(db), ".system.users");
722
+
723
+ mongo_pass_digest(user, pass, hex_digest);
724
+
725
+ bson_buffer_init(&bb);
726
+ bson_append_string(&bb, "user", user);
727
+ bson_from_buffer(&user_obj, &bb);
728
+
729
+ bson_buffer_init(&bb);
730
+ bson_append_start_object(&bb, "$set");
731
+ bson_append_string(&bb, "pwd", hex_digest);
732
+ bson_append_finish_object(&bb);
733
+ bson_from_buffer(&pass_obj, &bb);
734
+
735
+
736
+ MONGO_TRY{
737
+ mongo_update(conn, ns, &user_obj, &pass_obj, MONGO_UPDATE_UPSERT);
738
+ }MONGO_CATCH{
739
+ free(ns);
740
+ bson_destroy(&user_obj);
741
+ bson_destroy(&pass_obj);
742
+ MONGO_RETHROW();
743
+ }
744
+
745
+ free(ns);
746
+ bson_destroy(&user_obj);
747
+ bson_destroy(&pass_obj);
748
+ }
749
+
750
+ bson_bool_t mongo_cmd_authenticate(mongo_connection* conn, const char* db, const char* user, const char* pass){
751
+ bson_buffer bb;
752
+ bson from_db, auth_cmd;
753
+ const char* nonce;
754
+ bson_bool_t success = 0;
755
+
756
+ mongo_md5_state_t st;
757
+ mongo_md5_byte_t digest[16];
758
+ char hex_digest[33];
759
+
760
+ if (mongo_simple_int_command(conn, db, "getnonce", 1, &from_db)){
761
+ bson_iterator it;
762
+ bson_find(&it, &from_db, "nonce");
763
+ nonce = bson_iterator_string(&it);
764
+ }else{
765
+ return 0;
766
+ }
767
+
768
+ mongo_pass_digest(user, pass, hex_digest);
769
+
770
+ mongo_md5_init(&st);
771
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)nonce, strlen(nonce));
772
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)user, strlen(user));
773
+ mongo_md5_append(&st, (const mongo_md5_byte_t*)hex_digest, 32);
774
+ mongo_md5_finish(&st, digest);
775
+ digest2hex(digest, hex_digest);
776
+
777
+ bson_buffer_init(&bb);
778
+ bson_append_int(&bb, "authenticate", 1);
779
+ bson_append_string(&bb, "user", user);
780
+ bson_append_string(&bb, "nonce", nonce);
781
+ bson_append_string(&bb, "key", hex_digest);
782
+ bson_from_buffer(&auth_cmd, &bb);
783
+
784
+ bson_destroy(&from_db);
785
+
786
+ MONGO_TRY{
787
+ if(mongo_run_command(conn, db, &auth_cmd, &from_db)){
788
+ bson_iterator it;
789
+ if(bson_find(&it, &from_db, "ok"))
790
+ success = bson_iterator_bool(&it);
791
+ }
792
+ }MONGO_CATCH{
793
+ bson_destroy(&auth_cmd);
794
+ MONGO_RETHROW();
795
+ }
796
+
797
+ bson_destroy(&from_db);
798
+ bson_destroy(&auth_cmd);
799
+
800
+ return success;
801
+ }