Fossil SCM

Implemented /json/wiki/list (first draft, may change). Pulled in latest cson_sqlite3 additions to simplify the impl.

stephan 2011-09-19 18:48 UTC json
Commit 5cc88946a740e2697b049c6b739c0d3aeed9ee2e
--- src/cson_amalgamation.c
+++ src/cson_amalgamation.c
@@ -5133,384 +5133,10 @@
51335133
}
51345134
}
51355135
cson_kvp_list_reserve(self,0);
51365136
}
51375137
/* end file ./cson_lists.h */
5138
-/* begin file ./cson_session.c */
5139
-#include <string.h> /* strlen() */
5140
-#include <stdlib.h> /* memcpy() */
5141
-#include <assert.h>
5142
-
5143
-
5144
-/**
5145
- Maximum name length for cson_sessmgr_register(),
5146
- including the trailing NUL.
5147
-*/
5148
-enum {
5149
- CsonSessionNameLen = 32
5150
-};
5151
-
5152
-typedef struct cson_sessmgr_reg cson_sessmgr_reg;
5153
-/**
5154
- Holds name-to-factory mappings for cson_sessmgr implementations.
5155
-*/
5156
-struct cson_sessmgr_reg
5157
-{
5158
- char name[CsonSessionNameLen];
5159
- cson_sessmgr_factory_f factory;
5160
-};
5161
-
5162
-
5163
-#if !CSON_ENABLE_CPDO
5164
-int cson_sessmgr_cpdo( cson_sessmgr ** tgt, cson_object const * opt )
5165
-{
5166
- return cson_rc.UnsupportedError;
5167
-}
5168
-#endif
5169
-#if !CSON_ENABLE_WHIO
5170
-int cson_sessmgr_whio_ht( cson_sessmgr ** tgt, cson_object const * opt )
5171
-{
5172
- return cson_rc.UnsupportedError;
5173
-}
5174
-int cson_sessmgr_whio_epfs( cson_sessmgr ** tgt, cson_object const * opt )
5175
-{
5176
- return cson_rc.UnsupportedError;
5177
-}
5178
-#endif
5179
-
5180
-/**
5181
- Holds the list of registered cson_sessmgr implementations. Used by
5182
- cson_sessmgr_register(), cson_sessmgr_load(), and
5183
- cson_sessmgr_names().
5184
-
5185
- Maintenance reminder: the API docs promise that at least 10 slots
5186
- are initially available.
5187
-*/
5188
-static cson_sessmgr_reg CsonSessionReg[] = {
5189
-{{'f','i','l','e',0},cson_sessmgr_file},
5190
-#if CSON_ENABLE_CPDO
5191
-{{'c','p','d','o',0},cson_sessmgr_cpdo},
5192
-#endif
5193
-#if CSON_ENABLE_WHIO
5194
-{{'w','h','i','o','_','h','t',0},cson_sessmgr_whio_ht},
5195
-{{'w','h','i','o','_','e','p','f','s',0},cson_sessmgr_whio_epfs},
5196
-#endif
5197
-#define REG {{0},NULL}
5198
-REG,REG,REG,REG,REG,
5199
-REG,REG,REG,REG,REG
5200
-#undef REG
5201
-};
5202
-static const unsigned int CsonSessionRegLen = sizeof(CsonSessionReg)/sizeof(CsonSessionReg[0]);
5203
-
5204
-
5205
-int cson_sessmgr_register( char const * name, cson_sessmgr_factory_f f )
5206
-{
5207
- if( ! name || !*name || !f ) return cson_rc.ArgError;
5208
- else
5209
- {
5210
- cson_sessmgr_reg * r = CsonSessionReg;
5211
- unsigned int nlen = strlen(name);
5212
- unsigned int i = 0;
5213
- if( nlen >= CsonSessionNameLen ) return cson_rc.RangeError;
5214
- for( ; i < CsonSessionRegLen; ++i, ++r )
5215
- {
5216
- if( r->name[0] ) continue;
5217
- memcpy( r->name, name, nlen );
5218
- r->name[nlen] = 0;
5219
- r->factory = f;
5220
- return 0;
5221
- }
5222
- return cson_rc.RangeError;
5223
- }
5224
-}
5225
-
5226
-
5227
-int cson_sessmgr_load( char const * name, cson_sessmgr ** tgt, cson_object const * opt )
5228
-{
5229
- if( ! name || !*name || !tgt ) return cson_rc.ArgError;
5230
- else
5231
- {
5232
- cson_sessmgr_reg const * r = CsonSessionReg;
5233
- unsigned int i = 0;
5234
- for( ; i < CsonSessionRegLen; ++i, ++r )
5235
- {
5236
- if( ! r->name[0] ) break /* end of list */;
5237
- else if( 0 != strcmp( r->name, name ) ) continue;
5238
- else
5239
- {
5240
- assert( NULL != r->factory );
5241
- return r->factory( tgt, opt );
5242
- }
5243
-
5244
- }
5245
- return cson_rc.UnsupportedError;
5246
- }
5247
-}
5248
-
5249
-char const * const * cson_sessmgr_names()
5250
-{
5251
- static char const * names[sizeof(CsonSessionReg)/sizeof(CsonSessionReg[0])+1];
5252
- unsigned int i = 0;
5253
- cson_sessmgr_reg const * r = CsonSessionReg;
5254
- for( ; i < CsonSessionRegLen; ++i, ++r )
5255
- {
5256
- /*
5257
- pedantic threading note: as long as this function is not
5258
- used concurrently with cson_sessmgr_register(), the worst we
5259
- will do here if this function is called, or its results
5260
- used, concurrently is overwrite in-use values with the same
5261
- values.
5262
- */
5263
- names[i] = r->name[0] ? r->name : NULL;
5264
- }
5265
- names[i] = NULL;
5266
- return names;
5267
-}
5268
-/* end file ./cson_session.c */
5269
-/* begin file ./cson_session_file.c */
5270
-#if !defined(_WIN32) && !defined(_WIN64)
5271
-# if !defined(_POSIX_VERSION)
5272
-# define _POSIX_VERSION 200112L /* chmod(), unlink() */
5273
-# endif
5274
-# define ENABLE_POSIX_FILE_OPS 1
5275
-#else
5276
-# define ENABLE_POSIX_FILE_OPS 0
5277
-#endif
5278
-
5279
-#include <stdlib.h>
5280
-#include <string.h>
5281
-#include <assert.h>
5282
-#if ENABLE_POSIX_FILE_OPS
5283
-# define UNLINK_FILE unlink
5284
-# include <unistd.h> /* unlink() */
5285
-# include <sys/stat.h> /* chmod() */
5286
-#else
5287
-/* http://msdn.microsoft.com/en-us/library/1c3tczd6(v=vs.80).aspx
5288
- # define UNLINK_FILE _unlink
5289
- # include <io.h>
5290
-*/
5291
-# define UNLINK_FILE remove
5292
-# include <stdio.h> /* remove(), _unlink() */
5293
-#endif
5294
-
5295
-static int cson_session_file_load( cson_sessmgr * self, cson_value ** tgt, char const * id );
5296
-static int cson_session_file_save( cson_sessmgr * self, cson_value const * root, char const * id );
5297
-static int cson_session_file_remove( cson_sessmgr * self, char const * id );
5298
-static void cson_session_file_finalize( cson_sessmgr * self );
5299
-
5300
-static const cson_sessmgr_api cson_sessmgr_api_file =
5301
-{
5302
- cson_session_file_load,
5303
- cson_session_file_save,
5304
- cson_session_file_remove,
5305
- cson_session_file_finalize
5306
-};
5307
-
5308
-typedef struct cson_sessmgr_file_impl cson_sessmgr_file_impl;
5309
-struct cson_sessmgr_file_impl
5310
-{
5311
- char * dir;
5312
- char * prefix;
5313
- char * suffix;
5314
-};
5315
-
5316
-static const cson_sessmgr cson_sessmgr_file_empty =
5317
-{
5318
- &cson_sessmgr_api_file,
5319
- NULL
5320
-};
5321
-
5322
-static char * cson_session_file_strdup( char const * src )
5323
-{
5324
- size_t const n = src ? strlen(src) : 0;
5325
- char * rc = src ? (char *)calloc(1, n+1) : NULL;
5326
- if( ! rc ) return NULL;
5327
- memcpy( rc, src, n );
5328
- return rc;
5329
-}
5330
-
5331
-/* Helper macro for varios cson_sessmgr_api member implementations. */
5332
-#define IMPL_DECL(RC) \
5333
- cson_sessmgr_file_impl * impl = (self && (self->api == &cson_sessmgr_api_file)) \
5334
- ? (cson_sessmgr_file_impl*)self->impl \
5335
- : NULL; \
5336
- if( NULL == impl ) return RC
5337
-
5338
-static int cson_session_file_name( cson_sessmgr_file_impl * impl,
5339
- char const * id,
5340
- char * buf, unsigned int bufLen )
5341
-{
5342
- char const * dir = impl->dir ? impl->dir : ".";
5343
- char const * pre = impl->prefix ? impl->prefix : "";
5344
- char const * suf = impl->suffix ? impl->suffix : "";
5345
- char * pos = NULL /* current write possition. */;
5346
- unsigned int flen = 0 /* length of the next token. */;
5347
- unsigned int olen = 0 /* total number of bytes written so far. */;
5348
- if( ! id || !*id ) return cson_rc.ArgError;
5349
-
5350
-#define CHECKLEN if(olen >= bufLen) return cson_rc.RangeError; assert( pos < (buf+bufLen) )
5351
- pos = buf;
5352
-
5353
-#define PUSH(FIELD) \
5354
- flen = strlen(FIELD); \
5355
- olen += flen; \
5356
- CHECKLEN; \
5357
- strncpy( pos, FIELD, flen ); \
5358
- pos += flen
5359
-
5360
- PUSH(dir);
5361
-
5362
- ++olen;
5363
- CHECKLEN;
5364
-#if defined(_WIN32)
5365
- *(pos++) = '\\';
5366
-#else
5367
- *(pos++) = '/';
5368
-#endif
5369
-
5370
- PUSH(pre);
5371
- PUSH(id);
5372
- PUSH(suf);
5373
- if( pos >= (buf + bufLen) ) return cson_rc.RangeError;
5374
- *pos = 0;
5375
- return 0;
5376
-#undef PUSH
5377
-#undef CHECKLEN
5378
-}
5379
-
5380
-static int cson_session_file_load( cson_sessmgr * self, cson_value ** root, char const * id )
5381
-{
5382
- enum { BufSize = 1024 };
5383
- char fname[BufSize];
5384
- FILE * fh = NULL;
5385
- int rc;
5386
- IMPL_DECL(cson_rc.ArgError);
5387
- if( !root || !id || !*id ) return cson_rc.ArgError;
5388
- memset( fname, 0, BufSize );
5389
- rc = cson_session_file_name( impl, id, fname, BufSize );
5390
- if( 0 != rc ) return rc;
5391
- fh = fopen( fname, "r" );
5392
- if( ! fh ) return cson_rc.IOError;
5393
- rc = cson_parse_FILE( root, fh, NULL, NULL );
5394
- fclose( fh );
5395
- return rc;
5396
-}
5397
-
5398
-static int cson_session_file_save( cson_sessmgr * self, cson_value const * root, char const * id )
5399
-{
5400
- enum { BufSize = 1024 };
5401
- char fname[BufSize];
5402
- FILE * fh = NULL;
5403
- int rc;
5404
- IMPL_DECL(cson_rc.ArgError);
5405
- if( !root || !id || !*id ) return cson_rc.ArgError;
5406
- memset( fname, 0, BufSize );
5407
-
5408
- rc = cson_session_file_name( impl, id, fname, BufSize );
5409
- if( 0 != rc ) return rc;
5410
- fh = fopen( fname, "w" );
5411
- if( ! fh ) return cson_rc.IOError;
5412
-#if ENABLE_POSIX_FILE_OPS
5413
- chmod( fname, 0600 );
5414
-#endif
5415
- rc = cson_output_FILE( root, fh, NULL );
5416
- fclose( fh );
5417
- if( rc )
5418
- {
5419
- UNLINK_FILE( fname );
5420
- }
5421
- return rc;
5422
-}
5423
-
5424
-void cson_session_file_finalize( cson_sessmgr * self )
5425
-{
5426
- if( self && (self->api == &cson_sessmgr_api_file) )
5427
- {
5428
- cson_sessmgr_file_impl * impl = (cson_sessmgr_file_impl *)self->impl;
5429
- free( impl->dir );
5430
- free( impl->prefix );
5431
- free( impl->suffix );
5432
- free( impl );
5433
- *self = cson_sessmgr_file_empty;
5434
- free( self );
5435
- }
5436
-}
5437
-
5438
-static int cson_session_file_remove( cson_sessmgr * self, char const * id )
5439
-{
5440
- enum { BufSize = 1024 };
5441
- char fname[BufSize];
5442
- int rc;
5443
- IMPL_DECL(cson_rc.ArgError);
5444
- if( !id || !*id ) return cson_rc.ArgError;
5445
- memset( fname, 0, BufSize );
5446
- rc = cson_session_file_name( impl, id, fname, BufSize );
5447
- if( 0 != rc ) return rc;
5448
- rc = UNLINK_FILE( fname );
5449
- return (0==rc) ? 0 : cson_rc.IOError;
5450
-}
5451
-
5452
-
5453
-int cson_sessmgr_file( cson_sessmgr ** tgt, cson_object const * opt )
5454
-{
5455
- int rc;
5456
- cson_sessmgr * m = tgt ? (cson_sessmgr *)malloc(sizeof(cson_sessmgr)) : NULL;
5457
- cson_sessmgr_file_impl * impl = m
5458
- ? (cson_sessmgr_file_impl *)malloc(sizeof(cson_sessmgr_file_impl))
5459
- : NULL;
5460
- if( ! m ) return tgt ? cson_rc.AllocError : cson_rc.ArgError;
5461
- else if( ! impl )
5462
- {
5463
- free(m);
5464
- return cson_rc.AllocError;
5465
- }
5466
- *m = cson_sessmgr_file_empty;
5467
- m->impl = impl;
5468
- if( opt )
5469
- {
5470
- cson_string const * jstr;
5471
-#define CP(KEY) \
5472
- jstr = cson_value_get_string( cson_object_get( opt, # KEY ) ); \
5473
- if( jstr ) { \
5474
- impl->KEY = cson_session_file_strdup( cson_string_cstr( jstr ) ); \
5475
- if( ! impl->KEY ) { \
5476
- rc = cson_rc.AllocError; \
5477
- goto error_clean; \
5478
- } \
5479
- } (void)0
5480
-
5481
- CP(dir);
5482
- CP(prefix);
5483
- CP(suffix);
5484
-#undef CP
5485
- }
5486
-#define CP(KEY,VAL) if( ! impl->KEY ) { \
5487
- impl->KEY = cson_session_file_strdup(VAL); \
5488
- if( ! impl->KEY ) { \
5489
- rc = cson_rc.AllocError; \
5490
- goto error_clean; \
5491
- } \
5492
- } (void)0
5493
-#if ENABLE_POSIX_FILE_OPS
5494
- CP(dir,"/tmp");
5495
-#else
5496
- CP(dir,".");
5497
-#endif
5498
- CP(prefix,"cson-session-");
5499
- CP(suffix,".json");
5500
-#undef CP
5501
- *tgt = m;
5502
- return 0;
5503
- error_clean:
5504
- m->api->finalize( m );
5505
- return rc;
5506
-}
5507
-
5508
-#undef IMPL_DECL
5509
-#undef ENABLE_POSIX_FILE_OPS
5510
-#undef UNLINK_FILE
5511
-/* end file ./cson_session_file.c */
55125138
/* begin file ./cson_sqlite3.c */
55135139
/** @file cson_sqlite3.c
55145140
55155141
This file contains the implementation code for the cson
55165142
sqlite3-to-JSON API.
@@ -5532,11 +5158,11 @@
55325158
55335159
#if defined(__cplusplus)
55345160
extern "C" {
55355161
#endif
55365162
5537
-static cson_value * cson_sqlite3_stmt_to_value( sqlite3_stmt * st, int col )
5163
+cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col )
55385164
{
55395165
if( ! st ) return NULL;
55405166
else
55415167
{
55425168
#if 0
@@ -5546,39 +5172,29 @@
55465172
#else
55475173
int const vtype = sqlite3_column_type(st,col);
55485174
#endif
55495175
switch( vtype )
55505176
{
5551
- case SQLITE_NULL:
5552
- return cson_value_null();
5553
- case SQLITE_INTEGER: {
5177
+ case SQLITE_NULL:
5178
+ return cson_value_null();
5179
+ case SQLITE_INTEGER:
5180
+ /* FIXME: for large integers fall back to Double instead. */
55545181
return cson_value_new_integer( (cson_int_t) sqlite3_column_int64(st, col) );
5555
- }
5556
- case SQLITE_FLOAT:
5557
- return cson_value_new_double( sqlite3_column_double(st, col) );
5558
- case SQLITE_BLOB: /* arguably fall through... */
5559
- case SQLITE_TEXT: {
5560
- char const * str = (char const *)sqlite3_column_text(st,col);
5561
- return cson_value_new_string(str, str ? strlen(str) : 0);
5562
- }
5563
- default:
5564
- return NULL;
5565
- }
5566
- }
5567
-}
5568
-
5569
-/**
5570
- st must be a valid prepared statement. This function creates
5571
- a JSON array containing its columns, in order.
5572
-
5573
- Returns a new array value on success, which the caller owns.
5574
- On error NULL is returned.
5575
-
5576
- st is not traversed or freed by this function - only the column
5577
- count and names are read.
5578
-*/
5579
-static cson_value * cson_sqlite3_stmt_cols( sqlite3_stmt * st )
5182
+ case SQLITE_FLOAT:
5183
+ return cson_value_new_double( sqlite3_column_double(st, col) );
5184
+ case SQLITE_BLOB: /* arguably fall through... */
5185
+ case SQLITE_TEXT: {
5186
+ char const * str = (char const *)sqlite3_column_text(st,col);
5187
+ return cson_value_new_string(str, str ? strlen(str) : 0);
5188
+ }
5189
+ default:
5190
+ return NULL;
5191
+ }
5192
+ }
5193
+}
5194
+
5195
+cson_value * cson_sqlite3_column_names( sqlite3_stmt * st )
55805196
{
55815197
cson_value * aryV = NULL;
55825198
cson_array * ary = NULL;
55835199
char const * colName = NULL;
55845200
int i = 0;
@@ -5608,10 +5224,78 @@
56085224
cson_value_free(aryV);
56095225
return NULL;
56105226
}
56115227
}
56125228
5229
+
5230
+cson_value * cson_sqlite3_row_to_object( sqlite3_stmt * st )
5231
+{
5232
+ cson_value * rootV = NULL;
5233
+ cson_object * root = NULL;
5234
+ char const * colName = NULL;
5235
+ int i = 0;
5236
+ int rc = 0;
5237
+ cson_value * currentValue = NULL;
5238
+ int const colCount = sqlite3_column_count(st);
5239
+ if( !colCount ) return NULL;
5240
+ rootV = cson_value_new_object();
5241
+ if(!rootV) return NULL;
5242
+ root = cson_value_get_object(rootV);
5243
+ for( i = 0; i < colCount; ++i )
5244
+ {
5245
+ colName = sqlite3_column_name( st, i );
5246
+ if( ! colName ) goto error;
5247
+ currentValue = cson_sqlite3_column_to_value(st,i);
5248
+ if( ! currentValue ) currentValue = cson_value_null();
5249
+ rc = cson_object_set( root, colName, currentValue );
5250
+ if( 0 != rc )
5251
+ {
5252
+ cson_value_free( currentValue );
5253
+ goto error;
5254
+ }
5255
+ }
5256
+ goto end;
5257
+ error:
5258
+ cson_value_free( rootV );
5259
+ rootV = NULL;
5260
+ end:
5261
+ return rootV;
5262
+}
5263
+
5264
+cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st )
5265
+{
5266
+ cson_value * aryV = NULL;
5267
+ cson_array * ary = NULL;
5268
+ int i = 0;
5269
+ int rc = 0;
5270
+ int const colCount = sqlite3_column_count(st);
5271
+ if( ! colCount ) return NULL;
5272
+ aryV = cson_value_new_array();
5273
+ if( ! aryV ) return NULL;
5274
+ ary = cson_value_get_array(aryV);
5275
+ rc = cson_array_reserve(ary, (unsigned int) colCount );
5276
+ if( 0 != rc ) goto error;
5277
+
5278
+ for( i = 0; i < colCount; ++i ){
5279
+ cson_value * elem = cson_sqlite3_column_to_value(st,i);
5280
+ if( ! elem ) goto error;
5281
+ rc = cson_array_append(ary,elem);
5282
+ if(0!=rc)
5283
+ {
5284
+ cson_value_free( elem );
5285
+ goto end;
5286
+ }
5287
+ }
5288
+ goto end;
5289
+ error:
5290
+ cson_value_free(aryV);
5291
+ aryV = NULL;
5292
+ end:
5293
+ return aryV;
5294
+}
5295
+
5296
+
56135297
/**
56145298
Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
56155299
parameter is non-0.
56165300
*/
56175301
static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
@@ -5624,20 +5308,16 @@
56245308
cson_object * root = NULL;
56255309
cson_value * colsV = NULL;
56265310
cson_value * rowsV = NULL;
56275311
cson_array * rows = NULL;
56285312
cson_value * objV = NULL;
5629
- cson_object * obj = NULL;
5630
- cson_value * currentValue = NULL;
5631
- char const * colName = NULL;
56325313
int rc = 0;
5633
- int i = 0;
56345314
int const colCount = sqlite3_column_count(st);
56355315
if( colCount <= 0 ) return cson_rc.ArgError;
56365316
rootV = cson_value_new_object();
56375317
if( ! rootV ) return cson_rc.AllocError;
5638
- colsV = cson_sqlite3_stmt_cols(st);
5318
+ colsV = cson_sqlite3_column_names(st);
56395319
if( ! colsV )
56405320
{
56415321
cson_value_free( rootV );
56425322
RETURN(cson_rc.AllocError);
56435323
}
@@ -5646,13 +5326,11 @@
56465326
if( rc )
56475327
{
56485328
cson_value_free( colsV );
56495329
RETURN(rc);
56505330
}
5651
-
56525331
colsV = NULL;
5653
-
56545332
rowsV = cson_value_new_array();
56555333
if( ! rowsV ) RETURN(cson_rc.AllocError);
56565334
rc = cson_object_set( root, "rows", rowsV );
56575335
if( rc )
56585336
{
@@ -5661,32 +5339,18 @@
56615339
}
56625340
rows = cson_value_get_array(rowsV);
56635341
assert(rows);
56645342
while( SQLITE_ROW == sqlite3_step(st) )
56655343
{
5666
- objV = cson_value_new_object();
5667
- if( ! objV ) RETURN(cson_rc.AllocError);
5344
+ objV = cson_sqlite3_row_to_object(st);
5345
+ if( ! objV ) RETURN(cson_rc.UnknownError);
56685346
rc = cson_array_append( rows, objV );
56695347
if( rc )
56705348
{
56715349
cson_value_free( objV );
56725350
RETURN(rc);
56735351
}
5674
- obj = cson_value_get_object(objV);
5675
- for( i = 0; i < colCount; ++i )
5676
- {
5677
- colName = sqlite3_column_name( st, i );
5678
- if( ! colName ) RETURN(cson_rc.AllocError);
5679
- currentValue = cson_sqlite3_stmt_to_value(st,i);
5680
- if( ! currentValue ) currentValue = cson_value_null();
5681
- rc = cson_object_set( obj, colName, currentValue );
5682
- if( 0 != rc )
5683
- {
5684
- cson_value_free( currentValue );
5685
- RETURN(rc);
5686
- }
5687
- }
56885352
}
56895353
*tgt = rootV;
56905354
return 0;
56915355
}
56925356
#undef RETURN
@@ -5706,18 +5370,16 @@
57065370
cson_object * root = NULL;
57075371
cson_value * aryV = NULL;
57085372
cson_array * ary = NULL;
57095373
cson_value * rowsV = NULL;
57105374
cson_array * rows = NULL;
5711
- cson_value * colV = NULL;
57125375
int rc = 0;
5713
- int i = 0;
57145376
int const colCount = sqlite3_column_count(st);
57155377
if( colCount <= 0 ) return cson_rc.ArgError;
57165378
rootV = cson_value_new_object();
57175379
if( ! rootV ) return cson_rc.AllocError;
5718
- aryV = cson_sqlite3_stmt_cols(st);
5380
+ aryV = cson_sqlite3_column_names(st);
57195381
if( ! aryV )
57205382
{
57215383
cson_value_free( rootV );
57225384
RETURN(cson_rc.AllocError);
57235385
}
@@ -5740,32 +5402,18 @@
57405402
}
57415403
rows = cson_value_get_array(rowsV);
57425404
assert(rows);
57435405
while( SQLITE_ROW == sqlite3_step(st) )
57445406
{
5745
- aryV = cson_value_new_array();
5746
- if( ! aryV ) RETURN(cson_rc.AllocError);
5407
+ aryV = cson_sqlite3_row_to_array(st);
5408
+ if( ! aryV ) RETURN(cson_rc.UnknownError);
57475409
rc = cson_array_append( rows, aryV );
57485410
if( 0 != rc )
57495411
{
57505412
cson_value_free( aryV );
57515413
RETURN(rc);
57525414
}
5753
- ary = cson_value_get_array(aryV);
5754
- rc = cson_array_reserve(ary, (unsigned int) colCount );
5755
- if( 0 != rc ) RETURN(rc);
5756
- for( i = 0; i < colCount; ++i )
5757
- {
5758
- colV = cson_sqlite3_stmt_to_value(st,i);
5759
- if( ! colV ) colV = cson_value_null();
5760
- rc = cson_array_set( ary, i, colV );
5761
- if( 0 != rc )
5762
- {
5763
- cson_value_free( colV );
5764
- RETURN(rc);
5765
- }
5766
- }
57675415
}
57685416
*tgt = rootV;
57695417
return 0;
57705418
}
57715419
#undef RETURN
@@ -5797,2721 +5445,5 @@
57975445
} /*extern "C"*/
57985446
#endif
57995447
#undef MARKER
58005448
#endif /* CSON_ENABLE_SQLITE3 */
58015449
/* end file ./cson_sqlite3.c */
5802
-/* begin file cgi/whuuid.h */
5803
-#if !defined(WANDERGINHORSE_NET_WHUUID_H_INCLUDED)
5804
-#define WANDERGINHORSE_NET_WHUUID_H_INCLUDED 1
5805
-#include <stdio.h> /* only for decl of FILE. */
5806
-/************************************************************************
5807
-An experiment in creating random UUIDs (http://wikipedia.org/wiki/Uuid).
5808
-
5809
-
5810
-Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)
5811
-
5812
-License: Public Domain
5813
-
5814
-
5815
-Features:
5816
-
5817
-- Small API. Only two relevant classes and a handful of functions.
5818
-
5819
-- Uses a client-specified RNG source. Two are provided with the
5820
-library. The RNG source may be arbitrarily stateful, and each may have
5821
-instance-specific data.
5822
-
5823
-- State objects have a uniform cleanup interface, but each implementation
5824
-defines which cleanup behaviours need to be performed (e.g. closing
5825
-an input file).
5826
-
5827
-- Fairly fast, assuming your RNG is. (My 2.6GHz PC can generate, and send
5828
-them to stdout, just over 1.3 million UUIDs per second.)
5829
-
5830
-
5831
-Misfeatures:
5832
-
5833
-- Does not support a specific version of UUID, as detailed at
5834
-[http://wikipedia.org/wiki/Uuid]. Its UUIDs have random data in all
5835
-positions, as opposed to reserving certain positions for specific
5836
-values or using specified algorithms to generate the values. Thus the
5837
-UUIDs it generates are similar to Version 4 UUIDs except that no bytes
5838
-are reserved for specific values.
5839
-
5840
-PS: i don't really consider that to be a mis-feature. IMHO UUIDs
5841
-should be completely random, with no reserved bytes.
5842
-
5843
-
-------------------------------------------------------------------------
5844
-TIP: checking for duplicate UUIDs
5845
-
5846
-The sqlite3 tool can be used for checking for duplicate UUIDs. Simply
5847
-print the UUIDs, one per line, and feed them into sqlite3 like so:
5848
-
5849
-@code
5850
-sqlite3> create table ids (id,unide(id));
5851
-sqlite3> .import myUUIDListFile ids
5852
-@endcode
5853
-
5854
-If sqlite3 does not complain, there were no duplicates.
5855
-
5856
-You can also test by sorting the list, removing duplicates, and
5857
-checking the length of the list. e.g. assume we have a file named "1m"
5858
-containing 1 million UUIDs. From a Unix shell we could do:
5859
-
5860
-@code
5861
-~> sort -u < 1m > 1ms
5862
-~> ls -la 1m 1ms
5863
-@endcode
5864
-
5865
-If the files have the same size then there were no duplicates.
5866
-
5867
-In my tests i have not encountered duplicates except when testing
5868
-a deterministic RNG with a specific seed.
5869
-************************************************************************/
5870
-
5871
-/** @def WHUUID_CONFIG_KEEP_METRICS
5872
-
5873
- If WHUUID_CONFIG_KEEP_METRICS is a true value then the library keeps track
5874
- of how many times a given hex digit value is generated by the
5875
- whuuid_rng class. It has a minimal performance hit, but if
5876
- the data will never be used then it can be turned off.
5877
-*/
5878
-#define WHUUID_CONFIG_KEEP_METRICS 1
5879
-
5880
-/** @enum whuuid_constants
5881
-
5882
-A list of constant values used by the whuuid API.
5883
-
5884
-*/
5885
-enum whuuid_constants {
5886
-/**
5887
- The length of a UUID canonical-form string, not including
5888
- a trailing NULL bytes. e.g.:
5889
-
5890
- 00000000-0000-0000-0000-000000000000
5891
-*/
5892
-whuuid_length_canonical = 36,
5893
-/**
5894
- The length of a UUID in canonical form, including
5895
- a trailing NULL byte.
5896
-*/
5897
-whuuid_length_cstring = whuuid_length_canonical + 1,
5898
-/**
5899
- The number of bytes of data necessary to store
5900
- a UUID in "raw" form.
5901
-*/
5902
-whuuid_length_bytes = 16
5903
-};
5904
-
5905
-/**
5906
- Represents a single UUID.
5907
-*/
5908
-struct whuuid_t
5909
-{
5910
- unsigned char bytes[whuuid_length_bytes];
5911
-};
5912
-typedef struct whuuid_t whuuid_t;
5913
-/**
5914
- A zero-initialized whuiid_t initialization object.
5915
-*/
5916
-extern const whuuid_t whuuid_t_empty;
5917
-
5918
-/**
5919
- A class holding RNG information. Each instance manages a single RNG
5920
- source, which is used to populate any number of whuiid_t objects
5921
- with random data. They may or may not need to dynamically allocate
5922
- resources (e.g. open a file containing random data), depending
5923
- on the implementation.
5924
-
5925
- They should normally be initialized via factory functions, and
5926
- those functions should:
5927
-
5928
- a) Allocate any private resources the object needs and store them in
5929
- self->impl.
5930
-
5931
- b) Set the cleanup() member function to a function which knows how
5932
- to clean up any resources stored in self->impl.
5933
-
5934
- c) Set the rand() member to a function which knows how to use
5935
- the private state to generate random data.
5936
-
5937
-
5938
- The most basic usage looks something like this:
5939
-
5940
- @code
5941
- whuuid_rng st = whuuid_rng_lcrng; // a Linear Congruent RNG
5942
- whuuid_t u = whuuid_t_empty;
5943
- char buffer[whuuid_length_canonical+1]; // buffer for UUID string
5944
- buffer[whuuid_length_canonical] = 0; // add trailing NULL
5945
- for( int i =0; i < 100; ++i )
5946
- {// generate 100 UUIDs to print them
5947
- whuuid_fill_rand( &u, &st ); // generate UUID using st->rand()
5948
- whuuid_to_string( &u, buffer );
5949
- puts(buffer);
5950
- }
5951
- st.cleanup(&st); // see below.
5952
- @endcode
5953
-
5954
- In that particular case the state object has no state which
5955
- needs cleaning, but we could also set up a FILE as an input source,
5956
- in which case we need to clean up the object:
5957
-
5958
- @code
5959
- st = whuuid_rng_FILE;
5960
- st.impl = fopen("/dev/urandom", "r");
5961
- ... use st ...
5962
- st.cleanup(&st); // will fclose() the file
5963
- @endcode
5964
-
5965
- If a state object is dynamically allocated then it should be freed
5966
- after calling its cleanup() member to free any
5967
- implementation-specific resources.
5968
-*/
5969
-struct whuuid_rng
5970
-{
5971
- /**
5972
- Must set *tgt to sizeof(unsigned int) random bytes. Must return
5973
- 0 on success or non-zero if something goes wrong (e.g. the
5974
- input source has failed or run out of numbers). How it uses (or
5975
- ignores) the self argument is implementation-specific.
5976
- */
5977
- int (*rand)( struct whuuid_rng * self, unsigned int * tgt );
5978
- /**
5979
- Must clean up self, but not free self itself. How it does this
5980
- is implementation-specific. If it has no private state,
5981
- this function may be NULL.
5982
-
5983
- whuuid_rng objects can be allocated on the stack or via
5984
- arbitrary mechanisms, so the cleanup routine must not free the
5985
- self object. How it is freed (after it is cleaned up) depends
5986
- on how it was allocated.
5987
- */
5988
- void (*cleanup)( struct whuuid_rng * self );
5989
- /**
5990
- Implementations may store any private state here. This member is
5991
- not for public use.
5992
- */
5993
- void * impl;
5994
- /**
5995
- Stores the distribution of values created by this state
5996
- object. whuuid_fill_rand() updates these values.
5997
- */
5998
- unsigned long distribution[whuuid_length_bytes];
5999
-};
6000
-
6001
-
6002
-/** Convenience typedef. */
6003
-typedef struct whuuid_rng whuuid_rng;
6004
-
6005
-/**
6006
- A zero-initialized whuiid_state initialization object.
6007
-*/
6008
-extern const whuuid_rng whuuid_rng_empty;
6009
-
6010
-/**
6011
- An almost-empty whuiid_state initialization object with
6012
- its rand() member set to whuuid_lc_rand.
6013
-*/
6014
-extern const whuuid_rng whuuid_rng_lcrng;
6015
-
6016
-/**
6017
- A whuuid_state initialization object with its rand() member set to
6018
- whuuid_FILE_rand and its cleanup() member set to
6019
- whuuid_FILE_cleanup. Clients may copy this then set the impl
6020
- member to point it to an opened FILE handle. The FILE handle will
6021
- be closed when the cleanup() member is called. If the state object
6022
- should not close the file when it cleans up, set the cleanup()
6023
- member to NULL.
6024
-*/
6025
-extern const whuuid_rng whuuid_rng_FILE;
6026
-
6027
-/**
6028
- Implements the whuuid_rng::rand() interface.
6029
-
6030
- This implementaion uses/abuses st->impl to store a numeric state
6031
- value for a linear congruent RNG. If st->impl is NULL then a seed
6032
- value is generated using some external source (we malloc() a few
6033
- bytes to get a random address, and we use that address as a
6034
- seed). The state value is stored directly in st->impl and does not
6035
- need to be cleaned up. (The memory malloc()ed to get the initial
6036
- seed is free()d immediately after it is malloc()ed.)
6037
-
6038
- Returns 0 on success, non-zero on error. The only error conditions
6039
- are !st or !tgt. A malloc() error on the initial seeding will not
6040
- cause an error (but causes a determinate (but unspecified) seed
6041
- value to be used).
6042
-
6043
- In my (informal/unscientific) tests, this RNG works very well for
6044
- generating UUIDs, out-performing /dev/urandom in terms of even
6045
- numeric distribution most of the time.
6046
-*/
6047
-int whuuid_lc_rand( whuuid_rng * st, unsigned int *tgt );
6048
-
6049
-/**
6050
- Implements the whuuid_rng::rand() interface.
6051
-
6052
- If st->impl is not NULL it is assumed to be-a (FILE*) and
6053
- sizeof(unsigned int) bytes are read from it and returned via the
6054
- tgt argument.
6055
-
6056
- Returns non-zero if !st or !st->impl, or if reading from the file
6057
- fails.
6058
-
6059
- Results are undefined if st->impl is non-null but is-not-a FILE.
6060
-
6061
- Note that this implementation does nothing fancy like buffering
6062
- some larger amount of random input. Each call reads sizeof(int)
6063
- bytes. If performance is of a concern, create an implementation
6064
- which stores a struct containing the FILE and the buffer somewhere
6065
- in st->impl and reads the input in larger blocks. Also implement a
6066
- cleanup function which can free the buffer.
6067
-
6068
- @see whuuid_FILE_cleanup()
6069
- @see whuuid_rng_FILE
6070
-*/
6071
-int whuuid_FILE_rand( whuuid_rng * st, unsigned int * tgt );
6072
-
6073
-/**
6074
- Implements the whuuid_rng::cleanup() interface for state
6075
- objects where obj->impl is-a FILE handle opened via
6076
- fopen() (or equivalent).
6077
-
6078
- Assumes self->impl is-a (FILE*) and calls fclose() on it.
6079
-*/
6080
-void whuuid_FILE_cleanup( whuuid_rng * self );
6081
-
6082
-/**
6083
- Converts src->bytes to a canonical-form UUID string. dest must be
6084
- valid memory at least whuuid_length_canonical bytes long, and on
6085
- success exactly whuuid_length_canonical bytes will be written to it.
6086
- No terminating null is added.
6087
-
6088
- Returns 0 on success, non-zero on error. The only error conditions
6089
- are (!src) or (!dest).
6090
-*/
6091
-int whuuid_to_string( whuuid_t const * src, char * dest );
6092
-
6093
-/**
6094
- Populates all of dest->bytes, using st->rand() to collect the
6095
- random bytes. It calls st->rand() enough times to collect
6096
- whuuid_length_bytes bytes.
6097
-
6098
- Returns 0 on success, non-0 on error. The error conditions are:
6099
-
6100
- - !st or !dest
6101
-
6102
- - st->rand() returns non-0, in which case that error code is passed
6103
- back to the caller.
6104
-
6105
- st->distribution is modified by this function to record the number
6106
- of times any given digit (hex 0..f) is generated via a call to
6107
- rand() (but note that each call to st->rand() is used to generate
6108
- (sizeof(unsigning int)*2) digits).
6109
-
6110
- This routine does not guaranty that the bytes returned by
6111
- st->rand() are used in the exact same order as they are returned.
6112
-*/
6113
-int whuuid_fill_rand( whuuid_t * dest, whuuid_rng * st );
6114
-
6115
-/**
6116
- Copies whuuid_length_bytes bytes from src to dest->bytes.
6117
-
6118
- Returns 0 on success. The only error cases are !dest or !src.
6119
-*/
6120
-int whuuid_fill( whuuid_t * dest, unsigned char const * src );
6121
-
6122
-
6123
-/**
6124
- Compares lhs->bytes and rhs->bytes and
6125
- returns 0, less than 0, or greater than 0 depending on whether
6126
- lhs equals, is less than, or is greater to rhs, respectively.
6127
- i.e. it behaves like memcmp(3).
6128
-
6129
- A NULL value for lhs or rhs compares as less-than any other value
6130
- except NULL, to which it compares equal.
6131
-*/
6132
-short whuuid_compare( whuuid_t const * lhs, whuuid_t const * rhs );
6133
-
6134
-/**
6135
- Debugging/testing function which dumps the RNG distribution counts
6136
- of st to the given FILE handle. The stats are updated on each call
6137
- to whuuid_fill_rand() IF the WHUUID_CONFIG_KEEP_METRICS macro is
6138
- set to a true value when the library is built.
6139
-
6140
- If full is non-zero then a full list of metrics is dumped,
6141
- otherwise just an overview.
6142
-
6143
- Returns 0 on success, non-zero on error (!dest, !st, or
6144
- WHUUID_CONFIG_KEEP_METRICS is false).
6145
-*/
6146
-int whuuid_dump_distribution( whuuid_rng const * st, short full, FILE * dest );
6147
-
6148
-#endif /* WANDERGINHORSE_NET_WHUUID_H_INCLUDED */
6149
-/* end file cgi/whuuid.h */
6150
-/* begin file cgi/whuuid.c */
6151
-#include <assert.h>
6152
-#include <string.h> /* memset() */
6153
-
6154
-#include <stdlib.h> /* malloc(), free() */
6155
-
6156
-
6157
-#if WHUUID_CONFIG_KEEP_METRICS
6158
-# include <stdio.h> /* fprintf(), FILE */
6159
-#endif
6160
-
6161
-const whuuid_t whuuid_t_empty = {
6162
-{0,0,0,0,
6163
- 0,0,0,0,
6164
- 0,0,0,0,
6165
- 0,0,0,0}/*bytes*/
6166
-};
6167
-
6168
-
6169
-static void whuuid_noop_cleanup( whuuid_rng * self )
6170
-{
6171
- /* does nothing */
6172
-}
6173
-/**
6174
- An almost-empty-initialized whuuid_rng object which uses
6175
- whuuid_rand_uuint() as its random data source. It has no resources
6176
- associated with it.
6177
-*/
6178
-const whuuid_rng whuuid_rng_empty = {
6179
-NULL/*rand*/,
6180
-whuuid_noop_cleanup/*cleanup*/,
6181
-NULL/*impl*/,
6182
-{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
6183
-};
6184
-
6185
-const whuuid_rng whuuid_rng_lcrng = {
6186
-whuuid_lc_rand/*rand*/,
6187
-whuuid_noop_cleanup/*cleanup*/,
6188
-NULL/*impl*/,
6189
-{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
6190
-};
6191
-
6192
-const whuuid_rng whuuid_rng_FILE = {
6193
-whuuid_FILE_rand/*rand*/,
6194
-whuuid_FILE_cleanup/*cleanup*/,
6195
-NULL/*impl*/,
6196
-{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
6197
-};
6198
-
6199
-/** BITS2CHAR(X) expects (X<=15). Returns the hex-code char for it
6200
- ('0'..'f'), or 0 if X is out of range. */
6201
-#define BITS2CHAR(X) ( ((X)<=0x09) ? ('0'+(X)) : (((X)<=0xF) ? ('a'+((X)-10)) : 0))
6202
-
6203
-
6204
-void whuuid_FILE_cleanup( whuuid_rng * self )
6205
-{
6206
- if( self && self->impl )
6207
- {
6208
- fclose( (FILE*)self->impl );
6209
- self->impl = 0;
6210
- }
6211
-}
6212
-
6213
-int whuuid_FILE_rand( whuuid_rng * st, unsigned int * tgt )
6214
-{
6215
- if( st && st->impl )
6216
- {
6217
- unsigned int d = 0;
6218
- if( 1 != fread( &d, sizeof(d), 1, (FILE*)st->impl ) )
6219
- {
6220
- return -1;
6221
- }
6222
- *tgt = d;
6223
- return 0;
6224
- }
6225
- return -1;
6226
-}
6227
-
6228
-#include <time.h>
6229
-int whuuid_lc_rand( whuuid_rng * st, unsigned int * tgt )
6230
-{
6231
- typedef unsigned long NumType;
6232
- NumType num = (NumType)st->impl;
6233
- if( ! st || ! tgt ) return -1;
6234
-#define RNG(SEED) (NumType)( (NumType)((NumType)(SEED) * (NumType)1103515245) + 12345)
6235
- /* ^^^^^ This RNG Works very well for this use case (comparable
6236
- with /dev/urandom on my box). Algo found in Angband sources. */
6237
- if( ! num )
6238
- {
6239
- void * x;
6240
- num = (NumType) st;
6241
- /* Generate a unique seed. */
6242
- x = malloc( (num % 13)+9 );
6243
- free(x);
6244
- num = (NumType)(RNG(x) ^ num) >> 6
6245
- /*
6246
- The bitshift part is to work around the problem that the
6247
- left-most byte of generated UUIDs always have the same
6248
- starting sequences.
6249
- */
6250
- ;
6251
- }
6252
- else
6253
- {
6254
- num = RNG(num);
6255
- }
6256
-#undef RNG
6257
- st->impl = (void *)num;
6258
- *tgt = num;
6259
- return 0;
6260
-}
6261
-
6262
-int whuuid_to_string( whuuid_t const * src, char * dest )
6263
-{
6264
- unsigned int i = 0;
6265
- int part = 1;
6266
- int span = 0;
6267
- char byte = 0;
6268
- char nibble = 0;
6269
- if( ! src || ! dest ) return -1;
6270
- for( i = 0; i < whuuid_length_bytes; )
6271
- {
6272
- int x;
6273
- if( 1 == part ) span = 8;
6274
- else if( (part>1) && (part<5) ) span = 4;
6275
- else if( 5 == part ) span = 12;
6276
- for( x = 0; x < (span/2); ++x )
6277
- {
6278
- byte = src->bytes[i++];
6279
- nibble = (byte >> 4) & 0x0F;
6280
- *(dest++) = BITS2CHAR(nibble);
6281
- nibble = (byte & 0x0F);
6282
- *(dest++) = BITS2CHAR(nibble);
6283
- }
6284
- if( part < 5 )
6285
- {
6286
- *(dest++) = '-';
6287
- ++part;
6288
- }
6289
- else break;
6290
- }
6291
- return 0;
6292
-}
6293
-
6294
-int whuuid_fill( whuuid_t * dest, unsigned char const * src )
6295
-{
6296
- if( ! dest || ! src ) return -1;
6297
- else
6298
- {
6299
- memcpy( dest, src, whuuid_length_bytes );
6300
- return 0;
6301
- }
6302
-}
6303
-
6304
-int whuuid_fill_rand( whuuid_t * dest, whuuid_rng * st )
6305
-{
6306
- unsigned int i = 0, x = 0;
6307
- unsigned char * c = 0;
6308
- unsigned int r;
6309
- unsigned char nibble;
6310
- int rc = 0;
6311
- if( ! st || ! dest ) return -1;
6312
- if( ! dest ) return -1;
6313
- for( ; i < whuuid_length_bytes; )
6314
- {
6315
- rc = st->rand(st, &r);
6316
- if( rc ) break;
6317
- c = (unsigned char *)&r;
6318
- for( x = sizeof(r); (x > 0) && (i < whuuid_length_bytes); --x, ++i, ++c )
6319
- {
6320
- dest->bytes[i] = *c;
6321
-#if WHUUID_CONFIG_KEEP_METRICS
6322
- nibble = (*c >> 4) & 0x0F;
6323
- ++st->distribution[nibble];
6324
- nibble = (*c & 0x0F);
6325
- ++st->distribution[nibble];
6326
-#endif
6327
- }
6328
- }
6329
- return rc;
6330
-}
6331
-
6332
-short whuuid_compare( whuuid_t const * lhs, whuuid_t const * rhs )
6333
-{
6334
- if( ! lhs ) return rhs ? -1 : 0;
6335
- else if( ! rhs ) return 1;
6336
- else if( lhs == rhs ) return 0;
6337
- else
6338
- {
6339
-#if 0
6340
- unsigned int i = 0;
6341
- unsigned char const * l = lhs->bytes;
6342
- unsigned char const * r = rhs->bytes;
6343
- unsigned char bl = 0, br = 0; /* current byte of lhs/rhs*/
6344
- unsigned char nl = 0, nr = 0;/* 4-bit part of bl/br*/
6345
- for( ; i < whuuid_length_bytes; ++i )
6346
- {
6347
- bl = l[i];
6348
- br = r[i];
6349
- nl = (bl >> 4);
6350
- nr = (br >> 4);
6351
- if( nl < nr ) return -1;
6352
- else if( nl > nr ) return 1;
6353
- nl = (bl & 0x0F);
6354
- nr = (br & 0x0F);
6355
- if( nl < nr ) return -1;
6356
- else if( nl > nr ) return 1;
6357
- }
6358
- return 0;
6359
-#else
6360
- return memcmp( lhs->bytes, rhs->bytes, whuuid_length_bytes );
6361
-#endif
6362
- }
6363
-}
6364
-
6365
-int whuuid_dump_distribution( whuuid_rng const * st, short full, FILE * dest )
6366
-{
6367
-#if ! WHUUID_CONFIG_KEEP_METRICS
6368
- fprintf("WHUUID_CONFIG_KEEP_METRICS is false, so whuuid_dump_distribution() cannot work!\n");
6369
- return -1;
6370
-#else
6371
- unsigned short i = 0;
6372
- double total = 0;
6373
- unsigned long int max = 0, min = st->distribution[0];
6374
- unsigned long int x = 0;
6375
- char minL = 0, maxL = 0;
6376
- if( full )
6377
- {
6378
- fprintf(dest,"Random number distribution:\nDigit:\tCount:\n");
6379
- }
6380
- for( ; i < 16; ++i )
6381
- {
6382
- x = st->distribution[i];
6383
- total += x;
6384
- if( max < x )
6385
- {
6386
- max = x;
6387
- maxL = BITS2CHAR(i);
6388
- }
6389
- else if( min >= x )
6390
- {
6391
- min = x;
6392
- minL = BITS2CHAR(i);
6393
- }
6394
- }
6395
- if( full )
6396
- {
6397
- for( i = 0; i < 16; ++i )
6398
- {
6399
- x = st->distribution[i];
6400
- fprintf(dest,"%c\t%lu (%0.6f%%)\n",
6401
- BITS2CHAR(i),
6402
- x, (x/ total) *100 );
6403
- }
6404
- }
6405
- fprintf(dest,"Least Hits: '%c' (%lu)\nMost Hits: '%c' (%lu)\n",
6406
- minL, min, maxL, max );
6407
- if( max == min )
6408
- {
6409
- fprintf(dest,"Least==Most == best possible random distribution!\n" );
6410
- }
6411
- else
6412
- {
6413
- fprintf(dest,"Max-Min diff = %lu (%0.4f%% of Max)\n", max - min, ((max - min)/(double)max)*100 );
6414
- }
6415
- fprintf(dest,"Total random 4-bit UUID digits: %0.0f\n\n",total);
6416
- return 0;
6417
-#endif
6418
-}
6419
-
6420
-#undef BITS2CHAR
6421
-/* end file cgi/whuuid.c */
6422
-/* begin file cgi/cson_cgi.c */
6423
-#include <assert.h>
6424
-#include <stdlib.h> /* environ, getenv(), atexit() */
6425
-#include <ctype.h> /* isspace() */
6426
-#include <string.h> /* strlen() */
6427
-#include <stdarg.h>
6428
-#include <time.h>
6429
-#include <locale.h> /* setlocale(), needed for JSON parser. */
6430
-
6431
-#if CSON_ENABLE_UNIX
6432
-# define CSON_CGI_USE_SIGNALS 1
6433
-#else
6434
-# define CSON_CGI_USE_SIGNALS 0
6435
-#endif
6436
-
6437
-/* If RNG_FILENAME evaluates to true then we use that file for getting
6438
- random bytes for session IDs. FIXME: we effectively leak a file
6439
- handle if this is enabled.
6440
-*/
6441
-#if 0
6442
-# define RNG_FILENAME "/dev/urandom"
6443
-#else
6444
-# define RNG_FILENAME NULL
6445
-#endif
6446
-
6447
-
6448
-#if 1
6449
-#define MARKER if(1) printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); if(1) printf
6450
-#else
6451
-static void noop_printf(char const * fmt, ...) {}
6452
-#define MARKER if(0) printf
6453
-#endif
6454
-
6455
-#if CSON_CGI_USE_SIGNALS
6456
-# include <signal.h> /* signal() */
6457
-#endif
6458
-
6459
-const cson_cgi_init_opt cson_cgi_init_opt_empty = cson_cgi_init_opt_empty_m;
6460
-
6461
-/**
6462
- Some cson_cgi-internal value keys.
6463
-*/
6464
-static const struct {
6465
- char const * ENV_GET;
6466
- char const * ENV_POST;
6467
- char const * ENV_COOKIE;
6468
- char const * ENV_SYS;
6469
- char const * ENV_APP;
6470
- char const * ENV_ARGV;
6471
- char const * ENV_CONFIG;
6472
- char const * ENV_SESSION;
6473
- char const * RESPONSE_HEADERS;
6474
-} cson_cgi_keys = {
6475
-"$GET",
6476
-"$POST",
6477
-"$COOKIE",
6478
-"$ENV",
6479
-"$APP",
6480
-"$ARGV",
6481
-"$CONFIG",
6482
-"$SESSION",
6483
-"response.headers"
6484
-};
6485
-
6486
-
6487
-/**
6488
- Shared state used by the cson_cgi API.
6489
-*/
6490
-const cson_cgi_cx cson_cgi_cx_empty = cson_cgi_cx_empty_m;
6491
-
6492
-static int cson_cgi_printf(cson_cgi_cx * cx, char const * fmt, ... )
6493
-{
6494
- if( ! fmt ) return 0;
6495
- else
6496
- {
6497
- int rc;
6498
- va_list vargs;
6499
- assert( NULL != cx->opt.outStream );
6500
- va_start( vargs, fmt );
6501
- rc = vfprintf( cx->opt.outStream, fmt, vargs );
6502
- /*if( rc > 0 ) fflush( cx->opt.outStream );*/
6503
- va_end( vargs );
6504
- return rc;
6505
- }
6506
-}
6507
-
6508
-static int cson_cgi_puts(cson_cgi_cx * cx, char const * str)
6509
-{
6510
- size_t const slen = str ? strlen(str) : 0;
6511
- if( slen )
6512
- {
6513
- if( 1 != fwrite( str, slen, 1, cx->opt.outStream ) )
6514
- {
6515
- return -1;
6516
- }
6517
- }
6518
- if( 1 != fwrite( "\n", 1, 1, cx->opt.outStream ) )
6519
- {
6520
- return -2;
6521
- }
6522
- return (int) (slen + 1);
6523
-}
6524
-
6525
-static int cson_cgi_putchar(cson_cgi_cx * cx, char ch)
6526
-{
6527
- return ( 1 == fwrite( &ch, 1, 1, cx->opt.outStream ) )
6528
- ? 1
6529
- : -1;
6530
-}
6531
-
6532
-
6533
-cson_value * cson_cgi_argv(cson_cgi_cx *cx)
6534
-{
6535
- return cx ? cx->argv.jval : NULL;
6536
-}
6537
-
6538
-cson_array * cson_cgi_argv_array(cson_cgi_cx * cx)
6539
-{
6540
- return cx ? cson_value_get_array( cx->argv.jval ) : NULL;
6541
-}
6542
-
6543
-int cson_cgi_gc_add( cson_cgi_cx * cx, char const * key, cson_value * v, char freeOnError )
6544
-{
6545
- int const rc = cson_object_set( cx->gc.jobj, key, v );
6546
- if( (0 != rc) && freeOnError )
6547
- {
6548
- cson_value_free( v );
6549
- }
6550
- return rc;
6551
-}
6552
-
6553
-int cson_cgi_response_root_set( cson_cgi_cx * cx, cson_value * v )
6554
-{
6555
- if( ! cx ) return cson_rc.ArgError;
6556
- else if( v && !cson_value_is_object(v) && !cson_value_is_array(v) )
6557
- {
6558
- return cson_rc.TypeError;
6559
- }
6560
- else if( cx->response.root != v )
6561
- {
6562
- int rc = 0;
6563
- rc = cson_cgi_gc_add(cx, "response.root", v, 0 )
6564
- /** TODO: confirm that cson_object_set() does not
6565
- clean up the original object if insertion fails.
6566
- If it does, we've just hosed the root node.
6567
- */
6568
- ;
6569
- if( 0 != rc )
6570
- {
6571
- return rc;
6572
- }
6573
- else
6574
- {
6575
- cx->response.root = v;
6576
- return 0;
6577
- }
6578
- }
6579
- else
6580
- {
6581
- return 0;
6582
- }
6583
-
6584
-}
6585
-cson_value * cson_cgi_response_root_get( cson_cgi_cx * cx, char createMode )
6586
-{
6587
- if( ! cx ) return NULL;
6588
- else if( cx->response.root ) return cx->response.root;
6589
- else
6590
- {
6591
- if( 0 != createMode )
6592
- {
6593
- if( createMode > 0 )
6594
- {
6595
- cx->response.root = cson_value_new_object();
6596
- }
6597
- else if( createMode < 0 )
6598
- {
6599
- cx->response.root = cson_value_new_array();
6600
- }
6601
- if( cx->response.root &&
6602
- (0 != cson_cgi_gc_add(cx, "response.root", cx->response.root, 1 )) )
6603
- {
6604
- cx->response.root = NULL /* was cleaned up by cson_cgi_gc_add() */;
6605
- }
6606
- }
6607
- return cx->response.root;
6608
- }
6609
-}
6610
-
6611
-
6612
-/** @internal
6613
-
6614
-Tokenizes an input string on a given separator. Inputs are:
6615
-
6616
-- (inp) = is a pointer to the pointer to the start of the input.
6617
-
6618
-- (separator) = the separator character
6619
-
6620
-- (end) = a pointer to NULL. i.e. (*end == NULL)
6621
-
6622
-This function scans *inp for the given separator char or a NULL char.
6623
-Successive separators at the start of *inp are skipped. The effect is
6624
-that, when this function is called in a loop, all neighboring
6625
-separators are ignored. e.g. the string "aa.bb...cc" will tokenize to
6626
-the list (aa,bb,cc) if the separator is '.' and to (aa.,...cc) if the
6627
-separator is 'b'.
6628
-
6629
-Returns 0 (false) if it finds no token, else non-0 (true).
6630
-
6631
-Output:
6632
-
6633
-- (*inp) will be set to the first character of the next token.
6634
-
6635
-- (*end) will point to the one-past-the-end point of the token.
6636
-
6637
-If (*inp == *end) then the end of the string has been reached
6638
-without finding a token.
6639
-
6640
-Post-conditions:
6641
-
6642
-- (*end == *inp) if no token is found.
6643
-
6644
-- (*end > *inp) if a token is found.
6645
-
6646
-It is intolerant of NULL values for (inp, end), and will assert() in
6647
-debug builds if passed NULL as either parameter.
6648
-
6649
-When looping, one must be sure to re-set the inp and end
6650
-parameters. For example:
6651
-
6652
-@code
6653
-char const * head = input;
6654
-char const * tail = NULL;
6655
-while( cson_cgi_next_token( &inp, '/', &tail ) ) {
6656
- ...
6657
- head = tail;
6658
- tail = NULL;
6659
-}
6660
-@endcode
6661
-
6662
-If the loop calls 'continue', it must be careful to
6663
-ensure that the parameters are re-set, to avoid an endless
6664
-loop. This can be simplified with a goto:
6665
-
6666
-@code
6667
-while( cson_cgi_next_token( &inp, '/', &tail ) ) {
6668
- if( some condition ) {
6669
- ... do something ...
6670
- goto next_iter;
6671
- }
6672
- else {
6673
- ....
6674
- }
6675
- next_iter;
6676
- head = tail;
6677
- tail = NULL;
6678
-}
6679
-@endcode
6680
-
6681
-*/
6682
-char cson_cgi_next_token( char const ** inp, char separator, char const ** end )
6683
-{
6684
- char const * pos = NULL;
6685
- assert( inp && end && *inp );
6686
- if( ! inp || !end ) return 0;
6687
- else if( *inp == *end ) return 0;
6688
- pos = *inp;
6689
- if( !*pos )
6690
- {
6691
- *end = pos;
6692
- return 0;
6693
- }
6694
- for( ; *pos && (*pos == separator); ++pos) { /* skip preceeding splitters */ }
6695
- *inp = pos;
6696
- for( ; *pos && (*pos != separator); ++pos) { /* find next splitter */ }
6697
- *end = pos;
6698
- return (pos > *inp) ? 1 : 0;
6699
-}
6700
-
6701
-/**
6702
- If map->jval is NULL then map->jval is created using
6703
- cson_value_new_object() and map->jobj is assigned to its object
6704
- reference. The newly-created map->jval is appended to
6705
- cx->gc to ensure that map->jval lives a full life (as
6706
- opposed to potentially being prematurly GC'd if a client later adds
6707
- map->jval to his own container).
6708
-
6709
- If map->jval is not NULL then this function is a no-op.
6710
-
6711
- This function will assert() if map is NULL.
6712
-
6713
- Returns 0 on success, else cson_rc.AllocError. On error map->jval
6714
- will be NULL after this call.
6715
-
6716
- On success, ownership of map->jval is transfered to (or potentially
6717
- shared with) cx->gc.
6718
-*/
6719
-static int cson_cgi_init_env_map( cson_cgi_cx * cx, char const * gckey, cson_cgi_env_map * map )
6720
-{
6721
- int rc = 0;
6722
- assert( NULL != map );
6723
- if( NULL == map->jval )
6724
- {
6725
- assert( NULL == map->jobj );
6726
- map->jval = cson_value_new_object();
6727
- if( NULL == map->jval ) return cson_rc.AllocError;
6728
- rc = cson_cgi_gc_add( cx, gckey, map->jval, 1 )
6729
- /* We do this to avoid a corner case in cleanup logic
6730
- if the client stores this object in another container.
6731
- */;
6732
- if( 0 != rc )
6733
- {
6734
- map->jval = NULL /* was cleaned up by cson_cgi_gc_add() */;
6735
- }
6736
- else
6737
- {
6738
- map->jobj = cson_value_get_object( map->jval );
6739
- assert( NULL != map->jobj );
6740
- }
6741
- }
6742
- return rc;
6743
-}
6744
-
6745
-char const * cson_cgi_getenv_cstr( cson_cgi_cx * cx, char const * where, char const * key )
6746
-{
6747
- return cson_string_cstr( cson_value_get_string( cson_cgi_getenv(cx, where, key) ) );
6748
-}
6749
-
6750
-cson_value * cson_cgi_path_part( cson_cgi_cx * cx, unsigned short ndx )
6751
-{
6752
- cson_value * piV = cson_cgi_getenv( cx, "e", "PATH_INFO_SPLIT" );
6753
- if( ! piV ) return NULL;
6754
- else
6755
- {
6756
- unsigned int alen;
6757
- cson_array * ar = cson_value_get_array(piV);
6758
- assert( NULL != ar );
6759
- alen = cson_array_length_get( ar );
6760
- return ( ndx >= alen )
6761
- ? NULL
6762
- : cson_array_get( ar, ndx );
6763
- }
6764
-}
6765
-
6766
-char const * cson_cgi_path_part_cstr( cson_cgi_cx * cx, unsigned short ndx )
6767
-{
6768
- return cson_string_cstr( cson_value_get_string( cson_cgi_path_part( cx, ndx ) ) );
6769
-}
6770
-
6771
-/**
6772
- cson_cgi_hexchar_to_int():
6773
-
6774
- For 'a'-'f', 'A'-'F' and '0'-'9', returns the appropriate decimal
6775
- number. For any other character it returns -1.
6776
-*/
6777
-static int cson_cgi_hexchar_to_int( int ch )
6778
-{
6779
- if( (ch>='a' && ch<='f') ) return ch-'a'+10;
6780
- else if( (ch>='A' && ch<='F') ) return ch-'A'+10;
6781
- else if( (ch>='0' && ch<='9') ) return ch-'0';
6782
- return -1;
6783
-}
6784
-
6785
-/**
6786
-
6787
- Replaces %XX patterns in str with their equivalent character and
6788
- '+' characters with a single whitespace. %XX patterns which are
6789
- not hexidecimal values are not translated.
6790
-
6791
- str must be NULL or a NUL-terminated string. If it is NULL or the
6792
- first byte is NUL then 0 is returned and this function has no
6793
- side-effects.
6794
-
6795
- BUGS(?): URL-decoding might have a few bugs/corner cases.
6796
-*/
6797
-static int cson_cgi_urldecode_inline( char * str )
6798
-{
6799
- unsigned char ch = 0;
6800
- unsigned char cx1 = 0;
6801
- unsigned char cx2 = 0;
6802
- int decoded;
6803
- unsigned char * pos = (unsigned char *)str;
6804
- unsigned char * out = pos;
6805
- unsigned char const * end;
6806
- size_t slen = (str && *str) ? strlen(str) : 0;
6807
- if( !slen ) return 0;
6808
- end = pos + slen;
6809
- for( ; pos < end; ++pos )
6810
- {
6811
- ch = *pos;
6812
- if( ch == '%' )
6813
- {
6814
- cx1 = *(pos+1);
6815
- /* FIXME: with only minor refactoring we can remove the
6816
- isxdigit() calls and use cson_cgi_hexchar_to_int()
6817
- instead, checking for a negative return value. That
6818
- would potentially save us 2 extra function calls here.
6819
- */
6820
- if( isxdigit(cx1) )
6821
- {
6822
- cx2 = *(pos+2);
6823
- if( isxdigit(cx2) )
6824
- {
6825
- decoded = (cson_cgi_hexchar_to_int( cx1 ) * 16)
6826
- + cson_cgi_hexchar_to_int( cx2 );
6827
- *(out++) = (char)decoded;
6828
- pos += 2;
6829
- continue;
6830
- }
6831
- /* else fall through... */
6832
- }
6833
- /* else fall through... */
6834
- }
6835
- else if( ch == '+' )
6836
- {
6837
- *(out++) = ' ';
6838
- continue;
6839
- }
6840
- *(out++) = ch;
6841
- }
6842
- *out = 0;
6843
- return 0;
6844
-}
6845
-
6846
-/**
6847
- If PATH_INFO is set, this function splits it on '/'
6848
- characters and creates an array out of the elements.
6849
- The array is stored as $ENV["PATH_INFO_SPLIT"].
6850
-
6851
- Returns non-0 on error. If PATH_INFO is not set,
6852
- 0 is returned. If it is set but has no entries,
6853
- an empty array is created.
6854
-
6855
- A return value of cson_rc.RangeError probably means that a path
6856
- element was longer than our internal buffer size, in which case
6857
- processing ends and PATH_INFO_SPLIT is not set. That error can
6858
- probably be ignored by the caller, but all others are probably
6859
- serious (e.g. AllocError).
6860
-*/
6861
-static int cson_cgi_import_path_info(cson_cgi_cx *cx)
6862
-{
6863
- char const * pi = cson_cgi_getenv_cstr(cx, "e","PATH_INFO");
6864
- if( NULL == pi ) return 0;
6865
- else
6866
- {
6867
- cson_value * arV = cson_value_new_array();
6868
- cson_array * ar;
6869
- char const * head = pi;
6870
- char const * tail = NULL;
6871
- if( ! arV ) return cson_rc.AllocError;
6872
- else
6873
- {
6874
- enum { BufSize = 128 };
6875
- char buf[BufSize];
6876
- cson_value * partV;
6877
- unsigned int slen;
6878
- int rc = 0;
6879
- ar = cson_value_get_array(arV);
6880
- while( cson_cgi_next_token( &head, '/', &tail ) )
6881
- {
6882
- slen = (tail-head);
6883
- if( slen >= BufSize )
6884
- {
6885
- rc = cson_rc.RangeError;
6886
- goto end_clean;
6887
- }
6888
- memcpy( buf, head, slen );
6889
- buf[slen] = 0;
6890
- cson_cgi_urldecode_inline( buf );
6891
- partV = cson_value_new_string( buf, strlen(buf) );
6892
- if( ! partV )
6893
- {
6894
- rc = cson_rc.AllocError;
6895
- goto end_clean;
6896
- }
6897
- rc = cson_array_append( ar, partV );
6898
- if( rc )
6899
- {
6900
- cson_value_free( partV );
6901
- goto end_clean;
6902
- }
6903
- partV = NULL;
6904
- head = tail;
6905
- tail = NULL;
6906
- }
6907
- assert( 0 == rc );
6908
- rc = cson_object_set( cx->request.env.jobj,
6909
- "PATH_INFO_SPLIT",
6910
- arV );
6911
- end_clean:
6912
- if( rc )
6913
- {
6914
- cson_value_free( arV );
6915
- }
6916
- return rc;
6917
- }
6918
- }
6919
-}
6920
-
6921
-/**
6922
- Imports (extern char ** environ) into cx->request.env, initializing
6923
- cx->request.env if needed. If called multiple times the environment
6924
- is re-read each time, but old entries which are no longer in the
6925
- new environment are not removed from cx->request.env.
6926
-
6927
- Returns 0 on success.
6928
-*/
6929
-static int cson_cgi_import_environ(cson_cgi_cx * cx)
6930
-{
6931
- extern char ** environ;
6932
- int i = 0;
6933
- char const * e = environ[0];
6934
- char const * v = NULL;
6935
- enum { KeyBufSize = 512 };
6936
- char keybuf[KeyBufSize];
6937
- char * kpos = NULL;
6938
- int rc = 0;
6939
- cson_value * jv = NULL;
6940
- rc = cson_cgi_init_env_map( cx, cson_cgi_keys.ENV_SYS, &cx->request.env );
6941
- if( 0 != rc ) return rc;
6942
- for( ; e && *e; e = environ[++i] )
6943
- {
6944
- v = NULL;
6945
- memset( keybuf, 0, KeyBufSize );
6946
- kpos = keybuf;
6947
- for( ; *e && ('=' != *e); ++e )
6948
- {
6949
- *(kpos++) = *e;
6950
- assert( kpos < (keybuf+KeyBufSize) );
6951
- if( kpos >= (keybuf+KeyBufSize) )
6952
- {
6953
- return cson_rc.RangeError;
6954
- }
6955
- }
6956
- if( '=' == *e )
6957
- {
6958
- v = e+1;
6959
- }
6960
- else
6961
- {
6962
- v = "";
6963
- }
6964
- jv = cson_value_new_string( v, strlen(v) );
6965
- if( NULL == jv )
6966
- {
6967
- rc = cson_rc.AllocError;
6968
- break;
6969
- }
6970
- rc = cson_object_set( cx->request.env.jobj, keybuf, jv );
6971
- if( 0 != rc ) break;
6972
- }
6973
- if( 0 == rc )
6974
- {
6975
- rc = cson_cgi_import_path_info(cx);
6976
- }
6977
- return rc;
6978
-}
6979
-
6980
-/**
6981
- Tries to save the current session data, if any, using the
6982
- configured session manager.
6983
-
6984
- Returns 0 on success. If the environment has no session,
6985
- it is treated as success but nothing is actually saved.
6986
-
6987
- If no session manager has been configured then
6988
- cson_rc.UnsupportedError is returned.
6989
-*/
6990
-static int cson_cgi_session_save(cson_cgi_cx * cx)
6991
-{
6992
- if( ! cx->session.mgr )
6993
- {
6994
- return cson_rc.UnsupportedError;
6995
- }
6996
- else if( !cx->session.id || !cx->session.env.jval )
6997
- {
6998
- return 0;
6999
- }
7000
- else
7001
- {
7002
- return cx->session.mgr->api->save( cx->session.mgr,
7003
- cx->session.env.jval,
7004
- cx->session.id );
7005
- }
7006
-}
7007
-
7008
-cson_cgi_cx * cson_cgi_cx_alloc()
7009
-{
7010
- cson_cgi_cx * rc = (cson_cgi_cx *)malloc(sizeof(cson_cgi_cx));
7011
- if( rc )
7012
- {
7013
- *rc = cson_cgi_cx_empty;
7014
- rc->misc.allocStamp = rc;
7015
- }
7016
- return rc;
7017
-}
7018
-
7019
-char cson_cgi_cx_clean( cson_cgi_cx * cx )
7020
-{
7021
- if( !cx ) return 0;
7022
- else
7023
- {
7024
- void const * allocStamp = NULL;
7025
- if( cx->session.mgr )
7026
- {
7027
- cson_cgi_session_save(cx) /* ignoring error code */;
7028
- cx->session.mgr->api->finalize( cx->session.mgr );
7029
- cx->session.mgr = NULL;
7030
- }
7031
- if(NULL != cx->gc.jval)
7032
- {
7033
- cson_value_free( cx->gc.jval );
7034
- cx->gc.jval = NULL;
7035
- cx->gc.jobj = NULL;
7036
- }
7037
- if( cx->session.id )
7038
- {
7039
- free( cx->session.id );
7040
- cx->session.id = NULL;
7041
- }
7042
- cson_buffer_reserve( &cx->tmpBuf, 0 );
7043
- allocStamp = cx->misc.allocStamp;
7044
- if( cx->opt.inStream && (stdin != cx->opt.inStream) ) fclose(cx->opt.inStream);
7045
- if( cx->opt.outStream && (stderr == cx->opt.outStream) && (stdout != cx->opt.outStream) ) fclose(cx->opt.outStream);
7046
- if( cx->opt.errStream && (stderr == cx->opt.errStream) && (stdout != cx->opt.errStream) ) fclose(cx->opt.errStream);
7047
- *cx = cson_cgi_cx_empty;
7048
- return ( allocStamp == cx )
7049
- ? (free( cx ), 1)
7050
- : 0;
7051
- }
7052
-}
7053
-
7054
-cson_value * cson_cgi_env_get_val( cson_cgi_cx * cx, char which, char createIfNeeded )
7055
-{
7056
- cson_cgi_env_map * map = NULL;
7057
- cson_value * v = NULL;
7058
- char const * gckey = NULL;
7059
- switch( which )
7060
- {
7061
- case 'c':
7062
- case 'C':
7063
- map = &cx->request.cookie;
7064
- gckey = cson_cgi_keys.ENV_COOKIE;
7065
- break;
7066
- case 'e':
7067
- case 'E':
7068
- gckey = cson_cgi_keys.ENV_SYS;
7069
- map = &cx->request.env;
7070
- break;
7071
- case 'g':
7072
- case 'G':
7073
- gckey = cson_cgi_keys.ENV_GET;
7074
- map = &cx->request.get;
7075
- break;
7076
- case 'f':
7077
- case 'F':
7078
- gckey = cson_cgi_keys.ENV_CONFIG;
7079
- map = &cx->config;
7080
- break;
7081
- case 'p':
7082
- case 'P':
7083
- gckey = cson_cgi_keys.ENV_POST;
7084
- map = &cx->request.post;
7085
- break;
7086
- case 'a':
7087
- case 'A':
7088
- gckey = cson_cgi_keys.ENV_APP;
7089
- map = &cx->clientEnv;
7090
- break;
7091
- case 's':
7092
- case 'S':
7093
- gckey = cson_cgi_keys.ENV_SESSION;
7094
- map = &cx->session.env;
7095
- break;
7096
- default:
7097
- break;
7098
- }
7099
- if( map )
7100
- {
7101
- v = map->jval;
7102
- if( !v && createIfNeeded )
7103
- {
7104
- assert( NULL != gckey );
7105
- cson_cgi_init_env_map( cx, gckey, map );
7106
- v = map->jval;
7107
- }
7108
- }
7109
- return v;
7110
-}
7111
-
7112
-cson_object * cson_cgi_env_get_obj( cson_cgi_cx * cx, char which, char createIfNeeded )
7113
-{
7114
- return cson_value_get_object( cson_cgi_env_get_val( cx, which, createIfNeeded ) );
7115
-}
7116
-
7117
-/**
7118
- Sets a variable in one of the environment objects.
7119
-
7120
- env must be the conventional character representation
7121
- (case-insensitive) for on of the following environment objects:
7122
-
7123
- - g = GET
7124
- - p = POST
7125
- - e = ENV
7126
- - c = COOKIE
7127
- - u = USER
7128
-
7129
- On success 0 is returned and ownership of v is transfered to (or
7130
- shared with) the appropriate environment object. On error non-zero
7131
- is returned and ownership of v is not modified.
7132
-*/
7133
-static int cson_cgi_setenv_x( cson_cgi_cx * cx, char env, char const * key, cson_value * v )
7134
-{
7135
- if( ! key || !*key ) return cson_rc.ArgError;
7136
- else
7137
- {
7138
- cson_object * jo = cson_cgi_env_get_obj( cx, env, 1 );
7139
- return ( NULL == jo )
7140
- ? cson_rc.RangeError /* FIXME: expand the above code so we
7141
- can distinguish between invalid
7142
- env and allocation error. (Except that
7143
- there is no allocation on get_obj().*/
7144
- : cson_object_set( jo, key, v );
7145
- }
7146
-}
7147
-
7148
-int cson_cgi_setenv( cson_cgi_cx * cx, char const * key, cson_value * v )
7149
-{
7150
- return cson_cgi_setenv_x( cx, 'a', key, v );
7151
-}
7152
-
7153
-int cson_cgi_cookie_set( cson_cgi_cx * cx, char const * key, cson_value * v )
7154
-{
7155
-
7156
- if( ! key || !*key ) return cson_rc.ArgError;
7157
- else
7158
- {
7159
- cson_object * jo = cson_cgi_env_get_obj( cx, 'c', 1 );
7160
- return (NULL == jo)
7161
- ? cson_rc.AllocError
7162
- : cson_object_set( jo, key, v ? v : cson_value_null() );
7163
- }
7164
-}
7165
-
7166
-int cson_cgi_cookie_set2( cson_cgi_cx * cx,
7167
- char const * key, cson_value * v,
7168
- char const * domain, char const * path,
7169
- unsigned int expires, char secure, char httponly )
7170
-{
7171
- if( ! key || !*key ) return cson_rc.ArgError;
7172
- else
7173
- {
7174
- int rc;
7175
- cson_value * jv = cson_value_new_object();
7176
- cson_object * jo = cson_value_get_object(jv);
7177
- cson_value * x = NULL;
7178
- if( ! jo ) return cson_rc.AllocError;
7179
- if( ! v ) v = cson_value_null() /* reminder: does not allocate */;
7180
-
7181
-#define SET(KEY) if( 0 != (rc = cson_object_set( jo, KEY, x) ) ) { \
7182
- cson_value_free(x); \
7183
- cson_value_free( jv ); \
7184
- return rc; \
7185
- }
7186
-
7187
- if( NULL != domain )
7188
- {
7189
- x = cson_value_new_string( domain, strlen(domain) );
7190
- SET("domain");
7191
- }
7192
- if( NULL != path )
7193
- {
7194
- x = cson_value_new_string( path, strlen(path) );
7195
- SET("path");
7196
- }
7197
-
7198
- if( cson_value_is_null(v) )
7199
- {
7200
- x = cson_value_new_integer( 1 );
7201
- SET("expires");
7202
- }
7203
- else if( expires )
7204
- {
7205
- x = cson_value_new_integer( (cson_int_t) expires );
7206
- SET("expires");
7207
- }
7208
- if( secure )
7209
- {
7210
- x = cson_value_new_bool(secure);
7211
- SET("secure");
7212
- }
7213
- if( httponly )
7214
- {
7215
- x = cson_value_new_bool(httponly);
7216
- SET("httponly");
7217
- }
7218
-#undef SET
7219
- rc = cson_cgi_cookie_set( cx, key, jv );
7220
- if( 0 != rc )
7221
- {
7222
- cson_value_free( jv );
7223
- }
7224
- else
7225
- { /* set "value" last so that we can avoid tricky
7226
- ownership/lifetime problems in error cases.
7227
- */
7228
- if( 0 != (rc = cson_object_set( jo, "value", v) ) )
7229
- { /* remove the cookie. Note that this particular case
7230
- does not remove it from the HTTP client. In order to do that
7231
- we have to keep the existing path/domain/etc info.
7232
- */
7233
- cson_object * cookies = cson_cgi_env_get_obj( cx, 'c', 0 );
7234
- if( cookies )
7235
- {
7236
- cson_object_set( cookies, key, cson_value_null() )
7237
- /* Ignore error code, since we have no fallback
7238
- and cson_value_null() does not allocate.
7239
- Worst-case is that removing it fails, but when we
7240
- emit the cookie headers that cookie will be skipped
7241
- because it has no "value" field.
7242
- */
7243
- ;
7244
- }
7245
- }
7246
- }
7247
- return rc;
7248
- }
7249
-}
7250
-
7251
-cson_value * cson_cgi_getenv( cson_cgi_cx * cx, char const * fromWhere, char const * key )
7252
-{
7253
- cson_value * jv = NULL;
7254
- cson_object * map = NULL;
7255
- if( (NULL == fromWhere) || !*fromWhere ) fromWhere = CSON_CGI_GETENV_DEFAULT;
7256
- if( !key || !*key ) return NULL;
7257
- for( ; *fromWhere ; ++fromWhere )
7258
- {
7259
- map = cson_cgi_env_get_obj( cx, *fromWhere, 0 );
7260
- if( (NULL == map) && (('r'==*fromWhere)||('R'==*fromWhere)) )
7261
- {
7262
- jv = cson_cgi_getenv( cx, "gpc", key );
7263
- }
7264
- if( NULL != jv ) /* only in 'R' case */ break;
7265
- else if( NULL == map ) continue /* invalid character or NULL map */;
7266
- jv = cson_object_get( map, key );
7267
- if( NULL != jv ) break;
7268
- }
7269
- return jv;
7270
-}
7271
-
7272
-
7273
-int cson_cgi_response_header_add( cson_cgi_cx * cx, char const * key, cson_value * v )
7274
-{
7275
- int rc = 0;
7276
- if( !cx || ! key || !*key ) return cson_rc.ArgError;
7277
- rc = cson_cgi_init_env_map( cx, cson_cgi_keys.RESPONSE_HEADERS, &cx->response.headers );
7278
- if( 0 == rc )
7279
- {
7280
- assert( NULL != cx->response.headers.jobj );
7281
- rc = cson_object_set( cx->response.headers.jobj, key, v );
7282
- }
7283
- return rc;
7284
-}
7285
-
7286
-
7287
-char cson_cgi_is_jsonp(cson_cgi_cx * cx)
7288
-{
7289
- if( ! cx ) return 0;
7290
- else if( cx->misc.isJSONP < 0 )
7291
- { /* guess */
7292
- cx->misc.isJSONP = (NULL == cson_cgi_getenv( cx, "agp", CSON_CGI_KEY_JSONP ))
7293
- ? 0 : 1;
7294
- }
7295
- return cx->misc.isJSONP;
7296
-}
7297
-
7298
-void cson_cgi_enable_jsonp( cson_cgi_cx * cx, char b )
7299
-{
7300
- if( cx ) cx->misc.isJSONP = b ? 1 : 0;
7301
-}
7302
-
7303
-char const * cson_cgi_guess_content_type(cson_cgi_cx * cx)
7304
-{
7305
- char const * cset;
7306
- char doUtf8;
7307
- cset = getenv("HTTP_ACCEPT_CHARSET");
7308
- doUtf8 = ((NULL == cset) || (NULL!=strstr("utf-8",cset)))
7309
- ? 1 : 0;
7310
- if( cson_cgi_is_jsonp(cx) )
7311
- {
7312
- return doUtf8
7313
- ? "application/javascript; charset=utf-8"
7314
- : "application/javascript";
7315
- }
7316
- else
7317
- {
7318
- /*
7319
- Content-type
7320
-
7321
- If the browser does not sent an ACCEPT for application/json
7322
- then we fall back to text/plain.
7323
- */
7324
- char const * cstr;
7325
- cstr = getenv("HTTP_ACCEPT");
7326
- if( NULL == cstr )
7327
- {
7328
- return doUtf8
7329
- ? "application/json; charset=utf-8"
7330
- : "application/json";
7331
- }
7332
- else
7333
- {
7334
- if( strstr( cstr, "application/json" )
7335
- || strstr( cstr, "*/*" ) )
7336
- {
7337
- return doUtf8
7338
- ? "application/json; charset=utf-8"
7339
- : "application/json";
7340
- }
7341
- else
7342
- {
7343
- return "text/plain";
7344
- }
7345
- }
7346
- }
7347
-}
7348
-
7349
-
7350
-/**
7351
- URL-encodes src to dest and NUL-terminates it. dest must be at
7352
- least *destLen bytes long. Upon a successful return, *destLen
7353
- will be modified to hold the new string's length.
7354
-
7355
- Returns 0 on success. On error dest might be partially populated.
7356
-
7357
- Returns cson_rc.RangeError if dest is not long enough to hold
7358
- the conversion and a terminating NUL.
7359
-*/
7360
-static int cson_cgi_urlencode( char const * src, char * dest_, size_t * destLen )
7361
-{
7362
-#define needs_escape \
7363
- ( (ch >= 32 && ch <=47) \
7364
- || ( ch>=58 && ch<=64) \
7365
- || ( ch>=91 && ch<=96) \
7366
- || ( ch>=123 && ch<=126) \
7367
- || ( ch<32 || ch>=127) \
7368
- )
7369
- char const * pos = src;
7370
- char ch;
7371
- size_t dpos = 0;
7372
- char * dest = dest_;
7373
- static char const * hex = "0123456789ABCDEF";
7374
- if( ! dest || !destLen ) return cson_rc.RangeError;
7375
- for( ; pos && *pos; ++pos )
7376
- {
7377
- ch = *pos;
7378
- if( ! needs_escape )
7379
- {
7380
- if( ++dpos >= *destLen ) return cson_rc.RangeError;
7381
- *(dest++) = ch;
7382
- continue;
7383
- }
7384
- else
7385
- {
7386
- if( (dpos+=3) >= *destLen ) return cson_rc.RangeError;
7387
- *(dest++) = '%';
7388
- *(dest++) = hex[((ch>>4)&0xf)];
7389
- *(dest++) = hex[(ch&0xf)];
7390
- }
7391
- }
7392
- if( ++dpos >= *destLen ) return cson_rc.RangeError;
7393
- *dest = 0;
7394
- *destLen = dest - dest_;
7395
- return 0;
7396
-#undef needs_escape
7397
-}
7398
-
7399
-/**
7400
- If orig is one of the types (string,double,bool,undef,null) then
7401
- a pointer to its string representation is returned, else NULL
7402
- is returned.
7403
-
7404
- For non-string types, dest must be at least destLen bytes of memory, and
7405
- if destLen is not long enough to hold the string form then NULL is returned.
7406
-
7407
- On success a pointer to a string is returned. It will be one of:
7408
-
7409
- - if orig is-a string then it's underlying string.
7410
-
7411
- - for (double,integer,bool,undef,null), dest will be returned. The encoded
7412
- form is decimal for (double,integer), the number 0 or 1 for bool, and the
7413
- number 0 for (undef,null).
7414
-
7415
- Ownership of dest is not modified by this call.
7416
-
7417
- The returned value is valid until either orig or dest are modified.
7418
-
7419
- On error dest is not modified. Dest is also not modified if orig
7420
- is-a string, as its own string bytes are returned instead.
7421
-*/
7422
-static char const * cson_cgi_pod_to_string( cson_value const * orig,
7423
- char * dest, unsigned int destLen )
7424
-{
7425
- if( ! orig || !dest || !destLen ) return NULL;
7426
- else
7427
- {/* FIXME? use cson's output support for the numeric types. i
7428
- _think_ those bits might not be in the public API, though.
7429
- We could use it for serializing objects/arrays, in any case.
7430
- */
7431
- enum { NumBufSize = 80 };
7432
- if( cson_value_is_string(orig) )
7433
- {
7434
- cson_string const * jstr = cson_value_get_string(orig);
7435
- assert( NULL != jstr );
7436
- return cson_string_cstr( jstr );
7437
- }
7438
- else if( cson_value_is_integer(orig) )
7439
- {
7440
- char tmp[NumBufSize] = {0};
7441
- int const sc = sprintf( tmp, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig));
7442
- if( sc <= 0 ) return NULL;
7443
- else if( (unsigned int)sc >= destLen ) return NULL;
7444
- else
7445
- {
7446
- strcpy( dest, tmp );
7447
- return dest;
7448
- }
7449
- }
7450
- else if( cson_value_is_double(orig) )
7451
- {
7452
- char tmp[NumBufSize] = {0};
7453
- int const sc = sprintf( tmp, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(orig));
7454
- if( sc <= 0 ) return NULL;
7455
- else if( (unsigned int)sc >= destLen ) return NULL;
7456
- else
7457
- {
7458
- strcpy( dest, tmp );
7459
- if(1)
7460
- { /* Strip trailing zeroes... */
7461
- unsigned int urc = strlen(dest);
7462
- char * pos = dest + urc - 1;
7463
- for( ; ('0' == *pos) && urc && (*(pos-1) != '.'); --pos, --urc )
7464
- {
7465
- *pos = 0;
7466
- }
7467
- assert(urc && *pos);
7468
- }
7469
- return dest;
7470
- }
7471
- }
7472
- else if( cson_value_is_bool( orig ) )
7473
- {
7474
- char const bv = cson_value_get_bool(orig);
7475
- if( destLen < 2 ) return NULL;
7476
- *dest = bv ? '1' : '0';
7477
- *(dest+1) = 0;
7478
- return dest;
7479
- }
7480
- else if( cson_value_is_null( orig ) || cson_value_is_undef( orig ) )
7481
- {
7482
- if( destLen < 2 ) return NULL;
7483
- *dest = '0';
7484
- *(dest+1) = 0;
7485
- return dest;
7486
- }
7487
- else
7488
- {
7489
- return NULL;
7490
- }
7491
- }
7492
-}
7493
-
7494
-
7495
-/**
7496
- Writes an RFC822 timestamp string to dest, which must be at least destLen bytes long.
7497
- On success returns dest, else NULL. destLen must be at least 31.
7498
-*/
7499
-static char * cson_cgi_rfc822_timedate( time_t now, char * dest, unsigned int destLen )
7500
-{
7501
- static const char * dayNames[] =
7502
- {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
7503
- 0 };
7504
- static const char * monthNames[] =
7505
- {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
7506
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
7507
- 0};
7508
-
7509
- struct tm * t = (dest && (destLen>30)) ? gmtime(&now) : NULL;
7510
- if( ! t || (destLen<31) ) return NULL;
7511
- else
7512
- {
7513
- int const rc = sprintf( dest,
7514
- "%s, %d %s %02d %02d:%02d:%02d GMT",
7515
- dayNames[t->tm_wday], t->tm_mday,
7516
- monthNames[t->tm_mon],
7517
- t->tm_year+1900, t->tm_hour,
7518
- t->tm_min, t->tm_sec
7519
- );
7520
- assert( (rc>0) && ((unsigned int)rc) < destLen );
7521
- return dest;
7522
- }
7523
-}
7524
-
7525
-/**
7526
- Outputs the cookie-specific HTTP headers.
7527
-
7528
- Returns 0 on success.
7529
-*/
7530
-static int cson_cgi_response_output_cookies(cson_cgi_cx * cx)
7531
-{
7532
- cson_kvp * kvp = NULL;
7533
- cson_object * jo = NULL;
7534
- cson_object_iterator iter = cson_object_iterator_empty;
7535
- assert(cx);
7536
- jo = cx->request.cookie.jobj;
7537
- if( ! jo ) return 0;
7538
- else
7539
- {
7540
- enum { CookieBufSize = 1024 * 8,
7541
- ValBufSize = 1024 * 4,
7542
- TSBufSize = 32
7543
- };
7544
- char cookieBuf[CookieBufSize] = {0} /* buffer for whole cookie string */;
7545
- char valBuf[ValBufSize] = {0} /* buffer for value encoding */;
7546
- char urlBuf[ValBufSize] = {0} /* buffer for urlencoding */;
7547
- char tsBuf[TSBufSize] = {0} /* buffer for expiry timestamp */;
7548
- int rc = cson_object_iter_init( jo, &iter );
7549
- assert( CookieBufSize > ValBufSize );
7550
- if( 0 != rc ) return rc;
7551
- while( (kvp = cson_object_iter_next(&iter)) )
7552
- {
7553
- cson_string const * key = cson_kvp_key(kvp);
7554
- cson_value const * val = cson_kvp_value(kvp);
7555
- if( cson_value_is_null(val) )
7556
- {
7557
- cson_cgi_printf(cx,"Set-Cookie: %s=null; expires=Thu, 01-Jan-1970 00:00:01 GMT\r\n", cson_string_cstr(key));
7558
- continue;
7559
- }
7560
- if( cson_value_is_object(val) )
7561
- {
7562
- /*
7563
- Accept in Object in the form:
7564
-
7565
- {
7566
- value: VALUE,
7567
- domain: string,
7568
- path: string,
7569
- secure: bool,
7570
- httponly: bool,
7571
- expires: integer
7572
- }
7573
- */
7574
- cson_object const * obj = cson_value_get_object( val );
7575
- cson_value const * cv = cson_object_get( obj, "value" );
7576
- char const * valstr = NULL;
7577
- char const isNull = !cv || cson_value_is_null( cv );
7578
- if( isNull )
7579
- {
7580
- cson_cgi_printf(cx, "Set-Cookie: %s=0", cson_string_cstr(key));
7581
- }
7582
- else
7583
- {
7584
- /* FIXME: streamify urlencode so we can get around fixed buffer size. */
7585
- valstr = cson_cgi_pod_to_string( cv, valBuf, ValBufSize );
7586
- if( ! valstr ) continue;
7587
- else
7588
- {
7589
- size_t bSize = ValBufSize;
7590
- memset( urlBuf, 0, ValBufSize );
7591
- if( 0 != cson_cgi_urlencode( valstr, urlBuf, &bSize ) )
7592
- {
7593
- /* buffer is too small. Skip it. */
7594
- continue;
7595
- }
7596
- assert( bSize <= ValBufSize );
7597
- cson_cgi_printf(cx, "Set-Cookie: %s=%s", cson_string_cstr(key), urlBuf);
7598
- }
7599
- }
7600
-
7601
-#define DOPART(KEY,KEY2) cv = cson_object_get( obj, KEY ); \
7602
- if( cv ) { \
7603
- valstr = cson_cgi_pod_to_string( cv, valBuf, ValBufSize ); \
7604
- if( valstr ) { \
7605
- cson_cgi_printf( cx, "; "KEY2"=%s", valstr ); \
7606
- } } (void)0
7607
- DOPART("domain","Domain");
7608
- DOPART("path","Path");
7609
-#undef DOPART
7610
-
7611
- cv = cson_object_get( obj, "expires" );
7612
- if( cv || isNull )
7613
- {
7614
- cson_int_t const intVal = isNull ? 1 : cson_value_get_integer(cv);
7615
- if( intVal )
7616
- {
7617
- valstr = cson_cgi_rfc822_timedate( (time_t)intVal, tsBuf, TSBufSize );
7618
- if( valstr )
7619
- {
7620
- cson_cgi_printf( cx, "; Expires=%s", valstr );
7621
- }
7622
- }
7623
-#if 0
7624
- else if( cson_value_is_string(cv) )
7625
- {
7626
- /* TODO?: assume it's already propery formatted. */
7627
- }
7628
- else
7629
- {
7630
- /* skip it.*/
7631
- }
7632
-#endif
7633
- }
7634
- cv = cson_object_get( obj, "secure" );
7635
- if( cson_value_get_bool(cv) )
7636
- {
7637
- cson_cgi_printf( cx, "; Secure" );
7638
- }
7639
-
7640
- cv = cson_object_get( obj, "httponly" );
7641
- if( cson_value_get_bool(cv) )
7642
- {
7643
- cson_cgi_printf( cx, "; HttpOnly" );
7644
- }
7645
- cson_cgi_puts(cx, "\r");
7646
- }
7647
- else
7648
- {
7649
- char const * valstr;
7650
- memset( valBuf, 0, ValBufSize );
7651
- valstr = cson_cgi_pod_to_string( val, valBuf, ValBufSize );
7652
- if( ! valstr ) continue;
7653
- else
7654
- {
7655
- size_t bSize = CookieBufSize;
7656
- memset( cookieBuf, 0, CookieBufSize );
7657
- rc = cson_cgi_urlencode( valstr, cookieBuf, &bSize );
7658
- if( 0 != rc )
7659
- {
7660
- /* too beaucoup. skip it */
7661
- continue;
7662
- }
7663
- assert( bSize < CookieBufSize );
7664
- cson_cgi_printf(cx,"Set-Cookie: %s=%s\r\n", cson_string_cstr(key), cookieBuf);
7665
- }
7666
- }
7667
- }
7668
- return 0;
7669
- }
7670
-
7671
-}
7672
-int cson_cgi_response_output_headers(cson_cgi_cx * cx)
7673
-{
7674
- enum { BufSize = 64 };
7675
- cson_object * jo = NULL;
7676
- int rc;
7677
- rc = cson_cgi_printf(cx, "Content-type: %s\r\n", cson_cgi_guess_content_type(cx) );
7678
- if( rc <= 0 ) return rc;
7679
- rc = cson_cgi_puts(cx, "Status: 200 OK\r");
7680
- if( rc <= 0 ) return rc;
7681
- jo = cx->response.headers.jobj;
7682
- if( jo )
7683
- {
7684
- char buf[BufSize] = {0};
7685
- cson_object_iterator iter = cson_object_iterator_empty;
7686
- cson_kvp * kvp;
7687
- cson_string const * key;
7688
- cson_value const * val;
7689
- char const * valcstr;
7690
- rc = cson_object_iter_init( jo, &iter );
7691
- if( 0 != rc ) return rc;
7692
- while( (kvp = cson_object_iter_next(&iter)) )
7693
- {
7694
- key = cson_kvp_key(kvp);
7695
- val = cson_kvp_value(kvp);
7696
- valcstr = cson_cgi_pod_to_string( val, buf, BufSize );
7697
- if( ! valcstr ) continue;
7698
- assert( NULL != key );
7699
- assert( NULL != val );
7700
- cson_cgi_printf(cx, "%s: %s\r\n",
7701
- cson_string_cstr(key),
7702
- valcstr ? valcstr : "");
7703
- }
7704
- }
7705
- rc = cson_cgi_response_output_cookies(cx);
7706
- return rc;
7707
-}
7708
-
7709
-int cson_cgi_response_output_root(cson_cgi_cx * cx)
7710
-{
7711
- return ( !cx || !cx->response.root )
7712
- ? cson_rc.ArgError
7713
- : cson_output_FILE( cx->response.root, cx->opt.outStream, &cx->opt.outOpt );
7714
-}
7715
-
7716
-int cson_cgi_response_output_all(cson_cgi_cx * cx)
7717
-{
7718
- int rc = 0;
7719
- char isJP = 0;
7720
- char doHeaders = cx->opt.httpHeadersMode;
7721
- if( NULL == cx->response.root )
7722
- {
7723
- return cson_rc.ArgError;
7724
- }
7725
- isJP = cson_cgi_is_jsonp(cx);
7726
- if( doHeaders < 0 )
7727
- {
7728
- if( NULL!=getenv("GATEWAY_INTERFACE") )
7729
- {
7730
- doHeaders = 1;
7731
- }
7732
- }
7733
- if( doHeaders > 0 )
7734
- {
7735
- rc = cson_cgi_response_output_headers(cx);
7736
- if( 0 == rc )
7737
- {
7738
- cson_cgi_puts(cx,"\r")/*yes, putS, not putCHAR!*/;
7739
- }
7740
- else return rc;
7741
- }
7742
- if( isJP )
7743
- {
7744
- cson_cgi_printf(cx,"%s(", "FIXME_JSONP_CALLBACK_NAME" );
7745
- }
7746
- rc = cson_cgi_response_output_root(cx);
7747
- if( 0 == rc )
7748
- {
7749
- if( isJP )
7750
- {
7751
- cson_cgi_putchar(cx,')');
7752
- }
7753
- cson_cgi_putchar(cx,'\n');
7754
- fflush( cx->opt.outStream );
7755
- }
7756
- return rc;
7757
-}
7758
-
7759
-/**
7760
- Parses inp as a delimited list, separated by the given
7761
- separator character. Each item in the list is treated
7762
- as a key/value pair in the form KEY=VALUE, and inserted
7763
- into the target cson_object (which must not be NULL).
7764
-
7765
- This is intended for parsing HTTP GET-style parameter lists.
7766
-
7767
- If doUrlDecode is true (non-zero) then the VALUE part of the
7768
- key/value pair gets url-decoded before insertion. (FIXME? Also
7769
- decode the keys?)
7770
-
7771
- If firstOneWins is non-0 then if a given key in the parameters is
7772
- duplicated, entries after the first are ignored. If it is 0 then
7773
- the "last one wins." This is basically a workaround for when we
7774
- have multiple session ID cookies hanging around :/.
7775
-
7776
- On success it returns 0.
7777
-
7778
- If a given key contains the string "[]", that part is stripped and
7779
- the entry is treated like an array element. e.g. a query string of
7780
- "a[]=3&a[]=7" would result in an array property named "a" with the
7781
- (string) entries ("3", "7").
7782
-
7783
-*/
7784
-static int cson_cgi_parse_param_list( cson_cgi_cx * cx,
7785
- cson_object * tgt,
7786
- char const * inp,
7787
- char separator,
7788
- char doUrlDecode,
7789
- char firstOneWins)
7790
-{
7791
- if( ! tgt || !separator ) return cson_rc.ArgError;
7792
- else if( !inp || !*inp ) return 0;
7793
- else
7794
- {
7795
- char const * head = inp;
7796
- char const * tail = NULL;
7797
- char * out = NULL;
7798
- unsigned int inLen = strlen( inp );
7799
- unsigned int valLen;
7800
- cson_value * jval = NULL;
7801
- cson_value * listV = NULL;
7802
- cson_array * list = NULL;
7803
- int rc = cson_buffer_reserve( &cx->tmpBuf, inLen+1 );
7804
- if( 0 != rc ) return rc;
7805
- while( cson_cgi_next_token( &head, separator, &tail ) )
7806
- {
7807
- char const * key = head;
7808
- char * value = NULL;
7809
- rc = 0;
7810
- if( head == tail ) break;
7811
- out = (char *)cx->tmpBuf.mem;
7812
- memset( cx->tmpBuf.mem, 0, cx->tmpBuf.capacity );
7813
- for( ; (key<tail) && *key && isspace(*key); ++key )
7814
- {
7815
- /* strip leading spaces in the key name
7816
- (happens in cookie values). */
7817
- }
7818
- if( key==tail ) break;
7819
- else if( '='==*key )
7820
- {
7821
- /* all-space key. Just skip it. */
7822
- goto next_iter;
7823
- }
7824
- /* Write the key part to the buffer... */
7825
- for( ; (key<tail) && *key && ('='!=*key); ++key ) {
7826
- *(out++) = *key;
7827
- }
7828
- *(out++) = 0;
7829
- if( '=' == *key )
7830
- {
7831
- ++key;
7832
- }
7833
- value = out;
7834
- valLen = 0;
7835
- /* Write the value part to the buffer... */
7836
- for( ; (key<tail) && *key; ++key, ++valLen ) {
7837
- *(out++) = *key;
7838
- }
7839
- key = (char const *)cx->tmpBuf.mem;
7840
- if( firstOneWins && (NULL != cson_object_get( tgt, key )) )
7841
- {
7842
- goto next_iter;
7843
- }
7844
- if( doUrlDecode && valLen )
7845
- {
7846
- cson_cgi_urldecode_inline( value );
7847
- }
7848
- /*MARKER("key=[%s], valLen=%u, value=[%s]\n", key, valLen, value );*/
7849
- jval = cson_value_new_string( value, valLen );
7850
- if( NULL == jval )
7851
- {
7852
- rc = cson_rc.AllocError;
7853
- goto the_end;
7854
- }
7855
- if( NULL != (out = strstr(key,"[]")) )
7856
- { /* Treat key as an array entry, like PHP does... */
7857
- cson_value * freeThisOnErr = NULL;
7858
- *out = 0;
7859
- list = NULL;
7860
- listV = cson_object_get( tgt, key );
7861
- if( listV )
7862
- {
7863
- if( ! cson_value_is_array( listV ) )
7864
- {
7865
- /* skip it to avoid hosing a different entry. */
7866
- cson_value_free( jval );
7867
- jval = NULL;
7868
- goto next_iter;
7869
- }
7870
- }
7871
- else
7872
- { /* create a new array to hold the value */
7873
- listV = cson_value_new_array();
7874
- if( ! listV )
7875
- {
7876
- cson_value_free( jval );
7877
- rc = cson_rc.AllocError;
7878
- goto the_end;
7879
- }
7880
- rc = cson_object_set( tgt, key, listV );
7881
- if( 0 != rc )
7882
- {
7883
- cson_value_free( listV );
7884
- cson_value_free( jval );
7885
- goto the_end;
7886
- }
7887
- freeThisOnErr = listV;
7888
- }
7889
- list = cson_value_get_array( listV );
7890
- assert( NULL != list );
7891
- rc = cson_array_append( list, jval );
7892
- if( 0 != rc )
7893
- {
7894
- cson_value_free( jval );
7895
- cson_value_free( freeThisOnErr );
7896
- goto the_end;
7897
- }
7898
- }
7899
- else
7900
- {
7901
- rc = cson_object_set( tgt, key, jval );
7902
- if( 0 != rc )
7903
- {
7904
- cson_value_free( jval );
7905
- goto the_end;
7906
- }
7907
- }
7908
- next_iter:
7909
- head = tail;
7910
- tail = NULL;
7911
- }
7912
- the_end:
7913
- cson_buffer_reserve( &cx->tmpBuf, 0 );
7914
- return rc;
7915
- }
7916
-}
7917
-
7918
-
7919
-/**
7920
- Parses key/value pairs from a QUERY_STRING-formatted
7921
- string.
7922
-
7923
- Returns 0 on success. The "most likely" error condition, in terms
7924
- of potential code paths, is is an allocation error.
7925
-
7926
- TODO: if the key part of any entry ends with "[]", treat it as an
7927
- array entry, like PHP does.
7928
-*/
7929
-static int cson_cgi_parse_query_string( cson_cgi_cx * cx, char const * qstr )
7930
-{
7931
- cson_object * env = NULL;
7932
- if( !qstr || !*qstr ) return 0;
7933
- assert(cx);
7934
- env = cson_cgi_env_get_obj( cx, 'g', 1 );
7935
- if( NULL == env ) return cson_rc.AllocError /* guess! */;
7936
- return cson_cgi_parse_param_list( cx, env, qstr, '&', 1, 0 );
7937
-}
7938
-
7939
-#if CSON_CGI_ENABLE_POST_FORM_URLENCODED
7940
-static int cson_cgi_parse_post_urlencoded( cson_cgi_cx * cx, char const * qstr )
7941
-{
7942
- cson_object * env = NULL;
7943
- if( !qstr || !*qstr ) return 0;
7944
- assert(cx);
7945
- env = cson_cgi_env_get_obj( cx, 'p', 1 );
7946
- if( NULL == env ) return cson_rc.AllocError /* guess! */;
7947
- return cson_cgi_parse_param_list( cx, env, qstr, '&', 1, 0 );
7948
-}
7949
-#endif
7950
-
7951
-/**
7952
- Like cson_cgi_parse_query_string(), but expects qstr to be in COOKIE
7953
- format.
7954
-*/
7955
-static int cson_cgi_parse_cookies( cson_cgi_cx * cx, char const * qstr )
7956
-{
7957
- cson_object * env = NULL;
7958
- if( !qstr || !*qstr ) return 0;
7959
- assert(cx);
7960
- env = cson_cgi_env_get_obj(cx, 'c', 1 );
7961
- if( NULL == env ) return cson_rc.AllocError /* guess! */;
7962
- return cson_cgi_parse_param_list( cx, env, qstr, ';', 1, 1 );
7963
-}
7964
-
7965
-
7966
-/**
7967
- Initializes cx->argv.jval and cx->argv.jarr, adds them to the
7968
- garbage collector, then copies argv to cx->argv.jarr as an
7969
- array of JSON strings.
7970
-
7971
- Returns 0 on success.
7972
-
7973
- Results are undefined if argv is not a properly initialized array
7974
- of NUL-terminated strings with at least argc entries.
7975
-
7976
- If argc is 0 or less then cx->argv is still initialized but has
7977
- a length of 0.
7978
-
7979
- After the first call, further arguments are appended to the current
7980
- list.
7981
-*/
7982
-static int cson_cgi_init_argv( cson_cgi_cx * cx, int argc, char const * const * argv )
7983
-{
7984
- int rc = 0;
7985
- int i;
7986
- assert( NULL != cx->gc.jobj );
7987
- if( cx->argv.jval == NULL )
7988
- {
7989
- cson_value * v = cson_value_new_array();
7990
- if( NULL == v ) return cson_rc.AllocError;
7991
- rc = cson_cgi_gc_add( cx, cson_cgi_keys.ENV_ARGV, v, 1 );
7992
- if( 0 != rc )
7993
- {
7994
- /* reminder: v was freed by cson_cgi_gc_add() */
7995
- return rc;
7996
- }
7997
- cx->argv.jval = v;
7998
- cx->argv.jarr = cson_value_get_array( v );
7999
- assert( NULL != cx->argv.jarr );
8000
- }
8001
- for( i = 0; i < argc; ++i )
8002
- {
8003
- char const * arg = argv[i];
8004
- cson_value * vstr = cson_value_new_string( arg ? arg : "",
8005
- arg ? strlen(arg) : 0 );
8006
- if( NULL == vstr ) return cson_rc.AllocError;
8007
- rc = cson_array_append( cx->argv.jarr, vstr );
8008
- if( 0 != rc )
8009
- {
8010
- cson_value_free( vstr );
8011
- break;
8012
- }
8013
- }
8014
- return rc;
8015
-}
8016
-
8017
-typedef struct CgiPostReadState_ {
8018
- FILE * fh;
8019
- unsigned int len;
8020
- unsigned int pos;
8021
-} CgiPostReadState;
8022
-
8023
-static int cson_data_source_FILE_n( void * state, void * dest, unsigned int * n )
8024
-{
8025
- if( ! state || !dest || !n ) return cson_rc.ArgError;
8026
- else
8027
- {
8028
- CgiPostReadState * st = (CgiPostReadState *)state;
8029
- if( st->pos >= st->len )
8030
- {
8031
- *n = 0;
8032
- return 0;
8033
- }
8034
- else if( !*n || ((st->pos + *n) > st->len) ) return cson_rc.RangeError;
8035
- else
8036
- {
8037
- unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh );
8038
- if( ! rsz )
8039
- {
8040
- *n = rsz;
8041
- return feof(st->fh) ? 0 : cson_rc.IOError;
8042
- }
8043
- else
8044
- {
8045
- *n = rsz;
8046
- st->pos += *n;
8047
- return 0;
8048
- }
8049
- }
8050
- }
8051
-}
8052
-
8053
-
8054
-static int cson_cgi_parse_POST_JSON(cson_cgi_cx * cx, FILE * src, unsigned int contentLen)
8055
-{
8056
- cson_value * jv = NULL;
8057
- int rc = 0;
8058
- CgiPostReadState state;
8059
- cson_parse_info pinfo = cson_parse_info_empty;
8060
- assert( 0 != contentLen );
8061
- assert( NULL == cx->request.post.jval );
8062
- state.fh = src;
8063
- state.len = contentLen;
8064
- state.pos = 0;
8065
- rc = cson_parse( &jv, cson_data_source_FILE_n, &state, NULL, &pinfo );
8066
- if( rc )
8067
- {
8068
-#if 0
8069
- fprintf(stderr, "%s: Parsing POST as JSON failed: code=%d (%s) line=%u, col=%u\n",
8070
- __FILE__, rc, cson_rc_string(rc), pinfo.line, pinfo.col );
8071
-#endif
8072
- return rc;
8073
- }
8074
- rc = cson_cgi_gc_add( cx, cson_cgi_keys.ENV_POST, jv, 1 );
8075
- if( 0 == rc )
8076
- {
8077
- cx->request.post.jval = jv;
8078
- cx->request.post.jobj = cson_value_get_object( jv );
8079
- assert( cx->request.post.jobj && "FIXME: also support an Array as POST data node." );
8080
- }
8081
- return rc;
8082
-}
8083
-
8084
-static int cson_cgi_init_POST(cson_cgi_cx * cx)
8085
-{
8086
- if( ! cx || !cx->opt.inStream ) return cson_rc.ArgError;
8087
- else
8088
- {
8089
- FILE * src = cx->opt.inStream;
8090
- char const * ctype = cson_string_cstr( cson_value_get_string( cson_cgi_getenv( cx, "e", "CONTENT_TYPE" ) ) );
8091
- if( NULL == ctype ) return 0;
8092
- else
8093
- {
8094
- char const * clen = cson_string_cstr( cson_value_get_string( cson_cgi_getenv( cx, "e", "CONTENT_LENGTH" ) ) );
8095
- if( NULL == clen ) return cson_rc.ArgError;
8096
- else
8097
- {
8098
- char * endpt = NULL;
8099
- long len = strtol( clen, &endpt, 10 );
8100
- if( (endpt && *endpt) || (len<=0) ) return cson_rc.RangeError;
8101
-#if CSON_CGI_ENABLE_POST_FORM_URLENCODED
8102
- else if( 0 == strncmp(ctype,"application/x-www-form-urlencoded",33) )
8103
- {
8104
- cson_buffer buf = cson_buffer_empty;
8105
- int rc = cson_buffer_fill_from( &buf, cson_data_source_FILE, src );
8106
- if( rc )
8107
- {
8108
- goto end_clean;
8109
- return rc;
8110
- }
8111
- if( buf.mem && buf.used )
8112
- {
8113
-#if 1
8114
- if( strlen((char const *)buf.mem)
8115
- != buf.used )
8116
- {
8117
- /* assume bad/malicious input. */
8118
- rc = cson_rc.RangeError;
8119
- goto end_clean;
8120
- }
8121
-#endif
8122
- rc = cson_cgi_parse_post_urlencoded( cx, (char const *)buf.mem );
8123
- }
8124
- end_clean:
8125
- cson_buffer_reserve( &buf, 0 );
8126
- return rc;
8127
- }
8128
-#endif
8129
- else if( (0 == strncmp(ctype,"application/json",16))
8130
- || (0 == strncmp(ctype,"text/plain",10))
8131
- || (0 == strncmp(ctype,"application/javascript",22))
8132
- )
8133
- {
8134
- return cson_cgi_parse_POST_JSON(cx, src, len);
8135
- }
8136
- else
8137
- {
8138
- return cson_rc.TypeError;
8139
- }
8140
- }
8141
- }
8142
- }
8143
-}
8144
-
8145
-static int cson_cgi_init_config( cson_cgi_cx * cx, char const * fname )
8146
-{
8147
- int rc;
8148
- cson_value * root = NULL;
8149
- rc = cson_parse_filename( &root, fname, NULL, NULL );
8150
- if( 0 == rc )
8151
- {
8152
- assert( NULL != root );
8153
- if( ! cson_value_is_object(root) )
8154
- {
8155
- cson_value_free( root );
8156
- rc = cson_rc.TypeError;
8157
- }
8158
- else
8159
- {
8160
- rc = cson_cgi_gc_add( cx,cson_cgi_keys.ENV_CONFIG, root, 1 );
8161
- if( 0 == rc )
8162
- {
8163
- cx->config.jval = root;
8164
- cx->config.jobj = cson_value_get_object( root );
8165
- assert( NULL != cx->config.jobj );
8166
- }
8167
- }
8168
- }
8169
- return rc;
8170
-}
8171
-
8172
-static char * cson_cgi_strdup( char const * src )
8173
-{
8174
- size_t const n = src ? strlen(src) : 0;
8175
- char * rc = src ? (char *)malloc(n+1) : NULL;
8176
- if( ! rc ) return NULL;
8177
- memcpy( rc, src, n );
8178
- rc[n] = 0;
8179
- return rc;
8180
-}
8181
-
8182
-/**
8183
- Writes a 36-byte (plus one NUL byte) UUID value to dest. dest
8184
- must be at least 37 bytes long. If dest is NULL this function
8185
- has no side effects.
8186
-
8187
- Not thread-safe.
8188
-*/
8189
-void cson_cgi_generate_uuid( cson_cgi_cx * cx, char * dest )
8190
-{
8191
- static whuuid_rng rng = {
8192
- NULL/*rand*/,
8193
- NULL/*cleanup*/,
8194
- NULL/*impl*/
8195
-#if WHUUID_CONFIG_KEEP_METRICS
8196
- ,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
8197
-#endif
8198
- };
8199
- whuuid_t u = whuuid_t_empty;
8200
- if( NULL == dest ) return;
8201
- else if( (NULL==rng.rand) && (NULL != RNG_FILENAME) )
8202
- { /* try to open rng file... */
8203
- /* FIXME: we're missing a cleanup handler for the RNG_FILENAME case. */
8204
- FILE * f = fopen(RNG_FILENAME, "rb");
8205
- if( NULL != f )
8206
- {
8207
- rng = whuuid_rng_FILE;
8208
- rng.impl = f;
8209
- }
8210
- }
8211
- if( NULL == rng.rand )
8212
- { /* fall back to LC rng */
8213
- extern char ** environ;
8214
- void * addr;
8215
- unsigned long seed;
8216
- rng = whuuid_rng_lcrng;
8217
- addr = malloc(
8218
- (((unsigned long)environ) % 13) + 9
8219
- );
8220
- free(addr) /* but keep the address as a seed value */;
8221
- seed = (unsigned long)addr * (unsigned long)time(NULL);
8222
- rng.impl = (void *)seed;
8223
- }
8224
- whuuid_fill_rand( &u, &rng );
8225
- whuuid_to_string( &u, dest );
8226
-}
8227
-
8228
-char const * cson_cgi_session_id(cson_cgi_cx * cx)
8229
-{
8230
- return cx ? cx->session.id : NULL;
8231
-}
8232
-
8233
-
8234
-static int cson_cgi_init_session_mgr(cson_cgi_cx * cx)
8235
-{
8236
- /*
8237
- Check for this config structure:
8238
-
8239
- {
8240
- manager:"mgrID",
8241
- managers:{
8242
- mgrID:{
8243
- sessionDriver: "back-end-name" (e.g. "cpdo" or "file"),
8244
- ... back-end-specific options ...
8245
- },
8246
- otherManager: { ... }
8247
- }
8248
- */
8249
- cson_object const * conf = cson_cgi_env_get_obj(cx, 'f', 0 );
8250
- cson_string const * aString;
8251
- cson_value const * optV = NULL;
8252
- cson_object const * optObj = NULL;
8253
- if( NULL == conf ) return 0;
8254
- assert( cx && !cx->session.mgr );
8255
-
8256
- /* get "manager" part... */
8257
- aString = cson_value_get_string( cson_object_get_sub( conf, "session.manager", '.' ) );
8258
- if( NULL == aString ) return 0;
8259
-
8260
- /* Fetch that manager config ... */
8261
- optV = cson_object_get_sub( conf, "session.managers", '.' );
8262
- if( optV )
8263
- {
8264
- optV = cson_object_get( cson_value_get_object( optV ), cson_string_cstr( aString ) );
8265
- }
8266
- optObj = cson_value_get_object( optV );
8267
- if( ! optObj ) return 0;
8268
-
8269
- /* Get the "sessionDriver" part ... */
8270
- aString = cson_value_get_string( cson_object_get( optObj, "sessionDriver" ) );
8271
- if( NULL == aString ) return 0;
8272
-
8273
- return cson_sessmgr_load( cson_string_cstr(aString), &cx->session.mgr, optObj );
8274
-}
8275
-
8276
-
8277
-static char const * cson_cgi_get_session_key(cson_cgi_cx * cx)
8278
-{
8279
- cson_object const * conf = cson_cgi_env_get_obj( cx, 'f', 0 );
8280
- char const * sessKey = CSON_CGI_KEY_SESSION;
8281
- assert( NULL != cx );
8282
- if( conf )
8283
- {
8284
- cson_string const * k = cson_value_get_string( cson_object_get_sub( conf, "session.cookieName", '.' ) );
8285
- char const * ck = k ? cson_string_cstr(k) : NULL;
8286
- if( ck ) sessKey = ck;
8287
- }
8288
- return sessKey;
8289
-}
8290
-
8291
-static int cson_cgi_gen_session_id(cson_cgi_cx * cx)
8292
-{
8293
- char buf[37] = {0};
8294
- if( cx->session.id )
8295
- {
8296
- free( cx->session.id );
8297
- cx->session.id = NULL;
8298
- }
8299
- cson_cgi_generate_uuid( cx, buf );
8300
- cx->session.id = cson_cgi_strdup( buf );
8301
- return ( NULL == cx->session.id )
8302
- ? cson_rc.AllocError
8303
- : 0;
8304
-}
8305
-
8306
-static int cson_cgi_init_session( cson_cgi_cx * cx, char const * forceID )
8307
-{
8308
- char const * idstr;
8309
- char const * sessKey;
8310
- int rc = cson_cgi_init_session_mgr(cx);
8311
- if( 0 != rc ) return rc;
8312
- else if( NULL == cx->session.mgr ) return 0
8313
- /* treat as non-fatal error */;
8314
- sessKey = cson_cgi_get_session_key(cx);
8315
- assert( sessKey && *sessKey );
8316
- /* Try to get the session ID ... */
8317
- idstr = (forceID && *forceID)
8318
- ? forceID
8319
- : cson_string_cstr( cson_value_get_string( cson_cgi_getenv( cx, "cegp", sessKey ) ) );
8320
- if( NULL == idstr )
8321
- { /* Generate a session ID but defer creation of the session
8322
- object until the client does it. If they never use it,
8323
- we won't bother saving the session.
8324
- */
8325
- rc = cson_cgi_gen_session_id(cx);
8326
- if( 0 != rc ) return rc;
8327
- }
8328
- else
8329
- { /* try to load the session */
8330
- cson_value * sessV = NULL;
8331
- free( cx->session.id );
8332
- cx->session.id = cson_cgi_strdup( idstr );
8333
- if( ! cx->session.id ) return cson_rc.AllocError;
8334
- rc = cx->session.mgr->api->load( cx->session.mgr, &sessV,
8335
- cx->session.id );
8336
- if( (0 == rc) && sessV )
8337
- {
8338
- rc = cson_cgi_gc_add( cx, cson_cgi_keys.ENV_SESSION, sessV, 1 );
8339
- if( 0 != rc )
8340
- { /* almost certainly an alloc error */
8341
- return rc;
8342
- }
8343
- cx->session.env.jval = sessV;
8344
- cx->session.env.jobj = cson_value_get_object( sessV );
8345
- }
8346
- else
8347
- {
8348
- if( !forceID || !*forceID )
8349
- {
8350
- /* On load error, assume the session ID is
8351
- stale. Re-generate it to avoid potential future
8352
- collisions. This heuristic will cause us intermittent
8353
- grief when loading does not work for a second or three
8354
- due to network-related problems. Each time that
8355
- happens, the caller will lose his session.
8356
- */
8357
- rc = cson_cgi_gen_session_id(cx);
8358
- if( 0 != rc ) return rc;
8359
- }
8360
- }
8361
- }
8362
- assert( NULL != cx->session.id );
8363
- { /* make sure the session ID is set in the cookies and has an updated
8364
- expiry time... */
8365
- unsigned int expiry = 0;
8366
- cson_object const * conf;
8367
- cson_value * jstr = cson_value_new_string( cx->session.id,
8368
- strlen(cx->session.id) );
8369
- if( ! jstr ) return cson_rc.AllocError;
8370
- conf = cson_cgi_env_get_obj( cx, 'f', 0 );
8371
- if( conf )
8372
- {
8373
- expiry = cson_value_get_integer( cson_object_get_sub( conf, "session.cookieLifetimeMinutes", '.' ) );
8374
- if( expiry ) expiry *= 60 /* convert to seconds */;
8375
- }
8376
- if( ! expiry )
8377
- {
8378
- expiry = (60*60*24);
8379
- }
8380
- expiry += (unsigned int)time(NULL);
8381
-
8382
- rc = cson_cgi_cookie_set2( cx, sessKey, jstr,
8383
- NULL, NULL,
8384
- expiry,
8385
- 0/*FIXME: set 'secure' option in HTTPS mode.*/,
8386
- 0/*FIXME: make the httponly flag configurable*/ );
8387
- if( 0 != rc )
8388
- {
8389
- cson_value_free( jstr );
8390
- if( cson_rc.AllocError == rc ) return rc;
8391
- rc = 0 /* else treat as non-fatal */;
8392
- }
8393
- }
8394
- return 0;
8395
-}
8396
-
8397
-
8398
-
8399
-int cson_cgi_init(cson_cgi_cx * cx, int argc, char const * const * argv, cson_cgi_init_opt * opt )
8400
-{
8401
- int rc = 0;
8402
- static int hasInited = 0;
8403
- if( NULL == cx ) return cson_rc.ArgError;
8404
- else if( NULL != cx->gc.jval )
8405
- { /* we've already done this or object was mal-initialized... */
8406
- return cson_rc.ArgError;
8407
- }
8408
-
8409
- assert( NULL != CSON_CGI_GETENV_DEFAULT );
8410
-
8411
-#if CSON_CGI_USE_SIGNALS
8412
- {
8413
- /* FIXME: use sigaction() instead of signal() */
8414
- typedef void (*sighnd)(int);
8415
- sighnd oldSigPipe;
8416
- oldSigPipe = signal(SIGPIPE, SIG_IGN) /* to try avoid unclean termination if client disconnects. */;
8417
- if( SIG_ERR == oldSigPipe )
8418
- {
8419
- return cson_rc.UnknownError;
8420
- }
8421
- }
8422
-#endif
8423
-
8424
- if( ! hasInited )
8425
- {
8426
- hasInited = 1;
8427
- setlocale( LC_ALL, "C" )
8428
- /* supposedly important for underlying JSON parser.
8429
- FIXME: only do this init once!
8430
- */;
8431
- }
8432
-
8433
- cx->gc.jval = cson_value_new_object();
8434
- if( NULL == cx->gc.jval )
8435
- {
8436
- return cson_rc.AllocError;
8437
- }
8438
- cx->gc.jobj = cson_value_get_object( cx->gc.jval );
8439
- assert( NULL != cx->gc.jobj );
8440
-
8441
- if( opt )
8442
- {
8443
- cx->opt = *opt;
8444
- }
8445
- if( NULL == cx->opt.inStream ) cx->opt.inStream = stdin;
8446
- if( NULL == cx->opt.outStream ) cx->opt.outStream = stdout;
8447
- if( NULL == cx->opt.errStream ) cx->opt.errStream = stderr;
8448
-
8449
-#define CHECKRC if(rc) goto end
8450
- rc = cson_cgi_import_environ(cx);
8451
- CHECKRC;
8452
- rc = cson_cgi_init_argv( cx, argc, argv );
8453
- CHECKRC;
8454
- { /* read config file */
8455
- char const * conffile = cx->opt.configFile;
8456
- if( ! conffile )
8457
- {
8458
- cson_value const * v = cson_cgi_getenv( cx, "e", "CSON_CGI_CONFIG" );
8459
- if( v && cson_value_is_string(v) )
8460
- {
8461
- conffile = cson_string_cstr( cson_value_get_string( v ) );
8462
- }
8463
- }
8464
- if( conffile )
8465
- {
8466
- cson_cgi_init_config( cx, conffile )
8467
- /* Ignore error code.
8468
-
8469
- TODO:
8470
-
8471
- - use argv[0]+".json" as the default config file.
8472
- */
8473
- ;
8474
- }
8475
- }
8476
-
8477
- rc = cson_cgi_parse_query_string( cx, getenv("QUERY_STRING") );
8478
- CHECKRC;
8479
- rc = cson_cgi_parse_cookies( cx, getenv("HTTP_COOKIE") );
8480
- CHECKRC;
8481
- rc = cson_cgi_init_POST(cx);
8482
- if( cson_rc.AllocError == rc ) goto end;
8483
- else rc = 0
8484
- /* this can fail for several reasons which are non-fatal. */
8485
- ;
8486
-
8487
- if( (NULL == opt) )
8488
- {
8489
- /* TODO: read these values from cx->config, if available. */
8490
- cx->opt.outOpt.indentation = 1;
8491
- cx->opt.outOpt.addNewline = 1;
8492
- cx->opt.outOpt.addSpaceAfterColon = 1;
8493
- cx->opt.outOpt.indentSingleMemberValues = 1;
8494
- }
8495
-
8496
- rc = cson_cgi_init_session( cx, opt ? opt->sessionID : NULL )
8497
- /* ignore non-OOM error codes. Not fatal. */;
8498
- if( cson_rc.AllocError == rc ) goto end;
8499
- else rc = 0;
8500
-
8501
- /*
8502
- TODOs:
8503
-
8504
- - Read form-urlencoded POST data. (Do this BEFORE
8505
- restoring the session, so that we can get the session
8506
- ID from there if needed.)
8507
- */
8508
- end:
8509
- return rc;
8510
-#undef CHECKRC
8511
-}
8512
-
8513
-#undef cson_cgi_env_map_empty_m
8514
-#undef CSON_CGI_USE_SIGNALS
8515
-#undef RNG_FILENAME
8516
-/* end file cgi/cson_cgi.c */
85175450
--- src/cson_amalgamation.c
+++ src/cson_amalgamation.c
@@ -5133,384 +5133,10 @@
5133 }
5134 }
5135 cson_kvp_list_reserve(self,0);
5136 }
5137 /* end file ./cson_lists.h */
5138 /* begin file ./cson_session.c */
5139 #include <string.h> /* strlen() */
5140 #include <stdlib.h> /* memcpy() */
5141 #include <assert.h>
5142
5143
5144 /**
5145 Maximum name length for cson_sessmgr_register(),
5146 including the trailing NUL.
5147 */
5148 enum {
5149 CsonSessionNameLen = 32
5150 };
5151
5152 typedef struct cson_sessmgr_reg cson_sessmgr_reg;
5153 /**
5154 Holds name-to-factory mappings for cson_sessmgr implementations.
5155 */
5156 struct cson_sessmgr_reg
5157 {
5158 char name[CsonSessionNameLen];
5159 cson_sessmgr_factory_f factory;
5160 };
5161
5162
5163 #if !CSON_ENABLE_CPDO
5164 int cson_sessmgr_cpdo( cson_sessmgr ** tgt, cson_object const * opt )
5165 {
5166 return cson_rc.UnsupportedError;
5167 }
5168 #endif
5169 #if !CSON_ENABLE_WHIO
5170 int cson_sessmgr_whio_ht( cson_sessmgr ** tgt, cson_object const * opt )
5171 {
5172 return cson_rc.UnsupportedError;
5173 }
5174 int cson_sessmgr_whio_epfs( cson_sessmgr ** tgt, cson_object const * opt )
5175 {
5176 return cson_rc.UnsupportedError;
5177 }
5178 #endif
5179
5180 /**
5181 Holds the list of registered cson_sessmgr implementations. Used by
5182 cson_sessmgr_register(), cson_sessmgr_load(), and
5183 cson_sessmgr_names().
5184
5185 Maintenance reminder: the API docs promise that at least 10 slots
5186 are initially available.
5187 */
5188 static cson_sessmgr_reg CsonSessionReg[] = {
5189 {{'f','i','l','e',0},cson_sessmgr_file},
5190 #if CSON_ENABLE_CPDO
5191 {{'c','p','d','o',0},cson_sessmgr_cpdo},
5192 #endif
5193 #if CSON_ENABLE_WHIO
5194 {{'w','h','i','o','_','h','t',0},cson_sessmgr_whio_ht},
5195 {{'w','h','i','o','_','e','p','f','s',0},cson_sessmgr_whio_epfs},
5196 #endif
5197 #define REG {{0},NULL}
5198 REG,REG,REG,REG,REG,
5199 REG,REG,REG,REG,REG
5200 #undef REG
5201 };
5202 static const unsigned int CsonSessionRegLen = sizeof(CsonSessionReg)/sizeof(CsonSessionReg[0]);
5203
5204
5205 int cson_sessmgr_register( char const * name, cson_sessmgr_factory_f f )
5206 {
5207 if( ! name || !*name || !f ) return cson_rc.ArgError;
5208 else
5209 {
5210 cson_sessmgr_reg * r = CsonSessionReg;
5211 unsigned int nlen = strlen(name);
5212 unsigned int i = 0;
5213 if( nlen >= CsonSessionNameLen ) return cson_rc.RangeError;
5214 for( ; i < CsonSessionRegLen; ++i, ++r )
5215 {
5216 if( r->name[0] ) continue;
5217 memcpy( r->name, name, nlen );
5218 r->name[nlen] = 0;
5219 r->factory = f;
5220 return 0;
5221 }
5222 return cson_rc.RangeError;
5223 }
5224 }
5225
5226
5227 int cson_sessmgr_load( char const * name, cson_sessmgr ** tgt, cson_object const * opt )
5228 {
5229 if( ! name || !*name || !tgt ) return cson_rc.ArgError;
5230 else
5231 {
5232 cson_sessmgr_reg const * r = CsonSessionReg;
5233 unsigned int i = 0;
5234 for( ; i < CsonSessionRegLen; ++i, ++r )
5235 {
5236 if( ! r->name[0] ) break /* end of list */;
5237 else if( 0 != strcmp( r->name, name ) ) continue;
5238 else
5239 {
5240 assert( NULL != r->factory );
5241 return r->factory( tgt, opt );
5242 }
5243
5244 }
5245 return cson_rc.UnsupportedError;
5246 }
5247 }
5248
5249 char const * const * cson_sessmgr_names()
5250 {
5251 static char const * names[sizeof(CsonSessionReg)/sizeof(CsonSessionReg[0])+1];
5252 unsigned int i = 0;
5253 cson_sessmgr_reg const * r = CsonSessionReg;
5254 for( ; i < CsonSessionRegLen; ++i, ++r )
5255 {
5256 /*
5257 pedantic threading note: as long as this function is not
5258 used concurrently with cson_sessmgr_register(), the worst we
5259 will do here if this function is called, or its results
5260 used, concurrently is overwrite in-use values with the same
5261 values.
5262 */
5263 names[i] = r->name[0] ? r->name : NULL;
5264 }
5265 names[i] = NULL;
5266 return names;
5267 }
5268 /* end file ./cson_session.c */
5269 /* begin file ./cson_session_file.c */
5270 #if !defined(_WIN32) && !defined(_WIN64)
5271 # if !defined(_POSIX_VERSION)
5272 # define _POSIX_VERSION 200112L /* chmod(), unlink() */
5273 # endif
5274 # define ENABLE_POSIX_FILE_OPS 1
5275 #else
5276 # define ENABLE_POSIX_FILE_OPS 0
5277 #endif
5278
5279 #include <stdlib.h>
5280 #include <string.h>
5281 #include <assert.h>
5282 #if ENABLE_POSIX_FILE_OPS
5283 # define UNLINK_FILE unlink
5284 # include <unistd.h> /* unlink() */
5285 # include <sys/stat.h> /* chmod() */
5286 #else
5287 /* http://msdn.microsoft.com/en-us/library/1c3tczd6(v=vs.80).aspx
5288 # define UNLINK_FILE _unlink
5289 # include <io.h>
5290 */
5291 # define UNLINK_FILE remove
5292 # include <stdio.h> /* remove(), _unlink() */
5293 #endif
5294
5295 static int cson_session_file_load( cson_sessmgr * self, cson_value ** tgt, char const * id );
5296 static int cson_session_file_save( cson_sessmgr * self, cson_value const * root, char const * id );
5297 static int cson_session_file_remove( cson_sessmgr * self, char const * id );
5298 static void cson_session_file_finalize( cson_sessmgr * self );
5299
5300 static const cson_sessmgr_api cson_sessmgr_api_file =
5301 {
5302 cson_session_file_load,
5303 cson_session_file_save,
5304 cson_session_file_remove,
5305 cson_session_file_finalize
5306 };
5307
5308 typedef struct cson_sessmgr_file_impl cson_sessmgr_file_impl;
5309 struct cson_sessmgr_file_impl
5310 {
5311 char * dir;
5312 char * prefix;
5313 char * suffix;
5314 };
5315
5316 static const cson_sessmgr cson_sessmgr_file_empty =
5317 {
5318 &cson_sessmgr_api_file,
5319 NULL
5320 };
5321
5322 static char * cson_session_file_strdup( char const * src )
5323 {
5324 size_t const n = src ? strlen(src) : 0;
5325 char * rc = src ? (char *)calloc(1, n+1) : NULL;
5326 if( ! rc ) return NULL;
5327 memcpy( rc, src, n );
5328 return rc;
5329 }
5330
5331 /* Helper macro for varios cson_sessmgr_api member implementations. */
5332 #define IMPL_DECL(RC) \
5333 cson_sessmgr_file_impl * impl = (self && (self->api == &cson_sessmgr_api_file)) \
5334 ? (cson_sessmgr_file_impl*)self->impl \
5335 : NULL; \
5336 if( NULL == impl ) return RC
5337
5338 static int cson_session_file_name( cson_sessmgr_file_impl * impl,
5339 char const * id,
5340 char * buf, unsigned int bufLen )
5341 {
5342 char const * dir = impl->dir ? impl->dir : ".";
5343 char const * pre = impl->prefix ? impl->prefix : "";
5344 char const * suf = impl->suffix ? impl->suffix : "";
5345 char * pos = NULL /* current write possition. */;
5346 unsigned int flen = 0 /* length of the next token. */;
5347 unsigned int olen = 0 /* total number of bytes written so far. */;
5348 if( ! id || !*id ) return cson_rc.ArgError;
5349
5350 #define CHECKLEN if(olen >= bufLen) return cson_rc.RangeError; assert( pos < (buf+bufLen) )
5351 pos = buf;
5352
5353 #define PUSH(FIELD) \
5354 flen = strlen(FIELD); \
5355 olen += flen; \
5356 CHECKLEN; \
5357 strncpy( pos, FIELD, flen ); \
5358 pos += flen
5359
5360 PUSH(dir);
5361
5362 ++olen;
5363 CHECKLEN;
5364 #if defined(_WIN32)
5365 *(pos++) = '\\';
5366 #else
5367 *(pos++) = '/';
5368 #endif
5369
5370 PUSH(pre);
5371 PUSH(id);
5372 PUSH(suf);
5373 if( pos >= (buf + bufLen) ) return cson_rc.RangeError;
5374 *pos = 0;
5375 return 0;
5376 #undef PUSH
5377 #undef CHECKLEN
5378 }
5379
5380 static int cson_session_file_load( cson_sessmgr * self, cson_value ** root, char const * id )
5381 {
5382 enum { BufSize = 1024 };
5383 char fname[BufSize];
5384 FILE * fh = NULL;
5385 int rc;
5386 IMPL_DECL(cson_rc.ArgError);
5387 if( !root || !id || !*id ) return cson_rc.ArgError;
5388 memset( fname, 0, BufSize );
5389 rc = cson_session_file_name( impl, id, fname, BufSize );
5390 if( 0 != rc ) return rc;
5391 fh = fopen( fname, "r" );
5392 if( ! fh ) return cson_rc.IOError;
5393 rc = cson_parse_FILE( root, fh, NULL, NULL );
5394 fclose( fh );
5395 return rc;
5396 }
5397
5398 static int cson_session_file_save( cson_sessmgr * self, cson_value const * root, char const * id )
5399 {
5400 enum { BufSize = 1024 };
5401 char fname[BufSize];
5402 FILE * fh = NULL;
5403 int rc;
5404 IMPL_DECL(cson_rc.ArgError);
5405 if( !root || !id || !*id ) return cson_rc.ArgError;
5406 memset( fname, 0, BufSize );
5407
5408 rc = cson_session_file_name( impl, id, fname, BufSize );
5409 if( 0 != rc ) return rc;
5410 fh = fopen( fname, "w" );
5411 if( ! fh ) return cson_rc.IOError;
5412 #if ENABLE_POSIX_FILE_OPS
5413 chmod( fname, 0600 );
5414 #endif
5415 rc = cson_output_FILE( root, fh, NULL );
5416 fclose( fh );
5417 if( rc )
5418 {
5419 UNLINK_FILE( fname );
5420 }
5421 return rc;
5422 }
5423
5424 void cson_session_file_finalize( cson_sessmgr * self )
5425 {
5426 if( self && (self->api == &cson_sessmgr_api_file) )
5427 {
5428 cson_sessmgr_file_impl * impl = (cson_sessmgr_file_impl *)self->impl;
5429 free( impl->dir );
5430 free( impl->prefix );
5431 free( impl->suffix );
5432 free( impl );
5433 *self = cson_sessmgr_file_empty;
5434 free( self );
5435 }
5436 }
5437
5438 static int cson_session_file_remove( cson_sessmgr * self, char const * id )
5439 {
5440 enum { BufSize = 1024 };
5441 char fname[BufSize];
5442 int rc;
5443 IMPL_DECL(cson_rc.ArgError);
5444 if( !id || !*id ) return cson_rc.ArgError;
5445 memset( fname, 0, BufSize );
5446 rc = cson_session_file_name( impl, id, fname, BufSize );
5447 if( 0 != rc ) return rc;
5448 rc = UNLINK_FILE( fname );
5449 return (0==rc) ? 0 : cson_rc.IOError;
5450 }
5451
5452
5453 int cson_sessmgr_file( cson_sessmgr ** tgt, cson_object const * opt )
5454 {
5455 int rc;
5456 cson_sessmgr * m = tgt ? (cson_sessmgr *)malloc(sizeof(cson_sessmgr)) : NULL;
5457 cson_sessmgr_file_impl * impl = m
5458 ? (cson_sessmgr_file_impl *)malloc(sizeof(cson_sessmgr_file_impl))
5459 : NULL;
5460 if( ! m ) return tgt ? cson_rc.AllocError : cson_rc.ArgError;
5461 else if( ! impl )
5462 {
5463 free(m);
5464 return cson_rc.AllocError;
5465 }
5466 *m = cson_sessmgr_file_empty;
5467 m->impl = impl;
5468 if( opt )
5469 {
5470 cson_string const * jstr;
5471 #define CP(KEY) \
5472 jstr = cson_value_get_string( cson_object_get( opt, # KEY ) ); \
5473 if( jstr ) { \
5474 impl->KEY = cson_session_file_strdup( cson_string_cstr( jstr ) ); \
5475 if( ! impl->KEY ) { \
5476 rc = cson_rc.AllocError; \
5477 goto error_clean; \
5478 } \
5479 } (void)0
5480
5481 CP(dir);
5482 CP(prefix);
5483 CP(suffix);
5484 #undef CP
5485 }
5486 #define CP(KEY,VAL) if( ! impl->KEY ) { \
5487 impl->KEY = cson_session_file_strdup(VAL); \
5488 if( ! impl->KEY ) { \
5489 rc = cson_rc.AllocError; \
5490 goto error_clean; \
5491 } \
5492 } (void)0
5493 #if ENABLE_POSIX_FILE_OPS
5494 CP(dir,"/tmp");
5495 #else
5496 CP(dir,".");
5497 #endif
5498 CP(prefix,"cson-session-");
5499 CP(suffix,".json");
5500 #undef CP
5501 *tgt = m;
5502 return 0;
5503 error_clean:
5504 m->api->finalize( m );
5505 return rc;
5506 }
5507
5508 #undef IMPL_DECL
5509 #undef ENABLE_POSIX_FILE_OPS
5510 #undef UNLINK_FILE
5511 /* end file ./cson_session_file.c */
5512 /* begin file ./cson_sqlite3.c */
5513 /** @file cson_sqlite3.c
5514
5515 This file contains the implementation code for the cson
5516 sqlite3-to-JSON API.
@@ -5532,11 +5158,11 @@
5532
5533 #if defined(__cplusplus)
5534 extern "C" {
5535 #endif
5536
5537 static cson_value * cson_sqlite3_stmt_to_value( sqlite3_stmt * st, int col )
5538 {
5539 if( ! st ) return NULL;
5540 else
5541 {
5542 #if 0
@@ -5546,39 +5172,29 @@
5546 #else
5547 int const vtype = sqlite3_column_type(st,col);
5548 #endif
5549 switch( vtype )
5550 {
5551 case SQLITE_NULL:
5552 return cson_value_null();
5553 case SQLITE_INTEGER: {
 
5554 return cson_value_new_integer( (cson_int_t) sqlite3_column_int64(st, col) );
5555 }
5556 case SQLITE_FLOAT:
5557 return cson_value_new_double( sqlite3_column_double(st, col) );
5558 case SQLITE_BLOB: /* arguably fall through... */
5559 case SQLITE_TEXT: {
5560 char const * str = (char const *)sqlite3_column_text(st,col);
5561 return cson_value_new_string(str, str ? strlen(str) : 0);
5562 }
5563 default:
5564 return NULL;
5565 }
5566 }
5567 }
5568
5569 /**
5570 st must be a valid prepared statement. This function creates
5571 a JSON array containing its columns, in order.
5572
5573 Returns a new array value on success, which the caller owns.
5574 On error NULL is returned.
5575
5576 st is not traversed or freed by this function - only the column
5577 count and names are read.
5578 */
5579 static cson_value * cson_sqlite3_stmt_cols( sqlite3_stmt * st )
5580 {
5581 cson_value * aryV = NULL;
5582 cson_array * ary = NULL;
5583 char const * colName = NULL;
5584 int i = 0;
@@ -5608,10 +5224,78 @@
5608 cson_value_free(aryV);
5609 return NULL;
5610 }
5611 }
5612
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5613 /**
5614 Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
5615 parameter is non-0.
5616 */
5617 static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
@@ -5624,20 +5308,16 @@
5624 cson_object * root = NULL;
5625 cson_value * colsV = NULL;
5626 cson_value * rowsV = NULL;
5627 cson_array * rows = NULL;
5628 cson_value * objV = NULL;
5629 cson_object * obj = NULL;
5630 cson_value * currentValue = NULL;
5631 char const * colName = NULL;
5632 int rc = 0;
5633 int i = 0;
5634 int const colCount = sqlite3_column_count(st);
5635 if( colCount <= 0 ) return cson_rc.ArgError;
5636 rootV = cson_value_new_object();
5637 if( ! rootV ) return cson_rc.AllocError;
5638 colsV = cson_sqlite3_stmt_cols(st);
5639 if( ! colsV )
5640 {
5641 cson_value_free( rootV );
5642 RETURN(cson_rc.AllocError);
5643 }
@@ -5646,13 +5326,11 @@
5646 if( rc )
5647 {
5648 cson_value_free( colsV );
5649 RETURN(rc);
5650 }
5651
5652 colsV = NULL;
5653
5654 rowsV = cson_value_new_array();
5655 if( ! rowsV ) RETURN(cson_rc.AllocError);
5656 rc = cson_object_set( root, "rows", rowsV );
5657 if( rc )
5658 {
@@ -5661,32 +5339,18 @@
5661 }
5662 rows = cson_value_get_array(rowsV);
5663 assert(rows);
5664 while( SQLITE_ROW == sqlite3_step(st) )
5665 {
5666 objV = cson_value_new_object();
5667 if( ! objV ) RETURN(cson_rc.AllocError);
5668 rc = cson_array_append( rows, objV );
5669 if( rc )
5670 {
5671 cson_value_free( objV );
5672 RETURN(rc);
5673 }
5674 obj = cson_value_get_object(objV);
5675 for( i = 0; i < colCount; ++i )
5676 {
5677 colName = sqlite3_column_name( st, i );
5678 if( ! colName ) RETURN(cson_rc.AllocError);
5679 currentValue = cson_sqlite3_stmt_to_value(st,i);
5680 if( ! currentValue ) currentValue = cson_value_null();
5681 rc = cson_object_set( obj, colName, currentValue );
5682 if( 0 != rc )
5683 {
5684 cson_value_free( currentValue );
5685 RETURN(rc);
5686 }
5687 }
5688 }
5689 *tgt = rootV;
5690 return 0;
5691 }
5692 #undef RETURN
@@ -5706,18 +5370,16 @@
5706 cson_object * root = NULL;
5707 cson_value * aryV = NULL;
5708 cson_array * ary = NULL;
5709 cson_value * rowsV = NULL;
5710 cson_array * rows = NULL;
5711 cson_value * colV = NULL;
5712 int rc = 0;
5713 int i = 0;
5714 int const colCount = sqlite3_column_count(st);
5715 if( colCount <= 0 ) return cson_rc.ArgError;
5716 rootV = cson_value_new_object();
5717 if( ! rootV ) return cson_rc.AllocError;
5718 aryV = cson_sqlite3_stmt_cols(st);
5719 if( ! aryV )
5720 {
5721 cson_value_free( rootV );
5722 RETURN(cson_rc.AllocError);
5723 }
@@ -5740,32 +5402,18 @@
5740 }
5741 rows = cson_value_get_array(rowsV);
5742 assert(rows);
5743 while( SQLITE_ROW == sqlite3_step(st) )
5744 {
5745 aryV = cson_value_new_array();
5746 if( ! aryV ) RETURN(cson_rc.AllocError);
5747 rc = cson_array_append( rows, aryV );
5748 if( 0 != rc )
5749 {
5750 cson_value_free( aryV );
5751 RETURN(rc);
5752 }
5753 ary = cson_value_get_array(aryV);
5754 rc = cson_array_reserve(ary, (unsigned int) colCount );
5755 if( 0 != rc ) RETURN(rc);
5756 for( i = 0; i < colCount; ++i )
5757 {
5758 colV = cson_sqlite3_stmt_to_value(st,i);
5759 if( ! colV ) colV = cson_value_null();
5760 rc = cson_array_set( ary, i, colV );
5761 if( 0 != rc )
5762 {
5763 cson_value_free( colV );
5764 RETURN(rc);
5765 }
5766 }
5767 }
5768 *tgt = rootV;
5769 return 0;
5770 }
5771 #undef RETURN
@@ -5797,2721 +5445,5 @@
5797 } /*extern "C"*/
5798 #endif
5799 #undef MARKER
5800 #endif /* CSON_ENABLE_SQLITE3 */
5801 /* end file ./cson_sqlite3.c */
5802 /* begin file cgi/whuuid.h */
5803 #if !defined(WANDERGINHORSE_NET_WHUUID_H_INCLUDED)
5804 #define WANDERGINHORSE_NET_WHUUID_H_INCLUDED 1
5805 #include <stdio.h> /* only for decl of FILE. */
5806 /************************************************************************
5807 An experiment in creating random UUIDs (http://wikipedia.org/wiki/Uuid).
5808
5809
5810 Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)
5811
5812 License: Public Domain
5813
5814
5815 Features:
5816
5817 - Small API. Only two relevant classes and a handful of functions.
5818
5819 - Uses a client-specified RNG source. Two are provided with the
5820 library. The RNG source may be arbitrarily stateful, and each may have
5821 instance-specific data.
5822
5823 - State objects have a uniform cleanup interface, but each implementation
5824 defines which cleanup behaviours need to be performed (e.g. closing
5825 an input file).
5826
5827 - Fairly fast, assuming your RNG is. (My 2.6GHz PC can generate, and send
5828 them to stdout, just over 1.3 million UUIDs per second.)
5829
5830
5831 Misfeatures:
5832
5833 - Does not support a specific version of UUID, as detailed at
5834 [http://wikipedia.org/wiki/Uuid]. Its UUIDs have random data in all
5835 positions, as opposed to reserving certain positions for specific
5836 values or using specified algorithms to generate the values. Thus the
5837 UUIDs it generates are similar to Version 4 UUIDs except that no bytes
5838 are reserved for specific values.
5839
5840 PS: i don't really consider that to be a mis-feature. IMHO UUIDs
5841 should be completely random, with no reserved bytes.
5842
5843
-------------------------------------------------------------------------
5844 TIP: checking for duplicate UUIDs
5845
5846 The sqlite3 tool can be used for checking for duplicate UUIDs. Simply
5847 print the UUIDs, one per line, and feed them into sqlite3 like so:
5848
5849 @code
5850 sqlite3> create table ids (id,unide(id));
5851 sqlite3> .import myUUIDListFile ids
5852 @endcode
5853
5854 If sqlite3 does not complain, there were no duplicates.
5855
5856 You can also test by sorting the list, removing duplicates, and
5857 checking the length of the list. e.g. assume we have a file named "1m"
5858 containing 1 million UUIDs. From a Unix shell we could do:
5859
5860 @code
5861 ~> sort -u < 1m > 1ms
5862 ~> ls -la 1m 1ms
5863 @endcode
5864
5865 If the files have the same size then there were no duplicates.
5866
5867 In my tests i have not encountered duplicates except when testing
5868 a deterministic RNG with a specific seed.
5869 ************************************************************************/
5870
5871 /** @def WHUUID_CONFIG_KEEP_METRICS
5872
5873 If WHUUID_CONFIG_KEEP_METRICS is a true value then the library keeps track
5874 of how many times a given hex digit value is generated by the
5875 whuuid_rng class. It has a minimal performance hit, but if
5876 the data will never be used then it can be turned off.
5877 */
5878 #define WHUUID_CONFIG_KEEP_METRICS 1
5879
5880 /** @enum whuuid_constants
5881
5882 A list of constant values used by the whuuid API.
5883
5884 */
5885 enum whuuid_constants {
5886 /**
5887 The length of a UUID canonical-form string, not including
5888 a trailing NULL bytes. e.g.:
5889
5890 00000000-0000-0000-0000-000000000000
5891 */
5892 whuuid_length_canonical = 36,
5893 /**
5894 The length of a UUID in canonical form, including
5895 a trailing NULL byte.
5896 */
5897 whuuid_length_cstring = whuuid_length_canonical + 1,
5898 /**
5899 The number of bytes of data necessary to store
5900 a UUID in "raw" form.
5901 */
5902 whuuid_length_bytes = 16
5903 };
5904
5905 /**
5906 Represents a single UUID.
5907 */
5908 struct whuuid_t
5909 {
5910 unsigned char bytes[whuuid_length_bytes];
5911 };
5912 typedef struct whuuid_t whuuid_t;
5913 /**
5914 A zero-initialized whuiid_t initialization object.
5915 */
5916 extern const whuuid_t whuuid_t_empty;
5917
5918 /**
5919 A class holding RNG information. Each instance manages a single RNG
5920 source, which is used to populate any number of whuiid_t objects
5921 with random data. They may or may not need to dynamically allocate
5922 resources (e.g. open a file containing random data), depending
5923 on the implementation.
5924
5925 They should normally be initialized via factory functions, and
5926 those functions should:
5927
5928 a) Allocate any private resources the object needs and store them in
5929 self->impl.
5930
5931 b) Set the cleanup() member function to a function which knows how
5932 to clean up any resources stored in self->impl.
5933
5934 c) Set the rand() member to a function which knows how to use
5935 the private state to generate random data.
5936
5937
5938 The most basic usage looks something like this:
5939
5940 @code
5941 whuuid_rng st = whuuid_rng_lcrng; // a Linear Congruent RNG
5942 whuuid_t u = whuuid_t_empty;
5943 char buffer[whuuid_length_canonical+1]; // buffer for UUID string
5944 buffer[whuuid_length_canonical] = 0; // add trailing NULL
5945 for( int i =0; i < 100; ++i )
5946 {// generate 100 UUIDs to print them
5947 whuuid_fill_rand( &u, &st ); // generate UUID using st->rand()
5948 whuuid_to_string( &u, buffer );
5949 puts(buffer);
5950 }
5951 st.cleanup(&st); // see below.
5952 @endcode
5953
5954 In that particular case the state object has no state which
5955 needs cleaning, but we could also set up a FILE as an input source,
5956 in which case we need to clean up the object:
5957
5958 @code
5959 st = whuuid_rng_FILE;
5960 st.impl = fopen("/dev/urandom", "r");
5961 ... use st ...
5962 st.cleanup(&st); // will fclose() the file
5963 @endcode
5964
5965 If a state object is dynamically allocated then it should be freed
5966 after calling its cleanup() member to free any
5967 implementation-specific resources.
5968 */
5969 struct whuuid_rng
5970 {
5971 /**
5972 Must set *tgt to sizeof(unsigned int) random bytes. Must return
5973 0 on success or non-zero if something goes wrong (e.g. the
5974 input source has failed or run out of numbers). How it uses (or
5975 ignores) the self argument is implementation-specific.
5976 */
5977 int (*rand)( struct whuuid_rng * self, unsigned int * tgt );
5978 /**
5979 Must clean up self, but not free self itself. How it does this
5980 is implementation-specific. If it has no private state,
5981 this function may be NULL.
5982
5983 whuuid_rng objects can be allocated on the stack or via
5984 arbitrary mechanisms, so the cleanup routine must not free the
5985 self object. How it is freed (after it is cleaned up) depends
5986 on how it was allocated.
5987 */
5988 void (*cleanup)( struct whuuid_rng * self );
5989 /**
5990 Implementations may store any private state here. This member is
5991 not for public use.
5992 */
5993 void * impl;
5994 /**
5995 Stores the distribution of values created by this state
5996 object. whuuid_fill_rand() updates these values.
5997 */
5998 unsigned long distribution[whuuid_length_bytes];
5999 };
6000
6001
6002 /** Convenience typedef. */
6003 typedef struct whuuid_rng whuuid_rng;
6004
6005 /**
6006 A zero-initialized whuiid_state initialization object.
6007 */
6008 extern const whuuid_rng whuuid_rng_empty;
6009
6010 /**
6011 An almost-empty whuiid_state initialization object with
6012 its rand() member set to whuuid_lc_rand.
6013 */
6014 extern const whuuid_rng whuuid_rng_lcrng;
6015
6016 /**
6017 A whuuid_state initialization object with its rand() member set to
6018 whuuid_FILE_rand and its cleanup() member set to
6019 whuuid_FILE_cleanup. Clients may copy this then set the impl
6020 member to point it to an opened FILE handle. The FILE handle will
6021 be closed when the cleanup() member is called. If the state object
6022 should not close the file when it cleans up, set the cleanup()
6023 member to NULL.
6024 */
6025 extern const whuuid_rng whuuid_rng_FILE;
6026
6027 /**
6028 Implements the whuuid_rng::rand() interface.
6029
6030 This implementaion uses/abuses st->impl to store a numeric state
6031 value for a linear congruent RNG. If st->impl is NULL then a seed
6032 value is generated using some external source (we malloc() a few
6033 bytes to get a random address, and we use that address as a
6034 seed). The state value is stored directly in st->impl and does not
6035 need to be cleaned up. (The memory malloc()ed to get the initial
6036 seed is free()d immediately after it is malloc()ed.)
6037
6038 Returns 0 on success, non-zero on error. The only error conditions
6039 are !st or !tgt. A malloc() error on the initial seeding will not
6040 cause an error (but causes a determinate (but unspecified) seed
6041 value to be used).
6042
6043 In my (informal/unscientific) tests, this RNG works very well for
6044 generating UUIDs, out-performing /dev/urandom in terms of even
6045 numeric distribution most of the time.
6046 */
6047 int whuuid_lc_rand( whuuid_rng * st, unsigned int *tgt );
6048
6049 /**
6050 Implements the whuuid_rng::rand() interface.
6051
6052 If st->impl is not NULL it is assumed to be-a (FILE*) and
6053 sizeof(unsigned int) bytes are read from it and returned via the
6054 tgt argument.
6055
6056 Returns non-zero if !st or !st->impl, or if reading from the file
6057 fails.
6058
6059 Results are undefined if st->impl is non-null but is-not-a FILE.
6060
6061 Note that this implementation does nothing fancy like buffering
6062 some larger amount of random input. Each call reads sizeof(int)
6063 bytes. If performance is of a concern, create an implementation
6064 which stores a struct containing the FILE and the buffer somewhere
6065 in st->impl and reads the input in larger blocks. Also implement a
6066 cleanup function which can free the buffer.
6067
6068 @see whuuid_FILE_cleanup()
6069 @see whuuid_rng_FILE
6070 */
6071 int whuuid_FILE_rand( whuuid_rng * st, unsigned int * tgt );
6072
6073 /**
6074 Implements the whuuid_rng::cleanup() interface for state
6075 objects where obj->impl is-a FILE handle opened via
6076 fopen() (or equivalent).
6077
6078 Assumes self->impl is-a (FILE*) and calls fclose() on it.
6079 */
6080 void whuuid_FILE_cleanup( whuuid_rng * self );
6081
6082 /**
6083 Converts src->bytes to a canonical-form UUID string. dest must be
6084 valid memory at least whuuid_length_canonical bytes long, and on
6085 success exactly whuuid_length_canonical bytes will be written to it.
6086 No terminating null is added.
6087
6088 Returns 0 on success, non-zero on error. The only error conditions
6089 are (!src) or (!dest).
6090 */
6091 int whuuid_to_string( whuuid_t const * src, char * dest );
6092
6093 /**
6094 Populates all of dest->bytes, using st->rand() to collect the
6095 random bytes. It calls st->rand() enough times to collect
6096 whuuid_length_bytes bytes.
6097
6098 Returns 0 on success, non-0 on error. The error conditions are:
6099
6100 - !st or !dest
6101
6102 - st->rand() returns non-0, in which case that error code is passed
6103 back to the caller.
6104
6105 st->distribution is modified by this function to record the number
6106 of times any given digit (hex 0..f) is generated via a call to
6107 rand() (but note that each call to st->rand() is used to generate
6108 (sizeof(unsigning int)*2) digits).
6109
6110 This routine does not guaranty that the bytes returned by
6111 st->rand() are used in the exact same order as they are returned.
6112 */
6113 int whuuid_fill_rand( whuuid_t * dest, whuuid_rng * st );
6114
6115 /**
6116 Copies whuuid_length_bytes bytes from src to dest->bytes.
6117
6118 Returns 0 on success. The only error cases are !dest or !src.
6119 */
6120 int whuuid_fill( whuuid_t * dest, unsigned char const * src );
6121
6122
6123 /**
6124 Compares lhs->bytes and rhs->bytes and
6125 returns 0, less than 0, or greater than 0 depending on whether
6126 lhs equals, is less than, or is greater to rhs, respectively.
6127 i.e. it behaves like memcmp(3).
6128
6129 A NULL value for lhs or rhs compares as less-than any other value
6130 except NULL, to which it compares equal.
6131 */
6132 short whuuid_compare( whuuid_t const * lhs, whuuid_t const * rhs );
6133
6134 /**
6135 Debugging/testing function which dumps the RNG distribution counts
6136 of st to the given FILE handle. The stats are updated on each call
6137 to whuuid_fill_rand() IF the WHUUID_CONFIG_KEEP_METRICS macro is
6138 set to a true value when the library is built.
6139
6140 If full is non-zero then a full list of metrics is dumped,
6141 otherwise just an overview.
6142
6143 Returns 0 on success, non-zero on error (!dest, !st, or
6144 WHUUID_CONFIG_KEEP_METRICS is false).
6145 */
6146 int whuuid_dump_distribution( whuuid_rng const * st, short full, FILE * dest );
6147
6148 #endif /* WANDERGINHORSE_NET_WHUUID_H_INCLUDED */
6149 /* end file cgi/whuuid.h */
6150 /* begin file cgi/whuuid.c */
6151 #include <assert.h>
6152 #include <string.h> /* memset() */
6153
6154 #include <stdlib.h> /* malloc(), free() */
6155
6156
6157 #if WHUUID_CONFIG_KEEP_METRICS
6158 # include <stdio.h> /* fprintf(), FILE */
6159 #endif
6160
6161 const whuuid_t whuuid_t_empty = {
6162 {0,0,0,0,
6163 0,0,0,0,
6164 0,0,0,0,
6165 0,0,0,0}/*bytes*/
6166 };
6167
6168
6169 static void whuuid_noop_cleanup( whuuid_rng * self )
6170 {
6171 /* does nothing */
6172 }
6173 /**
6174 An almost-empty-initialized whuuid_rng object which uses
6175 whuuid_rand_uuint() as its random data source. It has no resources
6176 associated with it.
6177 */
6178 const whuuid_rng whuuid_rng_empty = {
6179 NULL/*rand*/,
6180 whuuid_noop_cleanup/*cleanup*/,
6181 NULL/*impl*/,
6182 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
6183 };
6184
6185 const whuuid_rng whuuid_rng_lcrng = {
6186 whuuid_lc_rand/*rand*/,
6187 whuuid_noop_cleanup/*cleanup*/,
6188 NULL/*impl*/,
6189 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
6190 };
6191
6192 const whuuid_rng whuuid_rng_FILE = {
6193 whuuid_FILE_rand/*rand*/,
6194 whuuid_FILE_cleanup/*cleanup*/,
6195 NULL/*impl*/,
6196 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
6197 };
6198
6199 /** BITS2CHAR(X) expects (X<=15). Returns the hex-code char for it
6200 ('0'..'f'), or 0 if X is out of range. */
6201 #define BITS2CHAR(X) ( ((X)<=0x09) ? ('0'+(X)) : (((X)<=0xF) ? ('a'+((X)-10)) : 0))
6202
6203
6204 void whuuid_FILE_cleanup( whuuid_rng * self )
6205 {
6206 if( self && self->impl )
6207 {
6208 fclose( (FILE*)self->impl );
6209 self->impl = 0;
6210 }
6211 }
6212
6213 int whuuid_FILE_rand( whuuid_rng * st, unsigned int * tgt )
6214 {
6215 if( st && st->impl )
6216 {
6217 unsigned int d = 0;
6218 if( 1 != fread( &d, sizeof(d), 1, (FILE*)st->impl ) )
6219 {
6220 return -1;
6221 }
6222 *tgt = d;
6223 return 0;
6224 }
6225 return -1;
6226 }
6227
6228 #include <time.h>
6229 int whuuid_lc_rand( whuuid_rng * st, unsigned int * tgt )
6230 {
6231 typedef unsigned long NumType;
6232 NumType num = (NumType)st->impl;
6233 if( ! st || ! tgt ) return -1;
6234 #define RNG(SEED) (NumType)( (NumType)((NumType)(SEED) * (NumType)1103515245) + 12345)
6235 /* ^^^^^ This RNG Works very well for this use case (comparable
6236 with /dev/urandom on my box). Algo found in Angband sources. */
6237 if( ! num )
6238 {
6239 void * x;
6240 num = (NumType) st;
6241 /* Generate a unique seed. */
6242 x = malloc( (num % 13)+9 );
6243 free(x);
6244 num = (NumType)(RNG(x) ^ num) >> 6
6245 /*
6246 The bitshift part is to work around the problem that the
6247 left-most byte of generated UUIDs always have the same
6248 starting sequences.
6249 */
6250 ;
6251 }
6252 else
6253 {
6254 num = RNG(num);
6255 }
6256 #undef RNG
6257 st->impl = (void *)num;
6258 *tgt = num;
6259 return 0;
6260 }
6261
6262 int whuuid_to_string( whuuid_t const * src, char * dest )
6263 {
6264 unsigned int i = 0;
6265 int part = 1;
6266 int span = 0;
6267 char byte = 0;
6268 char nibble = 0;
6269 if( ! src || ! dest ) return -1;
6270 for( i = 0; i < whuuid_length_bytes; )
6271 {
6272 int x;
6273 if( 1 == part ) span = 8;
6274 else if( (part>1) && (part<5) ) span = 4;
6275 else if( 5 == part ) span = 12;
6276 for( x = 0; x < (span/2); ++x )
6277 {
6278 byte = src->bytes[i++];
6279 nibble = (byte >> 4) & 0x0F;
6280 *(dest++) = BITS2CHAR(nibble);
6281 nibble = (byte & 0x0F);
6282 *(dest++) = BITS2CHAR(nibble);
6283 }
6284 if( part < 5 )
6285 {
6286 *(dest++) = '-';
6287 ++part;
6288 }
6289 else break;
6290 }
6291 return 0;
6292 }
6293
6294 int whuuid_fill( whuuid_t * dest, unsigned char const * src )
6295 {
6296 if( ! dest || ! src ) return -1;
6297 else
6298 {
6299 memcpy( dest, src, whuuid_length_bytes );
6300 return 0;
6301 }
6302 }
6303
6304 int whuuid_fill_rand( whuuid_t * dest, whuuid_rng * st )
6305 {
6306 unsigned int i = 0, x = 0;
6307 unsigned char * c = 0;
6308 unsigned int r;
6309 unsigned char nibble;
6310 int rc = 0;
6311 if( ! st || ! dest ) return -1;
6312 if( ! dest ) return -1;
6313 for( ; i < whuuid_length_bytes; )
6314 {
6315 rc = st->rand(st, &r);
6316 if( rc ) break;
6317 c = (unsigned char *)&r;
6318 for( x = sizeof(r); (x > 0) && (i < whuuid_length_bytes); --x, ++i, ++c )
6319 {
6320 dest->bytes[i] = *c;
6321 #if WHUUID_CONFIG_KEEP_METRICS
6322 nibble = (*c >> 4) & 0x0F;
6323 ++st->distribution[nibble];
6324 nibble = (*c & 0x0F);
6325 ++st->distribution[nibble];
6326 #endif
6327 }
6328 }
6329 return rc;
6330 }
6331
6332 short whuuid_compare( whuuid_t const * lhs, whuuid_t const * rhs )
6333 {
6334 if( ! lhs ) return rhs ? -1 : 0;
6335 else if( ! rhs ) return 1;
6336 else if( lhs == rhs ) return 0;
6337 else
6338 {
6339 #if 0
6340 unsigned int i = 0;
6341 unsigned char const * l = lhs->bytes;
6342 unsigned char const * r = rhs->bytes;
6343 unsigned char bl = 0, br = 0; /* current byte of lhs/rhs*/
6344 unsigned char nl = 0, nr = 0;/* 4-bit part of bl/br*/
6345 for( ; i < whuuid_length_bytes; ++i )
6346 {
6347 bl = l[i];
6348 br = r[i];
6349 nl = (bl >> 4);
6350 nr = (br >> 4);
6351 if( nl < nr ) return -1;
6352 else if( nl > nr ) return 1;
6353 nl = (bl & 0x0F);
6354 nr = (br & 0x0F);
6355 if( nl < nr ) return -1;
6356 else if( nl > nr ) return 1;
6357 }
6358 return 0;
6359 #else
6360 return memcmp( lhs->bytes, rhs->bytes, whuuid_length_bytes );
6361 #endif
6362 }
6363 }
6364
6365 int whuuid_dump_distribution( whuuid_rng const * st, short full, FILE * dest )
6366 {
6367 #if ! WHUUID_CONFIG_KEEP_METRICS
6368 fprintf("WHUUID_CONFIG_KEEP_METRICS is false, so whuuid_dump_distribution() cannot work!\n");
6369 return -1;
6370 #else
6371 unsigned short i = 0;
6372 double total = 0;
6373 unsigned long int max = 0, min = st->distribution[0];
6374 unsigned long int x = 0;
6375 char minL = 0, maxL = 0;
6376 if( full )
6377 {
6378 fprintf(dest,"Random number distribution:\nDigit:\tCount:\n");
6379 }
6380 for( ; i < 16; ++i )
6381 {
6382 x = st->distribution[i];
6383 total += x;
6384 if( max < x )
6385 {
6386 max = x;
6387 maxL = BITS2CHAR(i);
6388 }
6389 else if( min >= x )
6390 {
6391 min = x;
6392 minL = BITS2CHAR(i);
6393 }
6394 }
6395 if( full )
6396 {
6397 for( i = 0; i < 16; ++i )
6398 {
6399 x = st->distribution[i];
6400 fprintf(dest,"%c\t%lu (%0.6f%%)\n",
6401 BITS2CHAR(i),
6402 x, (x/ total) *100 );
6403 }
6404 }
6405 fprintf(dest,"Least Hits: '%c' (%lu)\nMost Hits: '%c' (%lu)\n",
6406 minL, min, maxL, max );
6407 if( max == min )
6408 {
6409 fprintf(dest,"Least==Most == best possible random distribution!\n" );
6410 }
6411 else
6412 {
6413 fprintf(dest,"Max-Min diff = %lu (%0.4f%% of Max)\n", max - min, ((max - min)/(double)max)*100 );
6414 }
6415 fprintf(dest,"Total random 4-bit UUID digits: %0.0f\n\n",total);
6416 return 0;
6417 #endif
6418 }
6419
6420 #undef BITS2CHAR
6421 /* end file cgi/whuuid.c */
6422 /* begin file cgi/cson_cgi.c */
6423 #include <assert.h>
6424 #include <stdlib.h> /* environ, getenv(), atexit() */
6425 #include <ctype.h> /* isspace() */
6426 #include <string.h> /* strlen() */
6427 #include <stdarg.h>
6428 #include <time.h>
6429 #include <locale.h> /* setlocale(), needed for JSON parser. */
6430
6431 #if CSON_ENABLE_UNIX
6432 # define CSON_CGI_USE_SIGNALS 1
6433 #else
6434 # define CSON_CGI_USE_SIGNALS 0
6435 #endif
6436
6437 /* If RNG_FILENAME evaluates to true then we use that file for getting
6438 random bytes for session IDs. FIXME: we effectively leak a file
6439 handle if this is enabled.
6440 */
6441 #if 0
6442 # define RNG_FILENAME "/dev/urandom"
6443 #else
6444 # define RNG_FILENAME NULL
6445 #endif
6446
6447
6448 #if 1
6449 #define MARKER if(1) printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); if(1) printf
6450 #else
6451 static void noop_printf(char const * fmt, ...) {}
6452 #define MARKER if(0) printf
6453 #endif
6454
6455 #if CSON_CGI_USE_SIGNALS
6456 # include <signal.h> /* signal() */
6457 #endif
6458
6459 const cson_cgi_init_opt cson_cgi_init_opt_empty = cson_cgi_init_opt_empty_m;
6460
6461 /**
6462 Some cson_cgi-internal value keys.
6463 */
6464 static const struct {
6465 char const * ENV_GET;
6466 char const * ENV_POST;
6467 char const * ENV_COOKIE;
6468 char const * ENV_SYS;
6469 char const * ENV_APP;
6470 char const * ENV_ARGV;
6471 char const * ENV_CONFIG;
6472 char const * ENV_SESSION;
6473 char const * RESPONSE_HEADERS;
6474 } cson_cgi_keys = {
6475 "$GET",
6476 "$POST",
6477 "$COOKIE",
6478 "$ENV",
6479 "$APP",
6480 "$ARGV",
6481 "$CONFIG",
6482 "$SESSION",
6483 "response.headers"
6484 };
6485
6486
6487 /**
6488 Shared state used by the cson_cgi API.
6489 */
6490 const cson_cgi_cx cson_cgi_cx_empty = cson_cgi_cx_empty_m;
6491
6492 static int cson_cgi_printf(cson_cgi_cx * cx, char const * fmt, ... )
6493 {
6494 if( ! fmt ) return 0;
6495 else
6496 {
6497 int rc;
6498 va_list vargs;
6499 assert( NULL != cx->opt.outStream );
6500 va_start( vargs, fmt );
6501 rc = vfprintf( cx->opt.outStream, fmt, vargs );
6502 /*if( rc > 0 ) fflush( cx->opt.outStream );*/
6503 va_end( vargs );
6504 return rc;
6505 }
6506 }
6507
6508 static int cson_cgi_puts(cson_cgi_cx * cx, char const * str)
6509 {
6510 size_t const slen = str ? strlen(str) : 0;
6511 if( slen )
6512 {
6513 if( 1 != fwrite( str, slen, 1, cx->opt.outStream ) )
6514 {
6515 return -1;
6516 }
6517 }
6518 if( 1 != fwrite( "\n", 1, 1, cx->opt.outStream ) )
6519 {
6520 return -2;
6521 }
6522 return (int) (slen + 1);
6523 }
6524
6525 static int cson_cgi_putchar(cson_cgi_cx * cx, char ch)
6526 {
6527 return ( 1 == fwrite( &ch, 1, 1, cx->opt.outStream ) )
6528 ? 1
6529 : -1;
6530 }
6531
6532
6533 cson_value * cson_cgi_argv(cson_cgi_cx *cx)
6534 {
6535 return cx ? cx->argv.jval : NULL;
6536 }
6537
6538 cson_array * cson_cgi_argv_array(cson_cgi_cx * cx)
6539 {
6540 return cx ? cson_value_get_array( cx->argv.jval ) : NULL;
6541 }
6542
6543 int cson_cgi_gc_add( cson_cgi_cx * cx, char const * key, cson_value * v, char freeOnError )
6544 {
6545 int const rc = cson_object_set( cx->gc.jobj, key, v );
6546 if( (0 != rc) && freeOnError )
6547 {
6548 cson_value_free( v );
6549 }
6550 return rc;
6551 }
6552
6553 int cson_cgi_response_root_set( cson_cgi_cx * cx, cson_value * v )
6554 {
6555 if( ! cx ) return cson_rc.ArgError;
6556 else if( v && !cson_value_is_object(v) && !cson_value_is_array(v) )
6557 {
6558 return cson_rc.TypeError;
6559 }
6560 else if( cx->response.root != v )
6561 {
6562 int rc = 0;
6563 rc = cson_cgi_gc_add(cx, "response.root", v, 0 )
6564 /** TODO: confirm that cson_object_set() does not
6565 clean up the original object if insertion fails.
6566 If it does, we've just hosed the root node.
6567 */
6568 ;
6569 if( 0 != rc )
6570 {
6571 return rc;
6572 }
6573 else
6574 {
6575 cx->response.root = v;
6576 return 0;
6577 }
6578 }
6579 else
6580 {
6581 return 0;
6582 }
6583
6584 }
6585 cson_value * cson_cgi_response_root_get( cson_cgi_cx * cx, char createMode )
6586 {
6587 if( ! cx ) return NULL;
6588 else if( cx->response.root ) return cx->response.root;
6589 else
6590 {
6591 if( 0 != createMode )
6592 {
6593 if( createMode > 0 )
6594 {
6595 cx->response.root = cson_value_new_object();
6596 }
6597 else if( createMode < 0 )
6598 {
6599 cx->response.root = cson_value_new_array();
6600 }
6601 if( cx->response.root &&
6602 (0 != cson_cgi_gc_add(cx, "response.root", cx->response.root, 1 )) )
6603 {
6604 cx->response.root = NULL /* was cleaned up by cson_cgi_gc_add() */;
6605 }
6606 }
6607 return cx->response.root;
6608 }
6609 }
6610
6611
6612 /** @internal
6613
6614 Tokenizes an input string on a given separator. Inputs are:
6615
6616 - (inp) = is a pointer to the pointer to the start of the input.
6617
6618 - (separator) = the separator character
6619
6620 - (end) = a pointer to NULL. i.e. (*end == NULL)
6621
6622 This function scans *inp for the given separator char or a NULL char.
6623 Successive separators at the start of *inp are skipped. The effect is
6624 that, when this function is called in a loop, all neighboring
6625 separators are ignored. e.g. the string "aa.bb...cc" will tokenize to
6626 the list (aa,bb,cc) if the separator is '.' and to (aa.,...cc) if the
6627 separator is 'b'.
6628
6629 Returns 0 (false) if it finds no token, else non-0 (true).
6630
6631 Output:
6632
6633 - (*inp) will be set to the first character of the next token.
6634
6635 - (*end) will point to the one-past-the-end point of the token.
6636
6637 If (*inp == *end) then the end of the string has been reached
6638 without finding a token.
6639
6640 Post-conditions:
6641
6642 - (*end == *inp) if no token is found.
6643
6644 - (*end > *inp) if a token is found.
6645
6646 It is intolerant of NULL values for (inp, end), and will assert() in
6647 debug builds if passed NULL as either parameter.
6648
6649 When looping, one must be sure to re-set the inp and end
6650 parameters. For example:
6651
6652 @code
6653 char const * head = input;
6654 char const * tail = NULL;
6655 while( cson_cgi_next_token( &inp, '/', &tail ) ) {
6656 ...
6657 head = tail;
6658 tail = NULL;
6659 }
6660 @endcode
6661
6662 If the loop calls 'continue', it must be careful to
6663 ensure that the parameters are re-set, to avoid an endless
6664 loop. This can be simplified with a goto:
6665
6666 @code
6667 while( cson_cgi_next_token( &inp, '/', &tail ) ) {
6668 if( some condition ) {
6669 ... do something ...
6670 goto next_iter;
6671 }
6672 else {
6673 ....
6674 }
6675 next_iter;
6676 head = tail;
6677 tail = NULL;
6678 }
6679 @endcode
6680
6681 */
6682 char cson_cgi_next_token( char const ** inp, char separator, char const ** end )
6683 {
6684 char const * pos = NULL;
6685 assert( inp && end && *inp );
6686 if( ! inp || !end ) return 0;
6687 else if( *inp == *end ) return 0;
6688 pos = *inp;
6689 if( !*pos )
6690 {
6691 *end = pos;
6692 return 0;
6693 }
6694 for( ; *pos && (*pos == separator); ++pos) { /* skip preceeding splitters */ }
6695 *inp = pos;
6696 for( ; *pos && (*pos != separator); ++pos) { /* find next splitter */ }
6697 *end = pos;
6698 return (pos > *inp) ? 1 : 0;
6699 }
6700
6701 /**
6702 If map->jval is NULL then map->jval is created using
6703 cson_value_new_object() and map->jobj is assigned to its object
6704 reference. The newly-created map->jval is appended to
6705 cx->gc to ensure that map->jval lives a full life (as
6706 opposed to potentially being prematurly GC'd if a client later adds
6707 map->jval to his own container).
6708
6709 If map->jval is not NULL then this function is a no-op.
6710
6711 This function will assert() if map is NULL.
6712
6713 Returns 0 on success, else cson_rc.AllocError. On error map->jval
6714 will be NULL after this call.
6715
6716 On success, ownership of map->jval is transfered to (or potentially
6717 shared with) cx->gc.
6718 */
6719 static int cson_cgi_init_env_map( cson_cgi_cx * cx, char const * gckey, cson_cgi_env_map * map )
6720 {
6721 int rc = 0;
6722 assert( NULL != map );
6723 if( NULL == map->jval )
6724 {
6725 assert( NULL == map->jobj );
6726 map->jval = cson_value_new_object();
6727 if( NULL == map->jval ) return cson_rc.AllocError;
6728 rc = cson_cgi_gc_add( cx, gckey, map->jval, 1 )
6729 /* We do this to avoid a corner case in cleanup logic
6730 if the client stores this object in another container.
6731 */;
6732 if( 0 != rc )
6733 {
6734 map->jval = NULL /* was cleaned up by cson_cgi_gc_add() */;
6735 }
6736 else
6737 {
6738 map->jobj = cson_value_get_object( map->jval );
6739 assert( NULL != map->jobj );
6740 }
6741 }
6742 return rc;
6743 }
6744
6745 char const * cson_cgi_getenv_cstr( cson_cgi_cx * cx, char const * where, char const * key )
6746 {
6747 return cson_string_cstr( cson_value_get_string( cson_cgi_getenv(cx, where, key) ) );
6748 }
6749
6750 cson_value * cson_cgi_path_part( cson_cgi_cx * cx, unsigned short ndx )
6751 {
6752 cson_value * piV = cson_cgi_getenv( cx, "e", "PATH_INFO_SPLIT" );
6753 if( ! piV ) return NULL;
6754 else
6755 {
6756 unsigned int alen;
6757 cson_array * ar = cson_value_get_array(piV);
6758 assert( NULL != ar );
6759 alen = cson_array_length_get( ar );
6760 return ( ndx >= alen )
6761 ? NULL
6762 : cson_array_get( ar, ndx );
6763 }
6764 }
6765
6766 char const * cson_cgi_path_part_cstr( cson_cgi_cx * cx, unsigned short ndx )
6767 {
6768 return cson_string_cstr( cson_value_get_string( cson_cgi_path_part( cx, ndx ) ) );
6769 }
6770
6771 /**
6772 cson_cgi_hexchar_to_int():
6773
6774 For 'a'-'f', 'A'-'F' and '0'-'9', returns the appropriate decimal
6775 number. For any other character it returns -1.
6776 */
6777 static int cson_cgi_hexchar_to_int( int ch )
6778 {
6779 if( (ch>='a' && ch<='f') ) return ch-'a'+10;
6780 else if( (ch>='A' && ch<='F') ) return ch-'A'+10;
6781 else if( (ch>='0' && ch<='9') ) return ch-'0';
6782 return -1;
6783 }
6784
6785 /**
6786
6787 Replaces %XX patterns in str with their equivalent character and
6788 '+' characters with a single whitespace. %XX patterns which are
6789 not hexidecimal values are not translated.
6790
6791 str must be NULL or a NUL-terminated string. If it is NULL or the
6792 first byte is NUL then 0 is returned and this function has no
6793 side-effects.
6794
6795 BUGS(?): URL-decoding might have a few bugs/corner cases.
6796 */
6797 static int cson_cgi_urldecode_inline( char * str )
6798 {
6799 unsigned char ch = 0;
6800 unsigned char cx1 = 0;
6801 unsigned char cx2 = 0;
6802 int decoded;
6803 unsigned char * pos = (unsigned char *)str;
6804 unsigned char * out = pos;
6805 unsigned char const * end;
6806 size_t slen = (str && *str) ? strlen(str) : 0;
6807 if( !slen ) return 0;
6808 end = pos + slen;
6809 for( ; pos < end; ++pos )
6810 {
6811 ch = *pos;
6812 if( ch == '%' )
6813 {
6814 cx1 = *(pos+1);
6815 /* FIXME: with only minor refactoring we can remove the
6816 isxdigit() calls and use cson_cgi_hexchar_to_int()
6817 instead, checking for a negative return value. That
6818 would potentially save us 2 extra function calls here.
6819 */
6820 if( isxdigit(cx1) )
6821 {
6822 cx2 = *(pos+2);
6823 if( isxdigit(cx2) )
6824 {
6825 decoded = (cson_cgi_hexchar_to_int( cx1 ) * 16)
6826 + cson_cgi_hexchar_to_int( cx2 );
6827 *(out++) = (char)decoded;
6828 pos += 2;
6829 continue;
6830 }
6831 /* else fall through... */
6832 }
6833 /* else fall through... */
6834 }
6835 else if( ch == '+' )
6836 {
6837 *(out++) = ' ';
6838 continue;
6839 }
6840 *(out++) = ch;
6841 }
6842 *out = 0;
6843 return 0;
6844 }
6845
6846 /**
6847 If PATH_INFO is set, this function splits it on '/'
6848 characters and creates an array out of the elements.
6849 The array is stored as $ENV["PATH_INFO_SPLIT"].
6850
6851 Returns non-0 on error. If PATH_INFO is not set,
6852 0 is returned. If it is set but has no entries,
6853 an empty array is created.
6854
6855 A return value of cson_rc.RangeError probably means that a path
6856 element was longer than our internal buffer size, in which case
6857 processing ends and PATH_INFO_SPLIT is not set. That error can
6858 probably be ignored by the caller, but all others are probably
6859 serious (e.g. AllocError).
6860 */
6861 static int cson_cgi_import_path_info(cson_cgi_cx *cx)
6862 {
6863 char const * pi = cson_cgi_getenv_cstr(cx, "e","PATH_INFO");
6864 if( NULL == pi ) return 0;
6865 else
6866 {
6867 cson_value * arV = cson_value_new_array();
6868 cson_array * ar;
6869 char const * head = pi;
6870 char const * tail = NULL;
6871 if( ! arV ) return cson_rc.AllocError;
6872 else
6873 {
6874 enum { BufSize = 128 };
6875 char buf[BufSize];
6876 cson_value * partV;
6877 unsigned int slen;
6878 int rc = 0;
6879 ar = cson_value_get_array(arV);
6880 while( cson_cgi_next_token( &head, '/', &tail ) )
6881 {
6882 slen = (tail-head);
6883 if( slen >= BufSize )
6884 {
6885 rc = cson_rc.RangeError;
6886 goto end_clean;
6887 }
6888 memcpy( buf, head, slen );
6889 buf[slen] = 0;
6890 cson_cgi_urldecode_inline( buf );
6891 partV = cson_value_new_string( buf, strlen(buf) );
6892 if( ! partV )
6893 {
6894 rc = cson_rc.AllocError;
6895 goto end_clean;
6896 }
6897 rc = cson_array_append( ar, partV );
6898 if( rc )
6899 {
6900 cson_value_free( partV );
6901 goto end_clean;
6902 }
6903 partV = NULL;
6904 head = tail;
6905 tail = NULL;
6906 }
6907 assert( 0 == rc );
6908 rc = cson_object_set( cx->request.env.jobj,
6909 "PATH_INFO_SPLIT",
6910 arV );
6911 end_clean:
6912 if( rc )
6913 {
6914 cson_value_free( arV );
6915 }
6916 return rc;
6917 }
6918 }
6919 }
6920
6921 /**
6922 Imports (extern char ** environ) into cx->request.env, initializing
6923 cx->request.env if needed. If called multiple times the environment
6924 is re-read each time, but old entries which are no longer in the
6925 new environment are not removed from cx->request.env.
6926
6927 Returns 0 on success.
6928 */
6929 static int cson_cgi_import_environ(cson_cgi_cx * cx)
6930 {
6931 extern char ** environ;
6932 int i = 0;
6933 char const * e = environ[0];
6934 char const * v = NULL;
6935 enum { KeyBufSize = 512 };
6936 char keybuf[KeyBufSize];
6937 char * kpos = NULL;
6938 int rc = 0;
6939 cson_value * jv = NULL;
6940 rc = cson_cgi_init_env_map( cx, cson_cgi_keys.ENV_SYS, &cx->request.env );
6941 if( 0 != rc ) return rc;
6942 for( ; e && *e; e = environ[++i] )
6943 {
6944 v = NULL;
6945 memset( keybuf, 0, KeyBufSize );
6946 kpos = keybuf;
6947 for( ; *e && ('=' != *e); ++e )
6948 {
6949 *(kpos++) = *e;
6950 assert( kpos < (keybuf+KeyBufSize) );
6951 if( kpos >= (keybuf+KeyBufSize) )
6952 {
6953 return cson_rc.RangeError;
6954 }
6955 }
6956 if( '=' == *e )
6957 {
6958 v = e+1;
6959 }
6960 else
6961 {
6962 v = "";
6963 }
6964 jv = cson_value_new_string( v, strlen(v) );
6965 if( NULL == jv )
6966 {
6967 rc = cson_rc.AllocError;
6968 break;
6969 }
6970 rc = cson_object_set( cx->request.env.jobj, keybuf, jv );
6971 if( 0 != rc ) break;
6972 }
6973 if( 0 == rc )
6974 {
6975 rc = cson_cgi_import_path_info(cx);
6976 }
6977 return rc;
6978 }
6979
6980 /**
6981 Tries to save the current session data, if any, using the
6982 configured session manager.
6983
6984 Returns 0 on success. If the environment has no session,
6985 it is treated as success but nothing is actually saved.
6986
6987 If no session manager has been configured then
6988 cson_rc.UnsupportedError is returned.
6989 */
6990 static int cson_cgi_session_save(cson_cgi_cx * cx)
6991 {
6992 if( ! cx->session.mgr )
6993 {
6994 return cson_rc.UnsupportedError;
6995 }
6996 else if( !cx->session.id || !cx->session.env.jval )
6997 {
6998 return 0;
6999 }
7000 else
7001 {
7002 return cx->session.mgr->api->save( cx->session.mgr,
7003 cx->session.env.jval,
7004 cx->session.id );
7005 }
7006 }
7007
7008 cson_cgi_cx * cson_cgi_cx_alloc()
7009 {
7010 cson_cgi_cx * rc = (cson_cgi_cx *)malloc(sizeof(cson_cgi_cx));
7011 if( rc )
7012 {
7013 *rc = cson_cgi_cx_empty;
7014 rc->misc.allocStamp = rc;
7015 }
7016 return rc;
7017 }
7018
7019 char cson_cgi_cx_clean( cson_cgi_cx * cx )
7020 {
7021 if( !cx ) return 0;
7022 else
7023 {
7024 void const * allocStamp = NULL;
7025 if( cx->session.mgr )
7026 {
7027 cson_cgi_session_save(cx) /* ignoring error code */;
7028 cx->session.mgr->api->finalize( cx->session.mgr );
7029 cx->session.mgr = NULL;
7030 }
7031 if(NULL != cx->gc.jval)
7032 {
7033 cson_value_free( cx->gc.jval );
7034 cx->gc.jval = NULL;
7035 cx->gc.jobj = NULL;
7036 }
7037 if( cx->session.id )
7038 {
7039 free( cx->session.id );
7040 cx->session.id = NULL;
7041 }
7042 cson_buffer_reserve( &cx->tmpBuf, 0 );
7043 allocStamp = cx->misc.allocStamp;
7044 if( cx->opt.inStream && (stdin != cx->opt.inStream) ) fclose(cx->opt.inStream);
7045 if( cx->opt.outStream && (stderr == cx->opt.outStream) && (stdout != cx->opt.outStream) ) fclose(cx->opt.outStream);
7046 if( cx->opt.errStream && (stderr == cx->opt.errStream) && (stdout != cx->opt.errStream) ) fclose(cx->opt.errStream);
7047 *cx = cson_cgi_cx_empty;
7048 return ( allocStamp == cx )
7049 ? (free( cx ), 1)
7050 : 0;
7051 }
7052 }
7053
7054 cson_value * cson_cgi_env_get_val( cson_cgi_cx * cx, char which, char createIfNeeded )
7055 {
7056 cson_cgi_env_map * map = NULL;
7057 cson_value * v = NULL;
7058 char const * gckey = NULL;
7059 switch( which )
7060 {
7061 case 'c':
7062 case 'C':
7063 map = &cx->request.cookie;
7064 gckey = cson_cgi_keys.ENV_COOKIE;
7065 break;
7066 case 'e':
7067 case 'E':
7068 gckey = cson_cgi_keys.ENV_SYS;
7069 map = &cx->request.env;
7070 break;
7071 case 'g':
7072 case 'G':
7073 gckey = cson_cgi_keys.ENV_GET;
7074 map = &cx->request.get;
7075 break;
7076 case 'f':
7077 case 'F':
7078 gckey = cson_cgi_keys.ENV_CONFIG;
7079 map = &cx->config;
7080 break;
7081 case 'p':
7082 case 'P':
7083 gckey = cson_cgi_keys.ENV_POST;
7084 map = &cx->request.post;
7085 break;
7086 case 'a':
7087 case 'A':
7088 gckey = cson_cgi_keys.ENV_APP;
7089 map = &cx->clientEnv;
7090 break;
7091 case 's':
7092 case 'S':
7093 gckey = cson_cgi_keys.ENV_SESSION;
7094 map = &cx->session.env;
7095 break;
7096 default:
7097 break;
7098 }
7099 if( map )
7100 {
7101 v = map->jval;
7102 if( !v && createIfNeeded )
7103 {
7104 assert( NULL != gckey );
7105 cson_cgi_init_env_map( cx, gckey, map );
7106 v = map->jval;
7107 }
7108 }
7109 return v;
7110 }
7111
7112 cson_object * cson_cgi_env_get_obj( cson_cgi_cx * cx, char which, char createIfNeeded )
7113 {
7114 return cson_value_get_object( cson_cgi_env_get_val( cx, which, createIfNeeded ) );
7115 }
7116
7117 /**
7118 Sets a variable in one of the environment objects.
7119
7120 env must be the conventional character representation
7121 (case-insensitive) for on of the following environment objects:
7122
7123 - g = GET
7124 - p = POST
7125 - e = ENV
7126 - c = COOKIE
7127 - u = USER
7128
7129 On success 0 is returned and ownership of v is transfered to (or
7130 shared with) the appropriate environment object. On error non-zero
7131 is returned and ownership of v is not modified.
7132 */
7133 static int cson_cgi_setenv_x( cson_cgi_cx * cx, char env, char const * key, cson_value * v )
7134 {
7135 if( ! key || !*key ) return cson_rc.ArgError;
7136 else
7137 {
7138 cson_object * jo = cson_cgi_env_get_obj( cx, env, 1 );
7139 return ( NULL == jo )
7140 ? cson_rc.RangeError /* FIXME: expand the above code so we
7141 can distinguish between invalid
7142 env and allocation error. (Except that
7143 there is no allocation on get_obj().*/
7144 : cson_object_set( jo, key, v );
7145 }
7146 }
7147
7148 int cson_cgi_setenv( cson_cgi_cx * cx, char const * key, cson_value * v )
7149 {
7150 return cson_cgi_setenv_x( cx, 'a', key, v );
7151 }
7152
7153 int cson_cgi_cookie_set( cson_cgi_cx * cx, char const * key, cson_value * v )
7154 {
7155
7156 if( ! key || !*key ) return cson_rc.ArgError;
7157 else
7158 {
7159 cson_object * jo = cson_cgi_env_get_obj( cx, 'c', 1 );
7160 return (NULL == jo)
7161 ? cson_rc.AllocError
7162 : cson_object_set( jo, key, v ? v : cson_value_null() );
7163 }
7164 }
7165
7166 int cson_cgi_cookie_set2( cson_cgi_cx * cx,
7167 char const * key, cson_value * v,
7168 char const * domain, char const * path,
7169 unsigned int expires, char secure, char httponly )
7170 {
7171 if( ! key || !*key ) return cson_rc.ArgError;
7172 else
7173 {
7174 int rc;
7175 cson_value * jv = cson_value_new_object();
7176 cson_object * jo = cson_value_get_object(jv);
7177 cson_value * x = NULL;
7178 if( ! jo ) return cson_rc.AllocError;
7179 if( ! v ) v = cson_value_null() /* reminder: does not allocate */;
7180
7181 #define SET(KEY) if( 0 != (rc = cson_object_set( jo, KEY, x) ) ) { \
7182 cson_value_free(x); \
7183 cson_value_free( jv ); \
7184 return rc; \
7185 }
7186
7187 if( NULL != domain )
7188 {
7189 x = cson_value_new_string( domain, strlen(domain) );
7190 SET("domain");
7191 }
7192 if( NULL != path )
7193 {
7194 x = cson_value_new_string( path, strlen(path) );
7195 SET("path");
7196 }
7197
7198 if( cson_value_is_null(v) )
7199 {
7200 x = cson_value_new_integer( 1 );
7201 SET("expires");
7202 }
7203 else if( expires )
7204 {
7205 x = cson_value_new_integer( (cson_int_t) expires );
7206 SET("expires");
7207 }
7208 if( secure )
7209 {
7210 x = cson_value_new_bool(secure);
7211 SET("secure");
7212 }
7213 if( httponly )
7214 {
7215 x = cson_value_new_bool(httponly);
7216 SET("httponly");
7217 }
7218 #undef SET
7219 rc = cson_cgi_cookie_set( cx, key, jv );
7220 if( 0 != rc )
7221 {
7222 cson_value_free( jv );
7223 }
7224 else
7225 { /* set "value" last so that we can avoid tricky
7226 ownership/lifetime problems in error cases.
7227 */
7228 if( 0 != (rc = cson_object_set( jo, "value", v) ) )
7229 { /* remove the cookie. Note that this particular case
7230 does not remove it from the HTTP client. In order to do that
7231 we have to keep the existing path/domain/etc info.
7232 */
7233 cson_object * cookies = cson_cgi_env_get_obj( cx, 'c', 0 );
7234 if( cookies )
7235 {
7236 cson_object_set( cookies, key, cson_value_null() )
7237 /* Ignore error code, since we have no fallback
7238 and cson_value_null() does not allocate.
7239 Worst-case is that removing it fails, but when we
7240 emit the cookie headers that cookie will be skipped
7241 because it has no "value" field.
7242 */
7243 ;
7244 }
7245 }
7246 }
7247 return rc;
7248 }
7249 }
7250
7251 cson_value * cson_cgi_getenv( cson_cgi_cx * cx, char const * fromWhere, char const * key )
7252 {
7253 cson_value * jv = NULL;
7254 cson_object * map = NULL;
7255 if( (NULL == fromWhere) || !*fromWhere ) fromWhere = CSON_CGI_GETENV_DEFAULT;
7256 if( !key || !*key ) return NULL;
7257 for( ; *fromWhere ; ++fromWhere )
7258 {
7259 map = cson_cgi_env_get_obj( cx, *fromWhere, 0 );
7260 if( (NULL == map) && (('r'==*fromWhere)||('R'==*fromWhere)) )
7261 {
7262 jv = cson_cgi_getenv( cx, "gpc", key );
7263 }
7264 if( NULL != jv ) /* only in 'R' case */ break;
7265 else if( NULL == map ) continue /* invalid character or NULL map */;
7266 jv = cson_object_get( map, key );
7267 if( NULL != jv ) break;
7268 }
7269 return jv;
7270 }
7271
7272
7273 int cson_cgi_response_header_add( cson_cgi_cx * cx, char const * key, cson_value * v )
7274 {
7275 int rc = 0;
7276 if( !cx || ! key || !*key ) return cson_rc.ArgError;
7277 rc = cson_cgi_init_env_map( cx, cson_cgi_keys.RESPONSE_HEADERS, &cx->response.headers );
7278 if( 0 == rc )
7279 {
7280 assert( NULL != cx->response.headers.jobj );
7281 rc = cson_object_set( cx->response.headers.jobj, key, v );
7282 }
7283 return rc;
7284 }
7285
7286
7287 char cson_cgi_is_jsonp(cson_cgi_cx * cx)
7288 {
7289 if( ! cx ) return 0;
7290 else if( cx->misc.isJSONP < 0 )
7291 { /* guess */
7292 cx->misc.isJSONP = (NULL == cson_cgi_getenv( cx, "agp", CSON_CGI_KEY_JSONP ))
7293 ? 0 : 1;
7294 }
7295 return cx->misc.isJSONP;
7296 }
7297
7298 void cson_cgi_enable_jsonp( cson_cgi_cx * cx, char b )
7299 {
7300 if( cx ) cx->misc.isJSONP = b ? 1 : 0;
7301 }
7302
7303 char const * cson_cgi_guess_content_type(cson_cgi_cx * cx)
7304 {
7305 char const * cset;
7306 char doUtf8;
7307 cset = getenv("HTTP_ACCEPT_CHARSET");
7308 doUtf8 = ((NULL == cset) || (NULL!=strstr("utf-8",cset)))
7309 ? 1 : 0;
7310 if( cson_cgi_is_jsonp(cx) )
7311 {
7312 return doUtf8
7313 ? "application/javascript; charset=utf-8"
7314 : "application/javascript";
7315 }
7316 else
7317 {
7318 /*
7319 Content-type
7320
7321 If the browser does not sent an ACCEPT for application/json
7322 then we fall back to text/plain.
7323 */
7324 char const * cstr;
7325 cstr = getenv("HTTP_ACCEPT");
7326 if( NULL == cstr )
7327 {
7328 return doUtf8
7329 ? "application/json; charset=utf-8"
7330 : "application/json";
7331 }
7332 else
7333 {
7334 if( strstr( cstr, "application/json" )
7335 || strstr( cstr, "*/*" ) )
7336 {
7337 return doUtf8
7338 ? "application/json; charset=utf-8"
7339 : "application/json";
7340 }
7341 else
7342 {
7343 return "text/plain";
7344 }
7345 }
7346 }
7347 }
7348
7349
7350 /**
7351 URL-encodes src to dest and NUL-terminates it. dest must be at
7352 least *destLen bytes long. Upon a successful return, *destLen
7353 will be modified to hold the new string's length.
7354
7355 Returns 0 on success. On error dest might be partially populated.
7356
7357 Returns cson_rc.RangeError if dest is not long enough to hold
7358 the conversion and a terminating NUL.
7359 */
7360 static int cson_cgi_urlencode( char const * src, char * dest_, size_t * destLen )
7361 {
7362 #define needs_escape \
7363 ( (ch >= 32 && ch <=47) \
7364 || ( ch>=58 && ch<=64) \
7365 || ( ch>=91 && ch<=96) \
7366 || ( ch>=123 && ch<=126) \
7367 || ( ch<32 || ch>=127) \
7368 )
7369 char const * pos = src;
7370 char ch;
7371 size_t dpos = 0;
7372 char * dest = dest_;
7373 static char const * hex = "0123456789ABCDEF";
7374 if( ! dest || !destLen ) return cson_rc.RangeError;
7375 for( ; pos && *pos; ++pos )
7376 {
7377 ch = *pos;
7378 if( ! needs_escape )
7379 {
7380 if( ++dpos >= *destLen ) return cson_rc.RangeError;
7381 *(dest++) = ch;
7382 continue;
7383 }
7384 else
7385 {
7386 if( (dpos+=3) >= *destLen ) return cson_rc.RangeError;
7387 *(dest++) = '%';
7388 *(dest++) = hex[((ch>>4)&0xf)];
7389 *(dest++) = hex[(ch&0xf)];
7390 }
7391 }
7392 if( ++dpos >= *destLen ) return cson_rc.RangeError;
7393 *dest = 0;
7394 *destLen = dest - dest_;
7395 return 0;
7396 #undef needs_escape
7397 }
7398
7399 /**
7400 If orig is one of the types (string,double,bool,undef,null) then
7401 a pointer to its string representation is returned, else NULL
7402 is returned.
7403
7404 For non-string types, dest must be at least destLen bytes of memory, and
7405 if destLen is not long enough to hold the string form then NULL is returned.
7406
7407 On success a pointer to a string is returned. It will be one of:
7408
7409 - if orig is-a string then it's underlying string.
7410
7411 - for (double,integer,bool,undef,null), dest will be returned. The encoded
7412 form is decimal for (double,integer), the number 0 or 1 for bool, and the
7413 number 0 for (undef,null).
7414
7415 Ownership of dest is not modified by this call.
7416
7417 The returned value is valid until either orig or dest are modified.
7418
7419 On error dest is not modified. Dest is also not modified if orig
7420 is-a string, as its own string bytes are returned instead.
7421 */
7422 static char const * cson_cgi_pod_to_string( cson_value const * orig,
7423 char * dest, unsigned int destLen )
7424 {
7425 if( ! orig || !dest || !destLen ) return NULL;
7426 else
7427 {/* FIXME? use cson's output support for the numeric types. i
7428 _think_ those bits might not be in the public API, though.
7429 We could use it for serializing objects/arrays, in any case.
7430 */
7431 enum { NumBufSize = 80 };
7432 if( cson_value_is_string(orig) )
7433 {
7434 cson_string const * jstr = cson_value_get_string(orig);
7435 assert( NULL != jstr );
7436 return cson_string_cstr( jstr );
7437 }
7438 else if( cson_value_is_integer(orig) )
7439 {
7440 char tmp[NumBufSize] = {0};
7441 int const sc = sprintf( tmp, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig));
7442 if( sc <= 0 ) return NULL;
7443 else if( (unsigned int)sc >= destLen ) return NULL;
7444 else
7445 {
7446 strcpy( dest, tmp );
7447 return dest;
7448 }
7449 }
7450 else if( cson_value_is_double(orig) )
7451 {
7452 char tmp[NumBufSize] = {0};
7453 int const sc = sprintf( tmp, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(orig));
7454 if( sc <= 0 ) return NULL;
7455 else if( (unsigned int)sc >= destLen ) return NULL;
7456 else
7457 {
7458 strcpy( dest, tmp );
7459 if(1)
7460 { /* Strip trailing zeroes... */
7461 unsigned int urc = strlen(dest);
7462 char * pos = dest + urc - 1;
7463 for( ; ('0' == *pos) && urc && (*(pos-1) != '.'); --pos, --urc )
7464 {
7465 *pos = 0;
7466 }
7467 assert(urc && *pos);
7468 }
7469 return dest;
7470 }
7471 }
7472 else if( cson_value_is_bool( orig ) )
7473 {
7474 char const bv = cson_value_get_bool(orig);
7475 if( destLen < 2 ) return NULL;
7476 *dest = bv ? '1' : '0';
7477 *(dest+1) = 0;
7478 return dest;
7479 }
7480 else if( cson_value_is_null( orig ) || cson_value_is_undef( orig ) )
7481 {
7482 if( destLen < 2 ) return NULL;
7483 *dest = '0';
7484 *(dest+1) = 0;
7485 return dest;
7486 }
7487 else
7488 {
7489 return NULL;
7490 }
7491 }
7492 }
7493
7494
7495 /**
7496 Writes an RFC822 timestamp string to dest, which must be at least destLen bytes long.
7497 On success returns dest, else NULL. destLen must be at least 31.
7498 */
7499 static char * cson_cgi_rfc822_timedate( time_t now, char * dest, unsigned int destLen )
7500 {
7501 static const char * dayNames[] =
7502 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
7503 0 };
7504 static const char * monthNames[] =
7505 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
7506 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
7507 0};
7508
7509 struct tm * t = (dest && (destLen>30)) ? gmtime(&now) : NULL;
7510 if( ! t || (destLen<31) ) return NULL;
7511 else
7512 {
7513 int const rc = sprintf( dest,
7514 "%s, %d %s %02d %02d:%02d:%02d GMT",
7515 dayNames[t->tm_wday], t->tm_mday,
7516 monthNames[t->tm_mon],
7517 t->tm_year+1900, t->tm_hour,
7518 t->tm_min, t->tm_sec
7519 );
7520 assert( (rc>0) && ((unsigned int)rc) < destLen );
7521 return dest;
7522 }
7523 }
7524
7525 /**
7526 Outputs the cookie-specific HTTP headers.
7527
7528 Returns 0 on success.
7529 */
7530 static int cson_cgi_response_output_cookies(cson_cgi_cx * cx)
7531 {
7532 cson_kvp * kvp = NULL;
7533 cson_object * jo = NULL;
7534 cson_object_iterator iter = cson_object_iterator_empty;
7535 assert(cx);
7536 jo = cx->request.cookie.jobj;
7537 if( ! jo ) return 0;
7538 else
7539 {
7540 enum { CookieBufSize = 1024 * 8,
7541 ValBufSize = 1024 * 4,
7542 TSBufSize = 32
7543 };
7544 char cookieBuf[CookieBufSize] = {0} /* buffer for whole cookie string */;
7545 char valBuf[ValBufSize] = {0} /* buffer for value encoding */;
7546 char urlBuf[ValBufSize] = {0} /* buffer for urlencoding */;
7547 char tsBuf[TSBufSize] = {0} /* buffer for expiry timestamp */;
7548 int rc = cson_object_iter_init( jo, &iter );
7549 assert( CookieBufSize > ValBufSize );
7550 if( 0 != rc ) return rc;
7551 while( (kvp = cson_object_iter_next(&iter)) )
7552 {
7553 cson_string const * key = cson_kvp_key(kvp);
7554 cson_value const * val = cson_kvp_value(kvp);
7555 if( cson_value_is_null(val) )
7556 {
7557 cson_cgi_printf(cx,"Set-Cookie: %s=null; expires=Thu, 01-Jan-1970 00:00:01 GMT\r\n", cson_string_cstr(key));
7558 continue;
7559 }
7560 if( cson_value_is_object(val) )
7561 {
7562 /*
7563 Accept in Object in the form:
7564
7565 {
7566 value: VALUE,
7567 domain: string,
7568 path: string,
7569 secure: bool,
7570 httponly: bool,
7571 expires: integer
7572 }
7573 */
7574 cson_object const * obj = cson_value_get_object( val );
7575 cson_value const * cv = cson_object_get( obj, "value" );
7576 char const * valstr = NULL;
7577 char const isNull = !cv || cson_value_is_null( cv );
7578 if( isNull )
7579 {
7580 cson_cgi_printf(cx, "Set-Cookie: %s=0", cson_string_cstr(key));
7581 }
7582 else
7583 {
7584 /* FIXME: streamify urlencode so we can get around fixed buffer size. */
7585 valstr = cson_cgi_pod_to_string( cv, valBuf, ValBufSize );
7586 if( ! valstr ) continue;
7587 else
7588 {
7589 size_t bSize = ValBufSize;
7590 memset( urlBuf, 0, ValBufSize );
7591 if( 0 != cson_cgi_urlencode( valstr, urlBuf, &bSize ) )
7592 {
7593 /* buffer is too small. Skip it. */
7594 continue;
7595 }
7596 assert( bSize <= ValBufSize );
7597 cson_cgi_printf(cx, "Set-Cookie: %s=%s", cson_string_cstr(key), urlBuf);
7598 }
7599 }
7600
7601 #define DOPART(KEY,KEY2) cv = cson_object_get( obj, KEY ); \
7602 if( cv ) { \
7603 valstr = cson_cgi_pod_to_string( cv, valBuf, ValBufSize ); \
7604 if( valstr ) { \
7605 cson_cgi_printf( cx, "; "KEY2"=%s", valstr ); \
7606 } } (void)0
7607 DOPART("domain","Domain");
7608 DOPART("path","Path");
7609 #undef DOPART
7610
7611 cv = cson_object_get( obj, "expires" );
7612 if( cv || isNull )
7613 {
7614 cson_int_t const intVal = isNull ? 1 : cson_value_get_integer(cv);
7615 if( intVal )
7616 {
7617 valstr = cson_cgi_rfc822_timedate( (time_t)intVal, tsBuf, TSBufSize );
7618 if( valstr )
7619 {
7620 cson_cgi_printf( cx, "; Expires=%s", valstr );
7621 }
7622 }
7623 #if 0
7624 else if( cson_value_is_string(cv) )
7625 {
7626 /* TODO?: assume it's already propery formatted. */
7627 }
7628 else
7629 {
7630 /* skip it.*/
7631 }
7632 #endif
7633 }
7634 cv = cson_object_get( obj, "secure" );
7635 if( cson_value_get_bool(cv) )
7636 {
7637 cson_cgi_printf( cx, "; Secure" );
7638 }
7639
7640 cv = cson_object_get( obj, "httponly" );
7641 if( cson_value_get_bool(cv) )
7642 {
7643 cson_cgi_printf( cx, "; HttpOnly" );
7644 }
7645 cson_cgi_puts(cx, "\r");
7646 }
7647 else
7648 {
7649 char const * valstr;
7650 memset( valBuf, 0, ValBufSize );
7651 valstr = cson_cgi_pod_to_string( val, valBuf, ValBufSize );
7652 if( ! valstr ) continue;
7653 else
7654 {
7655 size_t bSize = CookieBufSize;
7656 memset( cookieBuf, 0, CookieBufSize );
7657 rc = cson_cgi_urlencode( valstr, cookieBuf, &bSize );
7658 if( 0 != rc )
7659 {
7660 /* too beaucoup. skip it */
7661 continue;
7662 }
7663 assert( bSize < CookieBufSize );
7664 cson_cgi_printf(cx,"Set-Cookie: %s=%s\r\n", cson_string_cstr(key), cookieBuf);
7665 }
7666 }
7667 }
7668 return 0;
7669 }
7670
7671 }
7672 int cson_cgi_response_output_headers(cson_cgi_cx * cx)
7673 {
7674 enum { BufSize = 64 };
7675 cson_object * jo = NULL;
7676 int rc;
7677 rc = cson_cgi_printf(cx, "Content-type: %s\r\n", cson_cgi_guess_content_type(cx) );
7678 if( rc <= 0 ) return rc;
7679 rc = cson_cgi_puts(cx, "Status: 200 OK\r");
7680 if( rc <= 0 ) return rc;
7681 jo = cx->response.headers.jobj;
7682 if( jo )
7683 {
7684 char buf[BufSize] = {0};
7685 cson_object_iterator iter = cson_object_iterator_empty;
7686 cson_kvp * kvp;
7687 cson_string const * key;
7688 cson_value const * val;
7689 char const * valcstr;
7690 rc = cson_object_iter_init( jo, &iter );
7691 if( 0 != rc ) return rc;
7692 while( (kvp = cson_object_iter_next(&iter)) )
7693 {
7694 key = cson_kvp_key(kvp);
7695 val = cson_kvp_value(kvp);
7696 valcstr = cson_cgi_pod_to_string( val, buf, BufSize );
7697 if( ! valcstr ) continue;
7698 assert( NULL != key );
7699 assert( NULL != val );
7700 cson_cgi_printf(cx, "%s: %s\r\n",
7701 cson_string_cstr(key),
7702 valcstr ? valcstr : "");
7703 }
7704 }
7705 rc = cson_cgi_response_output_cookies(cx);
7706 return rc;
7707 }
7708
7709 int cson_cgi_response_output_root(cson_cgi_cx * cx)
7710 {
7711 return ( !cx || !cx->response.root )
7712 ? cson_rc.ArgError
7713 : cson_output_FILE( cx->response.root, cx->opt.outStream, &cx->opt.outOpt );
7714 }
7715
7716 int cson_cgi_response_output_all(cson_cgi_cx * cx)
7717 {
7718 int rc = 0;
7719 char isJP = 0;
7720 char doHeaders = cx->opt.httpHeadersMode;
7721 if( NULL == cx->response.root )
7722 {
7723 return cson_rc.ArgError;
7724 }
7725 isJP = cson_cgi_is_jsonp(cx);
7726 if( doHeaders < 0 )
7727 {
7728 if( NULL!=getenv("GATEWAY_INTERFACE") )
7729 {
7730 doHeaders = 1;
7731 }
7732 }
7733 if( doHeaders > 0 )
7734 {
7735 rc = cson_cgi_response_output_headers(cx);
7736 if( 0 == rc )
7737 {
7738 cson_cgi_puts(cx,"\r")/*yes, putS, not putCHAR!*/;
7739 }
7740 else return rc;
7741 }
7742 if( isJP )
7743 {
7744 cson_cgi_printf(cx,"%s(", "FIXME_JSONP_CALLBACK_NAME" );
7745 }
7746 rc = cson_cgi_response_output_root(cx);
7747 if( 0 == rc )
7748 {
7749 if( isJP )
7750 {
7751 cson_cgi_putchar(cx,')');
7752 }
7753 cson_cgi_putchar(cx,'\n');
7754 fflush( cx->opt.outStream );
7755 }
7756 return rc;
7757 }
7758
7759 /**
7760 Parses inp as a delimited list, separated by the given
7761 separator character. Each item in the list is treated
7762 as a key/value pair in the form KEY=VALUE, and inserted
7763 into the target cson_object (which must not be NULL).
7764
7765 This is intended for parsing HTTP GET-style parameter lists.
7766
7767 If doUrlDecode is true (non-zero) then the VALUE part of the
7768 key/value pair gets url-decoded before insertion. (FIXME? Also
7769 decode the keys?)
7770
7771 If firstOneWins is non-0 then if a given key in the parameters is
7772 duplicated, entries after the first are ignored. If it is 0 then
7773 the "last one wins." This is basically a workaround for when we
7774 have multiple session ID cookies hanging around :/.
7775
7776 On success it returns 0.
7777
7778 If a given key contains the string "[]", that part is stripped and
7779 the entry is treated like an array element. e.g. a query string of
7780 "a[]=3&a[]=7" would result in an array property named "a" with the
7781 (string) entries ("3", "7").
7782
7783 */
7784 static int cson_cgi_parse_param_list( cson_cgi_cx * cx,
7785 cson_object * tgt,
7786 char const * inp,
7787 char separator,
7788 char doUrlDecode,
7789 char firstOneWins)
7790 {
7791 if( ! tgt || !separator ) return cson_rc.ArgError;
7792 else if( !inp || !*inp ) return 0;
7793 else
7794 {
7795 char const * head = inp;
7796 char const * tail = NULL;
7797 char * out = NULL;
7798 unsigned int inLen = strlen( inp );
7799 unsigned int valLen;
7800 cson_value * jval = NULL;
7801 cson_value * listV = NULL;
7802 cson_array * list = NULL;
7803 int rc = cson_buffer_reserve( &cx->tmpBuf, inLen+1 );
7804 if( 0 != rc ) return rc;
7805 while( cson_cgi_next_token( &head, separator, &tail ) )
7806 {
7807 char const * key = head;
7808 char * value = NULL;
7809 rc = 0;
7810 if( head == tail ) break;
7811 out = (char *)cx->tmpBuf.mem;
7812 memset( cx->tmpBuf.mem, 0, cx->tmpBuf.capacity );
7813 for( ; (key<tail) && *key && isspace(*key); ++key )
7814 {
7815 /* strip leading spaces in the key name
7816 (happens in cookie values). */
7817 }
7818 if( key==tail ) break;
7819 else if( '='==*key )
7820 {
7821 /* all-space key. Just skip it. */
7822 goto next_iter;
7823 }
7824 /* Write the key part to the buffer... */
7825 for( ; (key<tail) && *key && ('='!=*key); ++key ) {
7826 *(out++) = *key;
7827 }
7828 *(out++) = 0;
7829 if( '=' == *key )
7830 {
7831 ++key;
7832 }
7833 value = out;
7834 valLen = 0;
7835 /* Write the value part to the buffer... */
7836 for( ; (key<tail) && *key; ++key, ++valLen ) {
7837 *(out++) = *key;
7838 }
7839 key = (char const *)cx->tmpBuf.mem;
7840 if( firstOneWins && (NULL != cson_object_get( tgt, key )) )
7841 {
7842 goto next_iter;
7843 }
7844 if( doUrlDecode && valLen )
7845 {
7846 cson_cgi_urldecode_inline( value );
7847 }
7848 /*MARKER("key=[%s], valLen=%u, value=[%s]\n", key, valLen, value );*/
7849 jval = cson_value_new_string( value, valLen );
7850 if( NULL == jval )
7851 {
7852 rc = cson_rc.AllocError;
7853 goto the_end;
7854 }
7855 if( NULL != (out = strstr(key,"[]")) )
7856 { /* Treat key as an array entry, like PHP does... */
7857 cson_value * freeThisOnErr = NULL;
7858 *out = 0;
7859 list = NULL;
7860 listV = cson_object_get( tgt, key );
7861 if( listV )
7862 {
7863 if( ! cson_value_is_array( listV ) )
7864 {
7865 /* skip it to avoid hosing a different entry. */
7866 cson_value_free( jval );
7867 jval = NULL;
7868 goto next_iter;
7869 }
7870 }
7871 else
7872 { /* create a new array to hold the value */
7873 listV = cson_value_new_array();
7874 if( ! listV )
7875 {
7876 cson_value_free( jval );
7877 rc = cson_rc.AllocError;
7878 goto the_end;
7879 }
7880 rc = cson_object_set( tgt, key, listV );
7881 if( 0 != rc )
7882 {
7883 cson_value_free( listV );
7884 cson_value_free( jval );
7885 goto the_end;
7886 }
7887 freeThisOnErr = listV;
7888 }
7889 list = cson_value_get_array( listV );
7890 assert( NULL != list );
7891 rc = cson_array_append( list, jval );
7892 if( 0 != rc )
7893 {
7894 cson_value_free( jval );
7895 cson_value_free( freeThisOnErr );
7896 goto the_end;
7897 }
7898 }
7899 else
7900 {
7901 rc = cson_object_set( tgt, key, jval );
7902 if( 0 != rc )
7903 {
7904 cson_value_free( jval );
7905 goto the_end;
7906 }
7907 }
7908 next_iter:
7909 head = tail;
7910 tail = NULL;
7911 }
7912 the_end:
7913 cson_buffer_reserve( &cx->tmpBuf, 0 );
7914 return rc;
7915 }
7916 }
7917
7918
7919 /**
7920 Parses key/value pairs from a QUERY_STRING-formatted
7921 string.
7922
7923 Returns 0 on success. The "most likely" error condition, in terms
7924 of potential code paths, is is an allocation error.
7925
7926 TODO: if the key part of any entry ends with "[]", treat it as an
7927 array entry, like PHP does.
7928 */
7929 static int cson_cgi_parse_query_string( cson_cgi_cx * cx, char const * qstr )
7930 {
7931 cson_object * env = NULL;
7932 if( !qstr || !*qstr ) return 0;
7933 assert(cx);
7934 env = cson_cgi_env_get_obj( cx, 'g', 1 );
7935 if( NULL == env ) return cson_rc.AllocError /* guess! */;
7936 return cson_cgi_parse_param_list( cx, env, qstr, '&', 1, 0 );
7937 }
7938
7939 #if CSON_CGI_ENABLE_POST_FORM_URLENCODED
7940 static int cson_cgi_parse_post_urlencoded( cson_cgi_cx * cx, char const * qstr )
7941 {
7942 cson_object * env = NULL;
7943 if( !qstr || !*qstr ) return 0;
7944 assert(cx);
7945 env = cson_cgi_env_get_obj( cx, 'p', 1 );
7946 if( NULL == env ) return cson_rc.AllocError /* guess! */;
7947 return cson_cgi_parse_param_list( cx, env, qstr, '&', 1, 0 );
7948 }
7949 #endif
7950
7951 /**
7952 Like cson_cgi_parse_query_string(), but expects qstr to be in COOKIE
7953 format.
7954 */
7955 static int cson_cgi_parse_cookies( cson_cgi_cx * cx, char const * qstr )
7956 {
7957 cson_object * env = NULL;
7958 if( !qstr || !*qstr ) return 0;
7959 assert(cx);
7960 env = cson_cgi_env_get_obj(cx, 'c', 1 );
7961 if( NULL == env ) return cson_rc.AllocError /* guess! */;
7962 return cson_cgi_parse_param_list( cx, env, qstr, ';', 1, 1 );
7963 }
7964
7965
7966 /**
7967 Initializes cx->argv.jval and cx->argv.jarr, adds them to the
7968 garbage collector, then copies argv to cx->argv.jarr as an
7969 array of JSON strings.
7970
7971 Returns 0 on success.
7972
7973 Results are undefined if argv is not a properly initialized array
7974 of NUL-terminated strings with at least argc entries.
7975
7976 If argc is 0 or less then cx->argv is still initialized but has
7977 a length of 0.
7978
7979 After the first call, further arguments are appended to the current
7980 list.
7981 */
7982 static int cson_cgi_init_argv( cson_cgi_cx * cx, int argc, char const * const * argv )
7983 {
7984 int rc = 0;
7985 int i;
7986 assert( NULL != cx->gc.jobj );
7987 if( cx->argv.jval == NULL )
7988 {
7989 cson_value * v = cson_value_new_array();
7990 if( NULL == v ) return cson_rc.AllocError;
7991 rc = cson_cgi_gc_add( cx, cson_cgi_keys.ENV_ARGV, v, 1 );
7992 if( 0 != rc )
7993 {
7994 /* reminder: v was freed by cson_cgi_gc_add() */
7995 return rc;
7996 }
7997 cx->argv.jval = v;
7998 cx->argv.jarr = cson_value_get_array( v );
7999 assert( NULL != cx->argv.jarr );
8000 }
8001 for( i = 0; i < argc; ++i )
8002 {
8003 char const * arg = argv[i];
8004 cson_value * vstr = cson_value_new_string( arg ? arg : "",
8005 arg ? strlen(arg) : 0 );
8006 if( NULL == vstr ) return cson_rc.AllocError;
8007 rc = cson_array_append( cx->argv.jarr, vstr );
8008 if( 0 != rc )
8009 {
8010 cson_value_free( vstr );
8011 break;
8012 }
8013 }
8014 return rc;
8015 }
8016
8017 typedef struct CgiPostReadState_ {
8018 FILE * fh;
8019 unsigned int len;
8020 unsigned int pos;
8021 } CgiPostReadState;
8022
8023 static int cson_data_source_FILE_n( void * state, void * dest, unsigned int * n )
8024 {
8025 if( ! state || !dest || !n ) return cson_rc.ArgError;
8026 else
8027 {
8028 CgiPostReadState * st = (CgiPostReadState *)state;
8029 if( st->pos >= st->len )
8030 {
8031 *n = 0;
8032 return 0;
8033 }
8034 else if( !*n || ((st->pos + *n) > st->len) ) return cson_rc.RangeError;
8035 else
8036 {
8037 unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh );
8038 if( ! rsz )
8039 {
8040 *n = rsz;
8041 return feof(st->fh) ? 0 : cson_rc.IOError;
8042 }
8043 else
8044 {
8045 *n = rsz;
8046 st->pos += *n;
8047 return 0;
8048 }
8049 }
8050 }
8051 }
8052
8053
8054 static int cson_cgi_parse_POST_JSON(cson_cgi_cx * cx, FILE * src, unsigned int contentLen)
8055 {
8056 cson_value * jv = NULL;
8057 int rc = 0;
8058 CgiPostReadState state;
8059 cson_parse_info pinfo = cson_parse_info_empty;
8060 assert( 0 != contentLen );
8061 assert( NULL == cx->request.post.jval );
8062 state.fh = src;
8063 state.len = contentLen;
8064 state.pos = 0;
8065 rc = cson_parse( &jv, cson_data_source_FILE_n, &state, NULL, &pinfo );
8066 if( rc )
8067 {
8068 #if 0
8069 fprintf(stderr, "%s: Parsing POST as JSON failed: code=%d (%s) line=%u, col=%u\n",
8070 __FILE__, rc, cson_rc_string(rc), pinfo.line, pinfo.col );
8071 #endif
8072 return rc;
8073 }
8074 rc = cson_cgi_gc_add( cx, cson_cgi_keys.ENV_POST, jv, 1 );
8075 if( 0 == rc )
8076 {
8077 cx->request.post.jval = jv;
8078 cx->request.post.jobj = cson_value_get_object( jv );
8079 assert( cx->request.post.jobj && "FIXME: also support an Array as POST data node." );
8080 }
8081 return rc;
8082 }
8083
8084 static int cson_cgi_init_POST(cson_cgi_cx * cx)
8085 {
8086 if( ! cx || !cx->opt.inStream ) return cson_rc.ArgError;
8087 else
8088 {
8089 FILE * src = cx->opt.inStream;
8090 char const * ctype = cson_string_cstr( cson_value_get_string( cson_cgi_getenv( cx, "e", "CONTENT_TYPE" ) ) );
8091 if( NULL == ctype ) return 0;
8092 else
8093 {
8094 char const * clen = cson_string_cstr( cson_value_get_string( cson_cgi_getenv( cx, "e", "CONTENT_LENGTH" ) ) );
8095 if( NULL == clen ) return cson_rc.ArgError;
8096 else
8097 {
8098 char * endpt = NULL;
8099 long len = strtol( clen, &endpt, 10 );
8100 if( (endpt && *endpt) || (len<=0) ) return cson_rc.RangeError;
8101 #if CSON_CGI_ENABLE_POST_FORM_URLENCODED
8102 else if( 0 == strncmp(ctype,"application/x-www-form-urlencoded",33) )
8103 {
8104 cson_buffer buf = cson_buffer_empty;
8105 int rc = cson_buffer_fill_from( &buf, cson_data_source_FILE, src );
8106 if( rc )
8107 {
8108 goto end_clean;
8109 return rc;
8110 }
8111 if( buf.mem && buf.used )
8112 {
8113 #if 1
8114 if( strlen((char const *)buf.mem)
8115 != buf.used )
8116 {
8117 /* assume bad/malicious input. */
8118 rc = cson_rc.RangeError;
8119 goto end_clean;
8120 }
8121 #endif
8122 rc = cson_cgi_parse_post_urlencoded( cx, (char const *)buf.mem );
8123 }
8124 end_clean:
8125 cson_buffer_reserve( &buf, 0 );
8126 return rc;
8127 }
8128 #endif
8129 else if( (0 == strncmp(ctype,"application/json",16))
8130 || (0 == strncmp(ctype,"text/plain",10))
8131 || (0 == strncmp(ctype,"application/javascript",22))
8132 )
8133 {
8134 return cson_cgi_parse_POST_JSON(cx, src, len);
8135 }
8136 else
8137 {
8138 return cson_rc.TypeError;
8139 }
8140 }
8141 }
8142 }
8143 }
8144
8145 static int cson_cgi_init_config( cson_cgi_cx * cx, char const * fname )
8146 {
8147 int rc;
8148 cson_value * root = NULL;
8149 rc = cson_parse_filename( &root, fname, NULL, NULL );
8150 if( 0 == rc )
8151 {
8152 assert( NULL != root );
8153 if( ! cson_value_is_object(root) )
8154 {
8155 cson_value_free( root );
8156 rc = cson_rc.TypeError;
8157 }
8158 else
8159 {
8160 rc = cson_cgi_gc_add( cx,cson_cgi_keys.ENV_CONFIG, root, 1 );
8161 if( 0 == rc )
8162 {
8163 cx->config.jval = root;
8164 cx->config.jobj = cson_value_get_object( root );
8165 assert( NULL != cx->config.jobj );
8166 }
8167 }
8168 }
8169 return rc;
8170 }
8171
8172 static char * cson_cgi_strdup( char const * src )
8173 {
8174 size_t const n = src ? strlen(src) : 0;
8175 char * rc = src ? (char *)malloc(n+1) : NULL;
8176 if( ! rc ) return NULL;
8177 memcpy( rc, src, n );
8178 rc[n] = 0;
8179 return rc;
8180 }
8181
8182 /**
8183 Writes a 36-byte (plus one NUL byte) UUID value to dest. dest
8184 must be at least 37 bytes long. If dest is NULL this function
8185 has no side effects.
8186
8187 Not thread-safe.
8188 */
8189 void cson_cgi_generate_uuid( cson_cgi_cx * cx, char * dest )
8190 {
8191 static whuuid_rng rng = {
8192 NULL/*rand*/,
8193 NULL/*cleanup*/,
8194 NULL/*impl*/
8195 #if WHUUID_CONFIG_KEEP_METRICS
8196 ,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}/*distribution*/
8197 #endif
8198 };
8199 whuuid_t u = whuuid_t_empty;
8200 if( NULL == dest ) return;
8201 else if( (NULL==rng.rand) && (NULL != RNG_FILENAME) )
8202 { /* try to open rng file... */
8203 /* FIXME: we're missing a cleanup handler for the RNG_FILENAME case. */
8204 FILE * f = fopen(RNG_FILENAME, "rb");
8205 if( NULL != f )
8206 {
8207 rng = whuuid_rng_FILE;
8208 rng.impl = f;
8209 }
8210 }
8211 if( NULL == rng.rand )
8212 { /* fall back to LC rng */
8213 extern char ** environ;
8214 void * addr;
8215 unsigned long seed;
8216 rng = whuuid_rng_lcrng;
8217 addr = malloc(
8218 (((unsigned long)environ) % 13) + 9
8219 );
8220 free(addr) /* but keep the address as a seed value */;
8221 seed = (unsigned long)addr * (unsigned long)time(NULL);
8222 rng.impl = (void *)seed;
8223 }
8224 whuuid_fill_rand( &u, &rng );
8225 whuuid_to_string( &u, dest );
8226 }
8227
8228 char const * cson_cgi_session_id(cson_cgi_cx * cx)
8229 {
8230 return cx ? cx->session.id : NULL;
8231 }
8232
8233
8234 static int cson_cgi_init_session_mgr(cson_cgi_cx * cx)
8235 {
8236 /*
8237 Check for this config structure:
8238
8239 {
8240 manager:"mgrID",
8241 managers:{
8242 mgrID:{
8243 sessionDriver: "back-end-name" (e.g. "cpdo" or "file"),
8244 ... back-end-specific options ...
8245 },
8246 otherManager: { ... }
8247 }
8248 */
8249 cson_object const * conf = cson_cgi_env_get_obj(cx, 'f', 0 );
8250 cson_string const * aString;
8251 cson_value const * optV = NULL;
8252 cson_object const * optObj = NULL;
8253 if( NULL == conf ) return 0;
8254 assert( cx && !cx->session.mgr );
8255
8256 /* get "manager" part... */
8257 aString = cson_value_get_string( cson_object_get_sub( conf, "session.manager", '.' ) );
8258 if( NULL == aString ) return 0;
8259
8260 /* Fetch that manager config ... */
8261 optV = cson_object_get_sub( conf, "session.managers", '.' );
8262 if( optV )
8263 {
8264 optV = cson_object_get( cson_value_get_object( optV ), cson_string_cstr( aString ) );
8265 }
8266 optObj = cson_value_get_object( optV );
8267 if( ! optObj ) return 0;
8268
8269 /* Get the "sessionDriver" part ... */
8270 aString = cson_value_get_string( cson_object_get( optObj, "sessionDriver" ) );
8271 if( NULL == aString ) return 0;
8272
8273 return cson_sessmgr_load( cson_string_cstr(aString), &cx->session.mgr, optObj );
8274 }
8275
8276
8277 static char const * cson_cgi_get_session_key(cson_cgi_cx * cx)
8278 {
8279 cson_object const * conf = cson_cgi_env_get_obj( cx, 'f', 0 );
8280 char const * sessKey = CSON_CGI_KEY_SESSION;
8281 assert( NULL != cx );
8282 if( conf )
8283 {
8284 cson_string const * k = cson_value_get_string( cson_object_get_sub( conf, "session.cookieName", '.' ) );
8285 char const * ck = k ? cson_string_cstr(k) : NULL;
8286 if( ck ) sessKey = ck;
8287 }
8288 return sessKey;
8289 }
8290
8291 static int cson_cgi_gen_session_id(cson_cgi_cx * cx)
8292 {
8293 char buf[37] = {0};
8294 if( cx->session.id )
8295 {
8296 free( cx->session.id );
8297 cx->session.id = NULL;
8298 }
8299 cson_cgi_generate_uuid( cx, buf );
8300 cx->session.id = cson_cgi_strdup( buf );
8301 return ( NULL == cx->session.id )
8302 ? cson_rc.AllocError
8303 : 0;
8304 }
8305
8306 static int cson_cgi_init_session( cson_cgi_cx * cx, char const * forceID )
8307 {
8308 char const * idstr;
8309 char const * sessKey;
8310 int rc = cson_cgi_init_session_mgr(cx);
8311 if( 0 != rc ) return rc;
8312 else if( NULL == cx->session.mgr ) return 0
8313 /* treat as non-fatal error */;
8314 sessKey = cson_cgi_get_session_key(cx);
8315 assert( sessKey && *sessKey );
8316 /* Try to get the session ID ... */
8317 idstr = (forceID && *forceID)
8318 ? forceID
8319 : cson_string_cstr( cson_value_get_string( cson_cgi_getenv( cx, "cegp", sessKey ) ) );
8320 if( NULL == idstr )
8321 { /* Generate a session ID but defer creation of the session
8322 object until the client does it. If they never use it,
8323 we won't bother saving the session.
8324 */
8325 rc = cson_cgi_gen_session_id(cx);
8326 if( 0 != rc ) return rc;
8327 }
8328 else
8329 { /* try to load the session */
8330 cson_value * sessV = NULL;
8331 free( cx->session.id );
8332 cx->session.id = cson_cgi_strdup( idstr );
8333 if( ! cx->session.id ) return cson_rc.AllocError;
8334 rc = cx->session.mgr->api->load( cx->session.mgr, &sessV,
8335 cx->session.id );
8336 if( (0 == rc) && sessV )
8337 {
8338 rc = cson_cgi_gc_add( cx, cson_cgi_keys.ENV_SESSION, sessV, 1 );
8339 if( 0 != rc )
8340 { /* almost certainly an alloc error */
8341 return rc;
8342 }
8343 cx->session.env.jval = sessV;
8344 cx->session.env.jobj = cson_value_get_object( sessV );
8345 }
8346 else
8347 {
8348 if( !forceID || !*forceID )
8349 {
8350 /* On load error, assume the session ID is
8351 stale. Re-generate it to avoid potential future
8352 collisions. This heuristic will cause us intermittent
8353 grief when loading does not work for a second or three
8354 due to network-related problems. Each time that
8355 happens, the caller will lose his session.
8356 */
8357 rc = cson_cgi_gen_session_id(cx);
8358 if( 0 != rc ) return rc;
8359 }
8360 }
8361 }
8362 assert( NULL != cx->session.id );
8363 { /* make sure the session ID is set in the cookies and has an updated
8364 expiry time... */
8365 unsigned int expiry = 0;
8366 cson_object const * conf;
8367 cson_value * jstr = cson_value_new_string( cx->session.id,
8368 strlen(cx->session.id) );
8369 if( ! jstr ) return cson_rc.AllocError;
8370 conf = cson_cgi_env_get_obj( cx, 'f', 0 );
8371 if( conf )
8372 {
8373 expiry = cson_value_get_integer( cson_object_get_sub( conf, "session.cookieLifetimeMinutes", '.' ) );
8374 if( expiry ) expiry *= 60 /* convert to seconds */;
8375 }
8376 if( ! expiry )
8377 {
8378 expiry = (60*60*24);
8379 }
8380 expiry += (unsigned int)time(NULL);
8381
8382 rc = cson_cgi_cookie_set2( cx, sessKey, jstr,
8383 NULL, NULL,
8384 expiry,
8385 0/*FIXME: set 'secure' option in HTTPS mode.*/,
8386 0/*FIXME: make the httponly flag configurable*/ );
8387 if( 0 != rc )
8388 {
8389 cson_value_free( jstr );
8390 if( cson_rc.AllocError == rc ) return rc;
8391 rc = 0 /* else treat as non-fatal */;
8392 }
8393 }
8394 return 0;
8395 }
8396
8397
8398
8399 int cson_cgi_init(cson_cgi_cx * cx, int argc, char const * const * argv, cson_cgi_init_opt * opt )
8400 {
8401 int rc = 0;
8402 static int hasInited = 0;
8403 if( NULL == cx ) return cson_rc.ArgError;
8404 else if( NULL != cx->gc.jval )
8405 { /* we've already done this or object was mal-initialized... */
8406 return cson_rc.ArgError;
8407 }
8408
8409 assert( NULL != CSON_CGI_GETENV_DEFAULT );
8410
8411 #if CSON_CGI_USE_SIGNALS
8412 {
8413 /* FIXME: use sigaction() instead of signal() */
8414 typedef void (*sighnd)(int);
8415 sighnd oldSigPipe;
8416 oldSigPipe = signal(SIGPIPE, SIG_IGN) /* to try avoid unclean termination if client disconnects. */;
8417 if( SIG_ERR == oldSigPipe )
8418 {
8419 return cson_rc.UnknownError;
8420 }
8421 }
8422 #endif
8423
8424 if( ! hasInited )
8425 {
8426 hasInited = 1;
8427 setlocale( LC_ALL, "C" )
8428 /* supposedly important for underlying JSON parser.
8429 FIXME: only do this init once!
8430 */;
8431 }
8432
8433 cx->gc.jval = cson_value_new_object();
8434 if( NULL == cx->gc.jval )
8435 {
8436 return cson_rc.AllocError;
8437 }
8438 cx->gc.jobj = cson_value_get_object( cx->gc.jval );
8439 assert( NULL != cx->gc.jobj );
8440
8441 if( opt )
8442 {
8443 cx->opt = *opt;
8444 }
8445 if( NULL == cx->opt.inStream ) cx->opt.inStream = stdin;
8446 if( NULL == cx->opt.outStream ) cx->opt.outStream = stdout;
8447 if( NULL == cx->opt.errStream ) cx->opt.errStream = stderr;
8448
8449 #define CHECKRC if(rc) goto end
8450 rc = cson_cgi_import_environ(cx);
8451 CHECKRC;
8452 rc = cson_cgi_init_argv( cx, argc, argv );
8453 CHECKRC;
8454 { /* read config file */
8455 char const * conffile = cx->opt.configFile;
8456 if( ! conffile )
8457 {
8458 cson_value const * v = cson_cgi_getenv( cx, "e", "CSON_CGI_CONFIG" );
8459 if( v && cson_value_is_string(v) )
8460 {
8461 conffile = cson_string_cstr( cson_value_get_string( v ) );
8462 }
8463 }
8464 if( conffile )
8465 {
8466 cson_cgi_init_config( cx, conffile )
8467 /* Ignore error code.
8468
8469 TODO:
8470
8471 - use argv[0]+".json" as the default config file.
8472 */
8473 ;
8474 }
8475 }
8476
8477 rc = cson_cgi_parse_query_string( cx, getenv("QUERY_STRING") );
8478 CHECKRC;
8479 rc = cson_cgi_parse_cookies( cx, getenv("HTTP_COOKIE") );
8480 CHECKRC;
8481 rc = cson_cgi_init_POST(cx);
8482 if( cson_rc.AllocError == rc ) goto end;
8483 else rc = 0
8484 /* this can fail for several reasons which are non-fatal. */
8485 ;
8486
8487 if( (NULL == opt) )
8488 {
8489 /* TODO: read these values from cx->config, if available. */
8490 cx->opt.outOpt.indentation = 1;
8491 cx->opt.outOpt.addNewline = 1;
8492 cx->opt.outOpt.addSpaceAfterColon = 1;
8493 cx->opt.outOpt.indentSingleMemberValues = 1;
8494 }
8495
8496 rc = cson_cgi_init_session( cx, opt ? opt->sessionID : NULL )
8497 /* ignore non-OOM error codes. Not fatal. */;
8498 if( cson_rc.AllocError == rc ) goto end;
8499 else rc = 0;
8500
8501 /*
8502 TODOs:
8503
8504 - Read form-urlencoded POST data. (Do this BEFORE
8505 restoring the session, so that we can get the session
8506 ID from there if needed.)
8507 */
8508 end:
8509 return rc;
8510 #undef CHECKRC
8511 }
8512
8513 #undef cson_cgi_env_map_empty_m
8514 #undef CSON_CGI_USE_SIGNALS
8515 #undef RNG_FILENAME
8516 /* end file cgi/cson_cgi.c */
8517
--- src/cson_amalgamation.c
+++ src/cson_amalgamation.c
@@ -5133,384 +5133,10 @@
5133 }
5134 }
5135 cson_kvp_list_reserve(self,0);
5136 }
5137 /* end file ./cson_lists.h */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5138 /* begin file ./cson_sqlite3.c */
5139 /** @file cson_sqlite3.c
5140
5141 This file contains the implementation code for the cson
5142 sqlite3-to-JSON API.
@@ -5532,11 +5158,11 @@
5158
5159 #if defined(__cplusplus)
5160 extern "C" {
5161 #endif
5162
5163 cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col )
5164 {
5165 if( ! st ) return NULL;
5166 else
5167 {
5168 #if 0
@@ -5546,39 +5172,29 @@
5172 #else
5173 int const vtype = sqlite3_column_type(st,col);
5174 #endif
5175 switch( vtype )
5176 {
5177 case SQLITE_NULL:
5178 return cson_value_null();
5179 case SQLITE_INTEGER:
5180 /* FIXME: for large integers fall back to Double instead. */
5181 return cson_value_new_integer( (cson_int_t) sqlite3_column_int64(st, col) );
5182 case SQLITE_FLOAT:
5183 return cson_value_new_double( sqlite3_column_double(st, col) );
5184 case SQLITE_BLOB: /* arguably fall through... */
5185 case SQLITE_TEXT: {
5186 char const * str = (char const *)sqlite3_column_text(st,col);
5187 return cson_value_new_string(str, str ? strlen(str) : 0);
5188 }
5189 default:
5190 return NULL;
5191 }
5192 }
5193 }
5194
5195 cson_value * cson_sqlite3_column_names( sqlite3_stmt * st )
 
 
 
 
 
 
 
 
 
 
 
5196 {
5197 cson_value * aryV = NULL;
5198 cson_array * ary = NULL;
5199 char const * colName = NULL;
5200 int i = 0;
@@ -5608,10 +5224,78 @@
5224 cson_value_free(aryV);
5225 return NULL;
5226 }
5227 }
5228
5229
5230 cson_value * cson_sqlite3_row_to_object( sqlite3_stmt * st )
5231 {
5232 cson_value * rootV = NULL;
5233 cson_object * root = NULL;
5234 char const * colName = NULL;
5235 int i = 0;
5236 int rc = 0;
5237 cson_value * currentValue = NULL;
5238 int const colCount = sqlite3_column_count(st);
5239 if( !colCount ) return NULL;
5240 rootV = cson_value_new_object();
5241 if(!rootV) return NULL;
5242 root = cson_value_get_object(rootV);
5243 for( i = 0; i < colCount; ++i )
5244 {
5245 colName = sqlite3_column_name( st, i );
5246 if( ! colName ) goto error;
5247 currentValue = cson_sqlite3_column_to_value(st,i);
5248 if( ! currentValue ) currentValue = cson_value_null();
5249 rc = cson_object_set( root, colName, currentValue );
5250 if( 0 != rc )
5251 {
5252 cson_value_free( currentValue );
5253 goto error;
5254 }
5255 }
5256 goto end;
5257 error:
5258 cson_value_free( rootV );
5259 rootV = NULL;
5260 end:
5261 return rootV;
5262 }
5263
5264 cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st )
5265 {
5266 cson_value * aryV = NULL;
5267 cson_array * ary = NULL;
5268 int i = 0;
5269 int rc = 0;
5270 int const colCount = sqlite3_column_count(st);
5271 if( ! colCount ) return NULL;
5272 aryV = cson_value_new_array();
5273 if( ! aryV ) return NULL;
5274 ary = cson_value_get_array(aryV);
5275 rc = cson_array_reserve(ary, (unsigned int) colCount );
5276 if( 0 != rc ) goto error;
5277
5278 for( i = 0; i < colCount; ++i ){
5279 cson_value * elem = cson_sqlite3_column_to_value(st,i);
5280 if( ! elem ) goto error;
5281 rc = cson_array_append(ary,elem);
5282 if(0!=rc)
5283 {
5284 cson_value_free( elem );
5285 goto end;
5286 }
5287 }
5288 goto end;
5289 error:
5290 cson_value_free(aryV);
5291 aryV = NULL;
5292 end:
5293 return aryV;
5294 }
5295
5296
5297 /**
5298 Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
5299 parameter is non-0.
5300 */
5301 static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
@@ -5624,20 +5308,16 @@
5308 cson_object * root = NULL;
5309 cson_value * colsV = NULL;
5310 cson_value * rowsV = NULL;
5311 cson_array * rows = NULL;
5312 cson_value * objV = NULL;
 
 
 
5313 int rc = 0;
 
5314 int const colCount = sqlite3_column_count(st);
5315 if( colCount <= 0 ) return cson_rc.ArgError;
5316 rootV = cson_value_new_object();
5317 if( ! rootV ) return cson_rc.AllocError;
5318 colsV = cson_sqlite3_column_names(st);
5319 if( ! colsV )
5320 {
5321 cson_value_free( rootV );
5322 RETURN(cson_rc.AllocError);
5323 }
@@ -5646,13 +5326,11 @@
5326 if( rc )
5327 {
5328 cson_value_free( colsV );
5329 RETURN(rc);
5330 }
 
5331 colsV = NULL;
 
5332 rowsV = cson_value_new_array();
5333 if( ! rowsV ) RETURN(cson_rc.AllocError);
5334 rc = cson_object_set( root, "rows", rowsV );
5335 if( rc )
5336 {
@@ -5661,32 +5339,18 @@
5339 }
5340 rows = cson_value_get_array(rowsV);
5341 assert(rows);
5342 while( SQLITE_ROW == sqlite3_step(st) )
5343 {
5344 objV = cson_sqlite3_row_to_object(st);
5345 if( ! objV ) RETURN(cson_rc.UnknownError);
5346 rc = cson_array_append( rows, objV );
5347 if( rc )
5348 {
5349 cson_value_free( objV );
5350 RETURN(rc);
5351 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5352 }
5353 *tgt = rootV;
5354 return 0;
5355 }
5356 #undef RETURN
@@ -5706,18 +5370,16 @@
5370 cson_object * root = NULL;
5371 cson_value * aryV = NULL;
5372 cson_array * ary = NULL;
5373 cson_value * rowsV = NULL;
5374 cson_array * rows = NULL;
 
5375 int rc = 0;
 
5376 int const colCount = sqlite3_column_count(st);
5377 if( colCount <= 0 ) return cson_rc.ArgError;
5378 rootV = cson_value_new_object();
5379 if( ! rootV ) return cson_rc.AllocError;
5380 aryV = cson_sqlite3_column_names(st);
5381 if( ! aryV )
5382 {
5383 cson_value_free( rootV );
5384 RETURN(cson_rc.AllocError);
5385 }
@@ -5740,32 +5402,18 @@
5402 }
5403 rows = cson_value_get_array(rowsV);
5404 assert(rows);
5405 while( SQLITE_ROW == sqlite3_step(st) )
5406 {
5407 aryV = cson_sqlite3_row_to_array(st);
5408 if( ! aryV ) RETURN(cson_rc.UnknownError);
5409 rc = cson_array_append( rows, aryV );
5410 if( 0 != rc )
5411 {
5412 cson_value_free( aryV );
5413 RETURN(rc);
5414 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5415 }
5416 *tgt = rootV;
5417 return 0;
5418 }
5419 #undef RETURN
@@ -5797,2721 +5445,5 @@
5445 } /*extern "C"*/
5446 #endif
5447 #undef MARKER
5448 #endif /* CSON_ENABLE_SQLITE3 */
5449 /* end file ./cson_sqlite3.c */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
-------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5450
--- src/cson_amalgamation.h
+++ src/cson_amalgamation.h
@@ -2044,10 +2044,59 @@
20442044
20452045
#if defined(__cplusplus)
20462046
extern "C" {
20472047
#endif
20482048
2049
+/**
2050
+ Converts a single value from a single 0-based column index to its JSON
2051
+ equivalent.
2052
+
2053
+ On success it returns a new JSON value, which will have a different concrete
2054
+ type depending on the field type reported by sqlite3_column_type(st,col):
2055
+
2056
+ Integer, double, null, or string (TEXT and BLOB data, though not
2057
+ all blob data is legal for a JSON string).
2058
+
2059
+ st must be a sqlite3_step()'d row and col must be a 0-based column
2060
+ index within that result row.
2061
+ */
2062
+cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col );
2063
+
2064
+/**
2065
+ Creates a JSON Array object containing the names of all columns
2066
+ of the given prepared statement handle.
2067
+
2068
+ Returns a new array value on success, which the caller owns. Its elements
2069
+ are in the same order as in the underlying query.
2070
+
2071
+ On error NULL is returned.
2072
+
2073
+ st is not traversed or freed by this function - only the column
2074
+ count and names are read.
2075
+*/
2076
+cson_value * cson_sqlite3_column_names( sqlite3_stmt * st );
2077
+
2078
+/**
2079
+ Creates a JSON Object containing key/value pairs corresponding
2080
+ to the result columns in the current row of the given statement
2081
+ handle. st must be a sqlite3_step()'d row result.
2082
+
2083
+ On success a new Object is returned which is owned by the
2084
+ caller. On error NULL is returned.
2085
+
2086
+ cson_sqlite3_column_to_value() is used to convert each column to a
2087
+ JSON value, and the column names are taken from
2088
+ sqlite3_column_name().
2089
+*/
2090
+cson_value * cson_sqlite3_row_to_object( sqlite3_stmt * st );
2091
+
2092
+/**
2093
+ Similar to cson_sqlite3_row_to_object(), but creates an Array
2094
+ value which contains the JSON-form values of the given result
2095
+ set row.
2096
+*/
2097
+cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st );
20492098
/**
20502099
Converts the results of an sqlite3 SELECT statement to JSON,
20512100
in the form of a cson_value object tree.
20522101
20532102
st must be a prepared, but not yet traversed, SELECT query.
@@ -2145,1586 +2194,5 @@
21452194
#endif
21462195
21472196
#endif /* CSON_ENABLE_SQLITE3 */
21482197
#endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */
21492198
/* end file include/wh/cson/cson_sqlite3.h */
2150
-/* begin file include/wh/cson/cson_session.h */
2151
-#if !defined(WANDERINGHORSE_NET_CSON_SESSION_H_INCLUDED)
2152
-#define WANDERINGHORSE_NET_CSON_SESSION_H_INCLUDED 1
2153
-
2154
-/** @page page_cson_session cson Session API
2155
-
2156
-The cson_session API provides a small interface,
2157
-called cson_sessmgr, which defines the basic operations
2158
-needed for implementent persistent application state,
2159
-across application sessions, by storing the state as
2160
-JSON data in "some back-end storage." The exact underlying
2161
-storage is not specified by the interface, but two
2162
-implementations are provided by the library:
2163
-
2164
-- File-based sessions.
2165
-
2166
-- Database-based sessions, using libcpdo for connection
2167
-abstraction.
2168
-
2169
-libcpdo is included, in full, in the cson source tree,
2170
-but can also be found on its web page:
2171
-
2172
- http://fossil.wanderinghorse.net/repos/cpdo/
2173
-
2174
-@see cson_sessmgr_register()
2175
-@see cson_sessmgr_load()
2176
-@see cson_sessmgr_names()
2177
-@see cson_sessmgr
2178
-@see cson_sessmgr_api
2179
-*/
2180
-
2181
-
2182
-#if defined(__cplusplus)
2183
-extern "C" {
2184
-#endif
2185
-
2186
- typedef struct cson_sessmgr cson_sessmgr;
2187
- typedef struct cson_sessmgr_api cson_sessmgr_api;
2188
-
2189
- /** @struct cson_sessmgr_api
2190
-
2191
- Defines operations required by "session managers." Session managers
2192
- are responsible for loading and saving cson session information
2193
- in the form of JSON data.
2194
-
2195
- @see cson_sessmgr
2196
- */
2197
- struct cson_sessmgr_api
2198
- {
2199
- /**
2200
- Loads/creates a session object (JSON data). The
2201
- implementation must use the given identifier for loading an
2202
- existing session, creating the session if createIfNeeded is
2203
- true and the session data is not found. If createIfNeeded
2204
- is true then the implementation must create an Object for
2205
- the session root, as opposed to an Array or other JSON
2206
- value. Clients are allowed to use non-Objects as their
2207
- sessions but doing so would seem to have no benefit, and is
2208
- not recommended.
2209
-
2210
- If the given id cannot be found then cson_rc.NotFoundError
2211
- must be returned. On success it must assign the root node
2212
- of the session tree to *tgt and return 0. On error *tgt
2213
- must not be modified and non-zero must be returned.
2214
-
2215
- On success ownership of *tgt is transfered to the caller.
2216
-
2217
- Error conditions include:
2218
-
2219
- - self, tgt, or id are NULL: cson_rc.ArgError
2220
-
2221
- - id is "not valid" (the meaning of "valid" is
2222
- implementation-dependent): cson_rc.ArgError
2223
-
2224
- The identifier string must be NUL-terminated. Its maximum
2225
- length, if any, is implementation-dependent.
2226
- */
2227
- int (*load)( cson_sessmgr * self, cson_value ** tgt, char const * id );
2228
-
2229
- /**
2230
- Must save the given JSON object tree to the underlying storage, using the given identifier
2231
- as its unique key. It must overwrite any existing session with that same identifier.
2232
- */
2233
- int (*save)( cson_sessmgr * self, cson_value const * root, char const * id );
2234
-
2235
- /**
2236
- Must remove all session data associated with the given id.
2237
-
2238
- Must return 0 on success, non-0 on error.
2239
- */
2240
- int (*remove)( cson_sessmgr * self, char const * id );
2241
- /**
2242
- Must free up any resources used by the self object and then
2243
- free self. After calling this, further use of the self
2244
- object invokes undefined behaviour.
2245
- */
2246
- void (*finalize)( cson_sessmgr * self );
2247
- };
2248
-
2249
- /**
2250
- cson_sessmgr is base interface type for concrete
2251
- cson_sessmgr_api implementations. Each holds a pointer to its
2252
- underlying implementation and to implementation-private
2253
- data.
2254
-
2255
- @see cson_sessmgr_register()
2256
- @see cson_sessmgr_load()
2257
- @see cson_sessmgr_names()
2258
- @see cson_sessmgr_api
2259
- */
2260
- struct cson_sessmgr
2261
- {
2262
- /**
2263
- The concrete implementation functions for this
2264
- session manager instance.
2265
- */
2266
- const cson_sessmgr_api * api;
2267
- /**
2268
- Private implementation date for this session manager
2269
- instance. It is owned by this object and will be freed when
2270
- thisObject->api->finalize(thisObject) is called. Client
2271
- code must never use nor rely on the type/contents of the
2272
- memory stored here.
2273
- */
2274
- void * impl;
2275
- };
2276
-
2277
- /**
2278
- A typedef for factory functions which instantiate cson_sessmgr
2279
- instances.
2280
-
2281
- The semantics are:
2282
-
2283
- - tgt must be a non-NULL pointer where the result object can be
2284
- stored. If it is NULL, cson_rc.ArgError must be returned.
2285
-
2286
- - opt (configuration options) may or may not be required,
2287
- depending on the manager. If it is required and not passed in,
2288
- cson_rc.ArgError must be returned. If the config options are
2289
- required but the passed-in object is missing certain values, or
2290
- has incorrect values, the implementation may substitute
2291
- sensible defaults (if possible) or return cson_rc.ArgError.
2292
-
2293
- - On error non-0 (one of the cson_rc values) must be returned
2294
- and tgt must not be modified.
2295
-
2296
- - On success *tgt must be pointed to the new manager object,
2297
- zero must be returned, and the caller takes over ownership of
2298
- the *tgt value (and must eventually free it with
2299
- obj->api->finalize(obj)).
2300
- */
2301
- typedef int (*cson_sessmgr_factory_f)( cson_sessmgr ** tgt, cson_object const * config );
2302
-
2303
-#define cson_sessmgr_empty_m { NULL/*api*/, NULL/*impl*/ }
2304
-
2305
- /**
2306
- Registers a session manager by name. The given name must be a
2307
- NUL-terminaed string shorter than some internal limit
2308
- (currently 32 bytes, including the trailing NUL). f must be a
2309
- function conforming to the cson_sessmgr_factory_f() interface.
2310
-
2311
- On success returns 0.
2312
-
2313
- On error either one of the arguments was invalid, an entry with
2314
- the given name was already found, or no space is left in the
2315
- internal registration list. The API guarantees that at least 10
2316
- slots are initially available, and it is not anticipated that
2317
- more than a small handful of them will ever be used.
2318
-
2319
- This function is not threadsafe - do not register factories
2320
- concurrently from multiple threads.
2321
-
2322
- By default the following registrations are (possibly)
2323
- pre-installed:
2324
-
2325
- - "file" = cson_sessmgr_file()
2326
-
2327
- - "cpdo" = cson_sessmgr_cpdo() IF this library is compiled with
2328
- the macro CSON_ENABLE_CPDO set to a true value. Exactly which
2329
- databases are supported by that back-end (if any) are
2330
- determined by how the cpdo library code is compiled.
2331
-
2332
- - "whio_ht" = cson_sessmgr_whio_ht() IF this library is compiled
2333
- with whio support.
2334
-
2335
- - "whio_epfs" = cson_sessmgr_whio_epfs() IF this library is
2336
- compiled with whio support.
2337
- */
2338
- int cson_sessmgr_register( char const * name, cson_sessmgr_factory_f f );
2339
-
2340
- /**
2341
- A front-end to loading cson_sessmgr intances by their
2342
- cson_session-conventional name. The first arguments must be a
2343
- NUL-terminated string holding the name of the session manager
2344
- driver. The other two arguments have the same semantics as for
2345
- cson_sessmgr_factory_f(), so see that typedef's documentation
2346
- regarding, e.g., ownership of the *tgt value.
2347
-
2348
- This function is thread-safe with regards to itself but not
2349
- with regards to cson_sessmgr_register(). That is, it is legal
2350
- to call this function concurrently from multiple threads,
2351
- provided the arguments themselves are not being used
2352
- concurrently. However, it is not safe to call this function
2353
- when cson_sessmgr_register() is being called from another
2354
- thread, as that function modifies the lookup table used by this
2355
- function.
2356
-
2357
- On success 0 is returned and the ownership of *tgt is as
2358
- documented for cson_sessmgr_factory_f(). On error non-0 is
2359
- returned and tgt is not modified.
2360
- */
2361
- int cson_sessmgr_load( char const * name, cson_sessmgr ** tgt, cson_object const * opt );
2362
-
2363
- /**
2364
- Returns the list of session managers registered via
2365
- cson_sessmgr_register(). This function is not thread-safe in
2366
- conjunction with cson_sessmgr_register(), and results are
2367
- undefined if that function is called while this function is
2368
- called or the results of this function call are being used.
2369
-
2370
- The returned array is never NULL but has a NULL as its final
2371
- entry.
2372
-
2373
- Example usage:
2374
-
2375
- @code
2376
- char const * const * mgr = cson_sessmgr_names();
2377
- for( ; *mgr; ++mgr ) puts( *mgr );
2378
- @endcode
2379
- */
2380
- char const * const * cson_sessmgr_names();
2381
-
2382
- /**
2383
- A cson_sessmgr_factory_f() implementation which returns a new
2384
- session manager which uses local files for storage.
2385
-
2386
- tgt must be a non-NULL pointer where the result can be stored.
2387
-
2388
- The opt object may be NULL or may be a configuration object
2389
- with the following structure:
2390
-
2391
- @code
2392
- {
2393
- dir: string (directory to store session files in),
2394
- prefix: string (prefix part of filename),
2395
- suffix: string (file extension, including leading '.')
2396
- }
2397
- @endcode
2398
-
2399
- Any missing options will assume (unspecified) default values.
2400
- This routine does not ensure the validity of the option values,
2401
- other than to make sure they are strings.
2402
-
2403
- The returned object is owned by the caller, who must eventually
2404
- free it using obj->api->finalize(obj). If it returns NULL,
2405
- the error was an out-of-memory condition or tgt was NULL.
2406
-
2407
- On error non-0 is returned, but the only error conditions are
2408
- allocation errors and (NULL==tgt), which will return
2409
- cson_rc.AllocError resp. cson_rc.ArgError.
2410
-
2411
- Threading notes:
2412
-
2413
- - As long as no two operations on these manager instances use
2414
- the same JSON object and/or session ID at the same time,
2415
- multi-threaded usage should be okay. All save()/load()/remove()
2416
- data is local to those operations, with the exception of the
2417
- input arguments (which must not be used concurrently to those
2418
- calls).
2419
-
2420
- Storage locking:
2421
-
2422
- - No locking of input/output files is done, under the
2423
- assumption that only one thread/process will be using a given
2424
- session ID (which should, after all, be unique world-wide). If
2425
- sessions will only be read, not written, there is little danger
2426
- of something going wrong vis-a-vis locking (assuming the
2427
- session files exists and can be read).
2428
-
2429
- TODO:
2430
-
2431
- - Add a config option to enable storage locking. If we'll
2432
- re-implement this to use the whio API under the hood then we
2433
- could use the (slightly simpler) whio_lock API for this.
2434
- */
2435
- int cson_sessmgr_file( cson_sessmgr ** tgt, cson_object const * opt );
2436
-
2437
- /**
2438
- This is only available if cson is compiled with cpdo support.
2439
-
2440
- Implements the cson_sessmgr_factory_f() interface.
2441
-
2442
- This function tries to create a database connection using the options
2443
- supplied in the opt object. The opt object must structurarly look like:
2444
-
2445
- @code
2446
- {
2447
- "dsn": "cpdo dsn string",
2448
- "user": "string",
2449
- "password": "string",
2450
- "table": "table_name_where_sessions_are_stored",
2451
- "fieldId": "field_name_for_session_id (VARCHAR/STRING)",
2452
- "fieldTimestamp": "field_name_for_last_saved_timestamp (INTEGER)",
2453
- "fieldSession": "field_name_for_session_data (TEXT)"
2454
- }
2455
- @endcode
2456
-
2457
- On success it returns 0 and sets *tgt to the new session manager,
2458
- which is owned by the caller and must eventually be freed by calling
2459
- obj->api->finalize(obj).
2460
-
2461
- This function can fail for any number of reasons:
2462
-
2463
- - Any parameters are NULL (cson_rc.ArgError).
2464
-
2465
- - cpdo cannot connect to the given DSN with the given
2466
- username/password. Any error in establishing a connection causes
2467
- cson_rc.IOError to be returned, as opposed to the underlying
2468
- cpdo error code.
2469
-
2470
- - Any of the "table" or "fieldXXX" properties are NULL. It
2471
- needs these data in order to know where to load/save sessions.
2472
-
2473
-
2474
- If any required options are missing, cson_rc.ArgError is
2475
- returned.
2476
-
2477
- TODO: add option "preferBlob", which can be used to set the db
2478
- field type preference for the fieldSession field to
2479
- blob. Currently it prefers string but will try blob operations
2480
- if string ops fail. Blobs have the disadvantage of much larger
2481
- encoded sizes but the advantage that the JSON data is encoded
2482
- (at least by sqlite3) as a hex number stream, making it
2483
- unreadable to casual observers.
2484
-
2485
- @endcode
2486
- */
2487
- int cson_sessmgr_cpdo( cson_sessmgr ** tgt, cson_object const * opt );
2488
-
2489
- /**
2490
- This cson_sessmgr_factory_f() implementation might or might not
2491
- be compiled in, depending on the mood of the cson
2492
- maintainer. It is very niche-market, and primarily exists just
2493
- to test (and show off) the whio_ht code.
2494
-
2495
- It uses libwhio's on-storage hashtable (called whio_ht)
2496
- as the underlying storage:
2497
-
2498
- http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_ht
2499
-
2500
- The opt object must not be NULL and must contain a single
2501
- string property named "file" which contains the path to the
2502
- whio_ht file to use for sessions. That file must have been
2503
- previously created, either programatically using the whio_ht
2504
- API or using whio-ht-tool:
2505
-
2506
- http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_ht_tool
2507
-
2508
- See cson_sessmgr_factory_f() for the semantics of the tgt
2509
- argument and the return value.
2510
-
2511
- Threading notes:
2512
-
2513
- While the underlying hashtable supports a client-defined mutex,
2514
- this usage of it does not set one (because we have no default
2515
- one to use). What this means for clients is that they must not
2516
- use this session manager from multiple threads, nor may they
2517
- use multiple instances in the same process which use the same
2518
- underlying hashtable file from multiple threads. How best to
2519
- remedy this (allowing the client to tell this API what mutex to
2520
- use) is not yet clear. Maybe a global whio_mutex object which
2521
- the client must initialize before instantiating these session
2522
- managers.
2523
-
2524
- Storage Locking:
2525
-
2526
- If the underlying filesystem reports that it supports file
2527
- locking (via the whio_lock API, basically meaning POSIX
2528
- fcntl()-style locking) the the session manager will use it. For
2529
- the load() operation a read lock is acquired and for
2530
- save()/remove() a write lock. The operations will fail if
2531
- locking fails, the exception being if the device reports that
2532
- it doesn't support locking, in which case we optimistically
2533
- save/load/remove without locking.
2534
-
2535
- Remember that in POSIX-style locking, a single process does not
2536
- see its own locks and can overwrite locks set via other
2537
- threads. This means that multi-threaded use of a given
2538
- instance, or multiple instances in the same process using the
2539
- same underlying hashtable file, will likely eventually corrupt
2540
- the hashtable.
2541
-
2542
- TODO:
2543
-
2544
- - Add a config option to disable storage locking, for clients
2545
- who really don't want to use it.
2546
- */
2547
- int cson_sessmgr_whio_ht( cson_sessmgr ** tgt, cson_object const * opt );
2548
-
2549
- /**
2550
- This cson_sessmgr_factory_f() implementation might or might not
2551
- be compiled in, depending on the mood of the cson
2552
- maintainer. It is very niche-market, and primarily exists just
2553
- to test (and show off) the whio_epfs code.
2554
-
2555
- It uses libwhio's embedded filesystem (called whio_epfs) as the
2556
- underlying storage:
2557
-
2558
- http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_epfs
2559
-
2560
- The opt object must not be NULL and must contain a single
2561
- string property named "file" which contains the path to the
2562
- whio_epfs "container file" to use for storing sessions. That
2563
- file must have been previously created, either programatically
2564
- using the whio_epfs API or using whio-epfs-mkfs:
2565
-
2566
- http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_epfs_mkfs
2567
-
2568
- The EPFS container file MUST be created with a "namer"
2569
- installed. See the above page for full details and examples.
2570
-
2571
- See cson_sessmgr_factory_f() for the semantics of the tgt
2572
- argument and the return value.
2573
-
2574
- Threading notes:
2575
-
2576
- - It is not legal to use this session manager from multiple threads.
2577
- Doing so will eventually corrupt the underlying EFS if multiple writers
2578
- work concurrently, and will also eventually _appear_ corrupt to multiple
2579
- readers.
2580
-
2581
- Storage locking:
2582
-
2583
- The underlying storage (EFS container file) is locked (with a
2584
- write lock) for the lifetime the the returned session manager
2585
- IF the storage reports that it supports locking. Unlocked
2586
- write access from an outside application will corrupt the EFS.
2587
-
2588
- TODOs:
2589
-
2590
- - Add config option to explicitly disable locking support.
2591
- */
2592
- int cson_sessmgr_whio_epfs( cson_sessmgr ** tgt, cson_object const * opt );
2593
-
2594
-#if 0
2595
- /** TODO? dummy manager which has no i/o support. */
2596
- int cson_sessmgr_transient( cson_sessmgr ** tgt );
2597
-#endif
2598
-
2599
-/* LICENSE
2600
-
2601
-This software's source code, including accompanying documentation and
2602
-demonstration applications, are licensed under the following
2603
-conditions...
2604
-
2605
-Certain files are imported from external projects and have their own
2606
-licensing terms. Namely, the JSON_parser.* files. See their files for
2607
-their official licenses, but the summary is "do what you want [with
2608
-them] but leave the license text and copyright in place."
2609
-
2610
-The author (Stephan G. Beal [http://wanderinghorse.net/home/stephan/])
2611
-explicitly disclaims copyright in all jurisdictions which recognize
2612
-such a disclaimer. In such jurisdictions, this software is released
2613
-into the Public Domain.
2614
-
2615
-In jurisdictions which do not recognize Public Domain property
2616
-(e.g. Germany as of 2011), this software is Copyright (c) 2011 by
2617
-Stephan G. Beal, and is released under the terms of the MIT License
2618
-(see below).
2619
-
2620
-In jurisdictions which recognize Public Domain property, the user of
2621
-this software may choose to accept it either as 1) Public Domain, 2)
2622
-under the conditions of the MIT License (see below), or 3) under the
2623
-terms of dual Public Domain/MIT License conditions described here, as
2624
-they choose.
2625
-
2626
-The MIT License is about as close to Public Domain as a license can
2627
-get, and is described in clear, concise terms at:
2628
-
2629
- http://en.wikipedia.org/wiki/MIT_License
2630
-
2631
-The full text of the MIT License follows:
2632
-
---
2633
-Copyright (c) 2011 Stephan G. Beal (http://wanderinghorse.net/home/stephan/)
2634
-
2635
-Permission is hereby granted, free of charge, to any person
2636
-obtaining a copy of this software and associated documentation
2637
-files (the "Software"), to deal in the Software without
2638
-restriction, including without limitation the rights to use,
2639
-copy, modify, merge, publish, distribute, sublicense, and/or sell
2640
-copies of the Software, and to permit persons to whom the
2641
-Software is furnished to do so, subject to the following
2642
-conditions:
2643
-
2644
-The above copyright notice and this permission notice shall be
2645
-included in all copies or substantial portions of the Software.
2646
-
2647
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2648
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
2649
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2650
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
2651
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
2652
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2653
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2654
-OTHER DEALINGS IN THE SOFTWARE.
2655
-
---END OF MIT LICENSE--
2656
-
2657
-For purposes of the above license, the term "Software" includes
2658
-documentation and demonstration source code which accompanies
2659
-this software. ("Accompanies" = is contained in the Software's
2660
-primary public source code repository.)
2661
-
2662
-*/
2663
-
2664
-#if defined(__cplusplus)
2665
-} /*extern "C"*/
2666
-#endif
2667
-
2668
-#endif /* WANDERINGHORSE_NET_CSON_SESSION_H_INCLUDED */
2669
-/* end file include/wh/cson/cson_session.h */
2670
-/* begin file include/wh/cson/cson_cgi.h */
2671
-#if !defined(WANDERINGHORSE_NET_CSON_CGI_H_INCLUDED)
2672
-#define WANDERINGHORSE_NET_CSON_CGI_H_INCLUDED 1
2673
-
2674
-/** @page page_cson_cgi cson CGI API
2675
-
2676
-cson_cgi is a small framework encapsulating features usefor for
2677
-writing JSON-only applications (primarily CGI apps) in C. It is based
2678
-off of the cson JSON library:
2679
-
2680
- http://fossil.wanderinghorse.net/repos/cson/
2681
-
2682
-In essence, it takes care of the basic CGI-related app setup, making
2683
-the data somewhat more accessible for client purposes. Clients
2684
-create, as output, a single JSON object. The framework takes care of
2685
-outputing it, along with any necessary HTTP headers.
2686
-
2687
-
2688
-Primary features:
2689
-
2690
-- Uses cson for its JSON handling, so it's pretty simple to use.
2691
-
2692
-- Provides a simple-to-use mini-framework for writing CGI applications
2693
-which generate only JSON output.
2694
-
2695
-- Various sources of system data are converted by the framework to
2696
-JSON for use by the client. This includes HTTP GET, OS environment,
2697
-command-line arguments, HTTP cookies, and (with some limitations) HTTP
2698
-POST.
2699
-
2700
-- Can read unencoded JSON POST data (TODO: read in form-urlencoded as
2701
-a JSON object).
2702
-
2703
-- Supports an optional JSON config file.
2704
-
2705
-- Optional persistent sessions using files, sqlite3, or MySQL5
2706
-for the storage.
2707
-
2708
-
2709
-Primary misfeatures:
2710
-
2711
-- Very young and not yet complete.
2712
-
2713
-- Intended for writing apps which ONLY generate JSON data, not HTML.
2714
-
2715
-- JSONP output support is currently incomplete.
2716
-
2717
-- TODO: support reading of POSTed Array data (currently only Objects
2718
-work).
2719
-
2720
-- We're missing a good number of convenience functions.
2721
-
2722
-- Add client API for setting cookies. Currently they can be fetched
2723
-or removed but not explicitly set.
2724
-
2725
-
2726
-Other potential TODOs:
2727
-
2728
-- Session support using session cookies for the IDs. For this to work
2729
-we also need to add a storage back-end or two (files, db (e.g. using
2730
-cpdo), etc.) and have a fast, good source of random numbers for
2731
-generating UUIDs. It _seems_ that using the address of (extern char **
2732
-environ) as a seed might be fairly random, but there are probably
2733
-environments where that won't suffice. i want to avoid
2734
-platform-specific bits, like /dev/urandom, if possible.
2735
-
2736
-- Add client-definable i/o routines. We have this code in the whprintf
2737
-tree, but i was hoping to avoid having to import that here. This would
2738
-allow a client to add, e.g., gzip support.
2739
-
2740
-*/
2741
-
2742
-/** @page page_cson_cgi_session cson CGI Sessions
2743
-
2744
-cson_cgi_init() will initialize a persistent session if it can figure
2745
-out everything it needs in order to do so. If it cannot it will simply
2746
-skip session initialization. A client can tell if a session was
2747
-established or not by calling cson_cgi_get_env_val(cx,'s',0). If that
2748
-returns NULL then no session was created during initialization.
2749
-The API currently provides no way to initialize one after the fact.
2750
-That is, client code can use cson_cgi_get_env_val(cx,'s',1) to create a
2751
-session object, but it won't automatically be persistent across
2752
-application sessions.
2753
-
2754
-Session management is really just the following:
2755
-
2756
-- Load a JSON object from some persistent storage.
2757
-- Save that object at shutdown.
2758
-
2759
-"Persistent storage" is the important phrase here. How the sessions
2760
-are saved is really unimportant. The library uses a generic interface
2761
-(cson_sessmgr) to handle the i/o, and can use any implementation we
2762
-care to provide. As of this writing (20110413) files, sqlite3, and
2763
-MySQL5 are supported.
2764
-
2765
-The session object is a JSON object available via cson_cgi_getenv(),
2766
-using "s" as the environment name (the second parameter to
2767
-cson_cgi_getenv()).
2768
-
2769
-At library shutdown (typically when main() exits, but also via
2770
-cson_cgi_cx_clean()), the session is saved using the current
2771
-session manager. If the session is not loaded during initialization,
2772
-but is created later, the library will assign a new session ID to it
2773
-before saving.
2774
-
2775
-See cson_cgi_config_file() for examples of configuring the session
2776
-management.
2777
-*/
2778
-
2779
-
2780
-#if defined(__cplusplus)
2781
-extern "C" {
2782
-#endif
2783
-
2784
- /** @def CSON_CGI_ENABLE_POST_FORM_URLENCODED
2785
-
2786
- If CSON_CGI_ENABLE_POST_FORM_URLENCODED is set to a true value
2787
- then the API will try to process form-urlencoded POST data,
2788
- otherwise it will not.
2789
-
2790
- Reminder to self: this is basically a quick hack for fossil
2791
- integration. We disable form encoding in that build because fossil
2792
- handles that itself and we must not interfere with it.
2793
- */
2794
-#if !defined(CSON_CGI_ENABLE_POST_FORM_URLENCODED)
2795
-#define CSON_CGI_ENABLE_POST_FORM_URLENCODED 0
2796
-#endif
2797
- /** @def CSON_CGI_GETENV_DEFAULT
2798
-
2799
- The default environment name(s) to use for cson_cgi_getenv().
2800
- */
2801
-#define CSON_CGI_GETENV_DEFAULT "gpce"
2802
- /** @def CSON_CGI_KEY_JSONP
2803
-
2804
- TODO?: get rid of this and move cson_cgi_keys into the public
2805
- API?
2806
-
2807
- The default environment key name to use for checking whether a
2808
- response should used JSONP or not.
2809
- */
2810
-#define CSON_CGI_KEY_JSONP "jspon"
2811
-
2812
-#define CSON_CGI_KEY_SESSION "CSONSESSID"
2813
-
2814
-
2815
- typedef struct cson_cgi_init_opt cson_cgi_init_opt;
2816
- /** @struct cson_cgi_init_opt
2817
-
2818
- A type to hold core runtime configuration information for
2819
- cson_cgi.
2820
- */
2821
- struct cson_cgi_init_opt
2822
- {
2823
- /**
2824
- stdin stream. If NULL, stdin is used.
2825
- */
2826
- FILE * inStream;
2827
- /**
2828
- stdout stream. If NULL, stdout is used.
2829
- */
2830
- FILE * outStream;
2831
- /**
2832
- stderr stream. If NULL, stderr is used.
2833
- */
2834
- FILE * errStream;
2835
- /**
2836
- Path to a JSON config file.
2837
- */
2838
- char const * configFile;
2839
-
2840
- /**
2841
- If set then the session will be forced to use this
2842
- ID, otherwise one will be generated.
2843
- */
2844
- char const * sessionID;
2845
-
2846
- /**
2847
- Values are interpretted as:
2848
-
2849
- 0 = do not output headers when cson_cgi_response_output_all()
2850
- is called.
2851
-
2852
- (>0) = always output headers when cson_cgi_response_output_all()
2853
- is called.
2854
-
2855
- (<0) = Try to determine, based on environment variables, if
2856
- we are running in CGI mode. If so, output the headers when
2857
- cson_cgi_response_output_all() is called, otherwise omit
2858
- them.
2859
-
2860
- If the headers are omitted then so is the empty line
2861
- which normally separates them from the response body!
2862
-
2863
- The intention of this flag is to allow non-CGI apps
2864
- to disable output of the HTTP headers.
2865
- */
2866
- char httpHeadersMode;
2867
-
2868
- /**
2869
- JSON output options.
2870
- */
2871
- cson_output_opt outOpt;
2872
-
2873
- };
2874
-
2875
- /** Empty-initialized cson_cgi_init_opt object. */
2876
-#define cson_cgi_init_opt_empty_m { \
2877
- NULL/*inStream*/, NULL/*outStream*/, NULL/*errStream*/, \
2878
- NULL /*configFile*/, NULL /*sessionID*/, \
2879
- -1/*httpHeadersMode*/, \
2880
- cson_output_opt_empty_m /*outOpt*/ \
2881
- }
2882
-
2883
- /** Empty-initialized cson_cgi_init_opt object. */
2884
- extern const cson_cgi_init_opt cson_cgi_init_opt_empty;
2885
-
2886
-
2887
- /** @struct cson_cgi_env_map
2888
- Holds a cson_object and its cson_value parent
2889
- reference.
2890
- */
2891
- struct cson_cgi_env_map
2892
- {
2893
- /** Parent reference of jobj. */
2894
- cson_value * jval;
2895
- /** Object reference of jval. */
2896
- cson_object * jobj;
2897
- };
2898
- typedef struct cson_cgi_env_map cson_cgi_env_map;
2899
-
2900
- /** Empty cson_cgi_env_map object. */
2901
-#define cson_cgi_env_map_empty_m { NULL, NULL }
2902
-
2903
- /** @struct cson_cgi_cx
2904
-
2905
- Internal state used by cson_cgi.
2906
-
2907
- Clients must not rely on its internal structure. It is in the
2908
- public API so that it can be stack- or custom-allocated. To
2909
- properly initialize such an object, use cson_cgi_cx_empty_m
2910
- cson_cgi_cx_empty, depending on the context.
2911
- */
2912
- struct cson_cgi_cx
2913
- {
2914
- /**
2915
- Various key/value stores used by the framework. Each
2916
- corresponds to some convention source of key/value
2917
- pairs, e.g. QUERY_STRING or POST parameters.
2918
- */
2919
- struct {
2920
- /**
2921
- The system's environment variables.
2922
- */
2923
- cson_cgi_env_map env;
2924
- /**
2925
- Holds QUERY_STRING key/value pairs.
2926
- */
2927
- cson_cgi_env_map get;
2928
- /**
2929
- Holds POST form/JSON data key/value pairs.
2930
- */
2931
- cson_cgi_env_map post;
2932
- /**
2933
- Holds cookie key/value pairs.
2934
- */
2935
- cson_cgi_env_map cookie;
2936
- /**
2937
- Holds request headers.
2938
- */
2939
- cson_cgi_env_map headers;
2940
- } request;
2941
- /**
2942
- Holds data related to the response JSON.
2943
- */
2944
- struct {
2945
- /**
2946
- HTTP error code to report.
2947
- */
2948
- int httpCode;
2949
-
2950
- /**
2951
- Root JSON object. Must be an Object or Array (per the JSON
2952
- spec).
2953
- */
2954
- cson_value * root;
2955
- /**
2956
- Holds HTTP response headers as an array of key/value
2957
- pairs.
2958
- */
2959
- cson_cgi_env_map headers;
2960
- } response;
2961
- /**
2962
- A place to store cson_value references
2963
- for cleanup purposes.
2964
- */
2965
- cson_cgi_env_map gc;
2966
- /**
2967
- Holds client-defined key/value pairs.
2968
- */
2969
- cson_cgi_env_map clientEnv;
2970
- cson_cgi_env_map config;
2971
- struct {
2972
- cson_cgi_env_map env;
2973
- cson_sessmgr * mgr;
2974
- char * id;
2975
- } session;
2976
- struct {
2977
- cson_array * jarr;
2978
- cson_value * jval;
2979
- } argv;
2980
-
2981
- cson_cgi_init_opt opt;
2982
- cson_buffer tmpBuf;
2983
- struct {
2984
- char isJSONP;
2985
- void const * allocStamp;
2986
- } misc;
2987
- };
2988
- typedef struct cson_cgi_cx cson_cgi_cx;
2989
- /**
2990
- Empty-initialized cson_cgi_cx object.
2991
- */
2992
- extern const cson_cgi_cx cson_cgi_cx_empty;
2993
- /**
2994
- Empty-initialized cson_cgi_cx object.
2995
- */
2996
-#define cson_cgi_cx_empty_m \
2997
- { \
2998
- { /*maps*/ \
2999
- cson_cgi_env_map_empty_m /*env*/, \
3000
- cson_cgi_env_map_empty_m /*get*/, \
3001
- cson_cgi_env_map_empty_m /*post*/, \
3002
- cson_cgi_env_map_empty_m /*cookie*/, \
3003
- cson_cgi_env_map_empty_m /*headers*/ \
3004
- }, \
3005
- {/*response*/ \
3006
- 0 /*httpCode*/, \
3007
- NULL /*root*/, \
3008
- cson_cgi_env_map_empty_m /*headers*/ \
3009
- }, \
3010
- cson_cgi_env_map_empty_m /*gc*/, \
3011
- cson_cgi_env_map_empty_m /*clientEnv*/, \
3012
- cson_cgi_env_map_empty_m /*config*/, \
3013
- {/*session*/ \
3014
- cson_cgi_env_map_empty_m /*env*/, \
3015
- NULL /*mgr*/, \
3016
- NULL /*id*/ \
3017
- }, \
3018
- {/*argv*/ \
3019
- NULL /*jarr*/, \
3020
- NULL /*jval*/ \
3021
- }, \
3022
- cson_cgi_init_opt_empty_m /*opt*/, \
3023
- cson_buffer_empty_m /* tmpBuf */, \
3024
- {/*misc*/ \
3025
- -1 /*isJSONP*/, \
3026
- NULL/*allocStamp*/ \
3027
- } \
3028
- }
3029
-
3030
- cson_cgi_cx * cson_cgi_cx_alloc();
3031
- /**
3032
- Cleans up all internal state of cx. IFF cx was allocated by
3033
- cson_cgi_cx_alloc() then cx is also free()d, else it is assumed
3034
- to have been allocated by the caller (possibly on the stack).
3035
-
3036
- Returns 1 if cx is not NULL and this function actually frees
3037
- it. If it returns 0 then either cx is NULL or this function
3038
- cleaned up its internals but did not free(cx) (cx is assumed to
3039
- have been allocated by the client).
3040
- */
3041
- char cson_cgi_cx_clean(cson_cgi_cx * cx);
3042
-
3043
- /**
3044
- Initializes the internal cson_cgi environment and must be
3045
- called one time, from main(), at application startup.
3046
-
3047
- cx must be either:
3048
-
3049
- - Created via cson_cgi_cx_alloc().
3050
-
3051
- - Alternately allocated (e.g. on the stack) and initialized
3052
- by copying cson_cgi_cx_empty over it.
3053
-
3054
- Any other initialization leads to undefined behaviour.
3055
-
3056
- Returns 0 on success. On error the problem is almost certainly
3057
- an allocation error. If it returns non-0 then the rest of the
3058
- API will not work (and using the rest of the API invokes
3059
- undefined behaviour unless documented otherwise for a specific
3060
- function), so the application should exit immediately with an
3061
- error code. The error codes returned by this function all come
3062
- from the cson_rc object.
3063
-
3064
- The returned object must eventually be passed to
3065
- cson_cgi_cx_clean(), regardless of success or failure, to clean
3066
- up any resources allocated for the object.
3067
-
3068
- On success:
3069
-
3070
- - 0 is returned.
3071
-
3072
- - The cx object takes over ownership of any streams set in the
3073
- opt object UNLESS they are the stdin/stdout/stderr streams (in
3074
- which case ownership does not change).
3075
-
3076
- On error non-0 is returned and ownership of the opt.xxStream
3077
- STILL transfers over to cx as described above (because this
3078
- simpifies client-side error handling ).
3079
-
3080
- The 'opt' parameter can be used to tweak certain properties
3081
- of the framework. It may be NULL, in which case defaults are
3082
- used.
3083
-
3084
- This function currently performs the following initialization:
3085
-
3086
- - Parses QUERY_STRING environment variable into a JSON object.
3087
-
3088
- - Parses the HTTP_COOKIE environment variable into a JSON object.
3089
-
3090
- - Transforms the system environment to JSON.
3091
-
3092
- - Copies the list of arguments (argv, in the form conventional
3093
- for main()) to JSON array form for downstream use by the client
3094
- application. It does not interpret these arguments in any
3095
- way. Clients may use cson_cgi_argv() and
3096
- cson_cgi_argv_array() to fetch the list later on in the
3097
- application's lifetime (presumably outside of main()). It is
3098
- legal to pass (argv==NULL) only if argc is 0 or less.
3099
-
3100
- - If the CONTENT_TYPE env var is one of (application/json,
3101
- application/javascript, or text/plain) and CONTENT_LENGTH is
3102
- set then stdin is assumed to be JSON data coming in via POST.
3103
- An error during that parsing is ignored for initialization purposes
3104
- unless it is an allocation error, in which case it is propagated
3105
- back to the caller of this function.
3106
-
3107
- - If the CSON_CGI_CONFIG env var is set then that file is read.
3108
- Errors in loading the config are silently ignored.
3109
-
3110
- - If session management is properly configured in the
3111
- configuration file and if a variable named CSON_CGI_KEY_SESSION
3112
- is found in the environment (cookies, GET, POST, or system env)
3113
- then the previous session is loaded. If it cannot be loaded,
3114
- the error is ignored. (Note that the cookie name can be
3115
- changed via the configuration file.)
3116
-
3117
- TODOs:
3118
-
3119
- - Add config file option to the opt object.
3120
-
3121
- - Only read POST data when REQUEST_METHOD==POST?
3122
-
3123
- - Convert form-urlencoded POST data to a JSON object.
3124
-
3125
- - Potentially add an option to do automatic data type detection
3126
- for numeric GET/POST/ENV/COOKIE data, such that fetching the
3127
- cson_value for such a key would return a numeric value object
3128
- as opposed to a string. Or we could add that option in a
3129
- separate function which walks a JSON Object and performs that
3130
- check/transformation on all of its entries. That currently
3131
- can't be done properly with the cson_object_iterator API
3132
- because changes to the object while looping invalidate the
3133
- iterator. This option would also open up problems when clients
3134
- pass huge strings which just happen to look like numbers.
3135
-
3136
-
3137
- @see cson_cgi_config_file()
3138
- */
3139
- int cson_cgi_init( cson_cgi_cx * cx, int argc, char const * const * argv, cson_cgi_init_opt * options );
3140
-
3141
- /**
3142
- Searches for a value from the CGI environment. The fromWhere
3143
- parameter is a NUL-terminated string which specifies which
3144
- environment(s) to check, and may be made up of any of the
3145
- letters [gprecl], case-insensitive. If fromWhere is NULL or its
3146
- first byte is NUL (i.e. it is empty) then the default value
3147
- defined in CSON_CGI_GETENV_DEFAULT is used.
3148
-
3149
- The environments are searched in the order specified in
3150
- fromWhere. The various letters mean:
3151
-
3152
- - g = GET: key/value pairs parsed from the QUERY_STRING
3153
- environment variable.
3154
-
3155
- - p = POST: form-encoded key/value pairs parsed from stdin.
3156
-
3157
- - r = REQUEST, equivalent to "gpc", a superset of GET/POST/COOKIE.
3158
-
3159
- - e = ENV, e.g. via getenv(), but see cson_cgi_env_get_val()
3160
- for more details.
3161
-
3162
- - c = COOKIE: request cookies (not response cookies) parsed
3163
- from the HTTP_COOKIE environment variable.
3164
-
3165
- - a = APP: an environment namespace reserved for client app use.
3166
-
3167
- - f = CONFIG FILE.
3168
-
3169
- - Use key 's' for the SESSION.
3170
-
3171
- Invalid characters are ignored.
3172
-
3173
- The returned value is owned by the cson_cgi environment and
3174
- must not be destroyed by the caller. NULL is returned if none
3175
- of the requested environments contain the given key.
3176
-
3177
- Results are undefined if fromWhere is not NULL and is not
3178
- NUL-terminated.
3179
-
3180
- TODOs:
3181
-
3182
- - Replace CSON_CGI_GETENV_DEFAULT with a runtime-configurable
3183
- value (via a config file).
3184
-
3185
- */
3186
- cson_value * cson_cgi_getenv( cson_cgi_cx * cx, char const * fromWhere, char const * key );
3187
-
3188
- /**
3189
- A convenience form of cson_cgi_getenv() which returns the given
3190
- key as a string. This will return NULL if the requested key
3191
- is-not-a string value. It does not convert non-string values to
3192
- strings.
3193
-
3194
- On success the string value is returned. Its bytes are owned by
3195
- this API and are valid until the given key is removed/replaced
3196
- from/in the environment object it was found in or that
3197
- environment object is cleaned up.
3198
- */
3199
- char const * cson_cgi_getenv_cstr( cson_cgi_cx * cx, char const * where, char const * key );
3200
-
3201
- /**
3202
- During initialization, if the PATH_INFO environment variable is set,
3203
- it is split on '/' characters into array. That array is stored in the
3204
- environment with the name PATH_INFO_SPLIT. This function returns the
3205
- element of the PATH_INFO at the given index, or NULL if ndx is out
3206
- of bounds or if no PATH_INFO is available.
3207
-
3208
- e.g. if PATH_INFO=/a/b/c, passing 0 to this function would return
3209
- "a", passing 2 would return "c", and passing anything greater than 2
3210
- would return NULL.
3211
- */
3212
- char const * cson_cgi_path_part_cstr( cson_cgi_cx * cx, unsigned short ndx );
3213
-
3214
- /**
3215
- Functionally equivalent to cson_cgi_path_part_cstr(), but
3216
- returns the underlying value as a cson value handle. That handle
3217
- is owned by the underlying PATH_INFO_SPLIT array (which is
3218
- owned by the "e" environment object).
3219
-
3220
- Unless the client has mucked with the PATH_INFO_SPLIT data, the
3221
- returned value will (if it is not NULL) have a logical type of
3222
- String.
3223
- */
3224
- cson_value * cson_cgi_path_part( cson_cgi_cx * cx, unsigned short ndx );
3225
-
3226
- /**
3227
- Sets or unsets a key in the "user" environment/namespace. If v is NULL
3228
- then the value is removed, otherwise it is set/replaced.
3229
-
3230
- Returns 0 on success. If key is NULL or has a length of 0 then
3231
- cson_rc.ArgError is returned.
3232
-
3233
- The user namespace object can be fetched via
3234
- cson_cgi_env_get_val('a',...).
3235
-
3236
- On success ownership of v is transfered to (or shared with) the
3237
- cson_cgi API. On error ownership of v is not modified. Aside from
3238
- */
3239
- int cson_cgi_setenv( cson_cgi_cx * cx, char const * key, cson_value * v );
3240
-
3241
- /**
3242
- This function is not implemented, but exists as a convenient
3243
- place to document the cson_cgi config file format.
3244
-
3245
- cson_cgi_init() accepts the name of a configuration file
3246
- (assumed to be in JSON format) to read during
3247
- initialization. The library optionally uses the configuration
3248
- to change certain aspects of its behaviour.
3249
-
3250
- The following commented JSON demonstrates the configuration
3251
- file options:
3252
-
3253
- @code
3254
- {
3255
- "formatting": { // NOT YET HONORED. Will mimic cson_output_opt.
3256
- "indentation": 1,
3257
- "addNewline": true,
3258
- "addSpaceAfterColon": true,
3259
- "indentSingleMemberValues": true
3260
- },
3261
- "session": { // Options for session handling
3262
- "manager": "file", // name of session manager impl. Should
3263
- // have a matching entry in "managers" (below)
3264
- "cookieLifetimeMinutes": 10080, // cookie lifetime in minutes
3265
- "cookieName": "cson_session_id", // cookie name for session ID
3266
- "managers": {
3267
- "file": {
3268
- "sessionDriver": "file", -- cson_cgi-internal session manager name
3269
- "dir": "./SESSIONS",
3270
- "prefix": "cson-session-",
3271
- "suffix": ".json"
3272
- },
3273
- "mysql5": {
3274
- "sessionDriver": "cpdo", -- cson_cgi-internal session manager name
3275
- "dsn": "mysql5:dbname=cpdo;host=localhost",
3276
- "user": "cpdo",
3277
- "password": "cpdo",
3278
- "table": "cson_session",
3279
- "fieldId": "id",
3280
- "fieldTimestamp": "last_saved",
3281
- "fieldSession": "json"
3282
- },
3283
- "sqlite3": {
3284
- "sessionDriver": "cpdo", -- cson_cgi-internal session manager name
3285
- "dsn": "sqlite3:sessions.sqlite3",
3286
- "user": null,
3287
- "password": null,
3288
- "table": "cson_session",
3289
- "fieldId": "id",
3290
- "fieldTimestamp": "last_saved",
3291
- "fieldSession": "json"
3292
- }
3293
- }
3294
- }
3295
- }
3296
- @endcode
3297
-
3298
- TODO: allow initialization to take a JSON object, as opposed to
3299
- a filename, so that we can embed the configuration inside client-side
3300
- config data.
3301
- */
3302
- void cson_cgi_config_file();
3303
-
3304
- /**
3305
- Sets or (if v is NULL) unsets a cookie value.
3306
-
3307
- v must either be of one of the types (string, integer, double,
3308
- bool, null, NULL) or must be an object with the following
3309
- structure:
3310
-
3311
- @code
3312
- {
3313
- value: (string, integer, double, bool, or null),
3314
- OPTIONAL path: string,
3315
- OPTIONAL domain: string,
3316
- OPTIONAL expires: integer (Unix epoch timestamp),
3317
- OPTIONAL secure: bool,
3318
- OPTIONAL httponly: bool
3319
- }
3320
- @endcode
3321
-
3322
- For the object form, if the "value" property is missing or not of
3323
- the correct type then the cookie will not be emitted in the
3324
- HTTP response headers. The other properties are optional. A value
3325
- of NULL or cson_value_null() will cause the expiry field (if set)
3326
- to be ignored. Note, however, that removal will only work
3327
- on the client side if all other cookie parameters match
3328
- (e.g. domain and path).
3329
-
3330
- Returns 0 on success, non-0 on error.
3331
-
3332
- A duplicate cookie replaces any previous cookie with the same
3333
- key.
3334
-
3335
- On success ownership of v is shared with the cson_cgi API (via
3336
- reference counting). On error ownership of v is not modified.
3337
- */
3338
- int cson_cgi_cookie_set( cson_cgi_cx * cx, char const * key, cson_value * v );
3339
-
3340
- /**
3341
- Sets or (if v is NULL) unsets an HTTP cookie value. key may not
3342
- be NULL nor have a length of 0. v must be one of the types
3343
- (string, integer, double, bool, null, NULL). Any other pointer
3344
- arguments may be NULL, in which case they are not used.
3345
- If v is NULL then the JSON null value is used as a placeholder
3346
- value so that when the HTTP headers are generated, the cookie
3347
- can be unset on the client side.
3348
-
3349
- This function creates an object with the structure documented
3350
- in cson_cgi_cookie_set() and then passes that object to
3351
- cson_cgi_cookie_set(). Any parameters which have NULL/0 values
3352
- are not emitted in the generated object, with the exception of
3353
- (v==NULL), which causes the expiry property to be ignored and a
3354
- value from a time far in the past to be used (so that the
3355
- client will expire it)..
3356
-
3357
- Returns 0 on success, non-0 on error.
3358
-
3359
- On success ownership of v is shared with the cson_cgi API (via
3360
- reference counting). On error ownership of v is not modified.
3361
- */
3362
- int cson_cgi_cookie_set2( cson_cgi_cx * cx, char const * key, cson_value * v,
3363
- char const * domain, char const * path,
3364
- unsigned int expires, char secure, char httponly );
3365
-
3366
- /**
3367
- Returns the internal "environment" JSON object corresponding
3368
- to the given 'which' letter, which must be one of
3369
- (case-insensitive):
3370
-
3371
- - g = GET
3372
- - p = POST
3373
- - c = COOKIE
3374
- - e = ENV (i.e. system environment)
3375
- - s = SESSION
3376
- - a = APP (application-specific)
3377
-
3378
- TODO: s = SESSION
3379
-
3380
- See cson_cgi_getenv() for more details about each of those.
3381
-
3382
- Returns NULL if 'which' is not one of the above.
3383
-
3384
- Note that in the 'e' (system environment) case, making
3385
- modifications to the returned object will NOT also modify the
3386
- system environment. Likewise, future updates to the system
3387
- environment will not be automatically reflected in the
3388
- returned object.
3389
-
3390
- The returned object is owned by the cson_cgi environment and
3391
- must not be destroyed by the caller.
3392
-
3393
- If createIfNeeded is non-0 (true) then the requested
3394
- environment object is created if it was formerly empty. In that
3395
- case, a return value of NULL can indicate an invalid 'which'
3396
- parameter or an allocation error.
3397
-
3398
- To get the Object reference to this environment use
3399
- cson_cgi_env_get_obj() or pass the result of this function
3400
- to cson_value_get_object().
3401
-
3402
- The returned value is owned by the cson_cgi API.
3403
-
3404
- The public API does not provide a way for clients to modify
3405
- several of the internal environment stores, e.g. HTTP GET
3406
- parameters are set only by this framework. However, clients
3407
- can (if needed) get around this by fetching the associated
3408
- "environment object" via this function or
3409
- cson_cgi_env_get_obj(), and modifying it directly. Clients are
3410
- encouraged to use the other public APIs for dealing with the
3411
- environment, however, and are encouraged to not directly modify
3412
- "special" namespaces like the cookie/GET/POST data.
3413
- */
3414
- cson_value * cson_cgi_env_get_val( cson_cgi_cx * cx, char which, char createIfNeeded );
3415
-
3416
- /**
3417
- Equivalent to:
3418
-
3419
- @code
3420
- cson_value_get_object( cson_cgi_env_get_val( which, createIfNeeded ) );
3421
- @endcode
3422
-
3423
- Note, however, that it is at least theoretically possible that
3424
- cson_cgi_env_get_val() return non-NULL but this function
3425
- returns NULL. If that happens it means that the value returned
3426
- by cson_cgi_env_get_val() is-not-a Object instance, but is
3427
- something else (maybe an array?).
3428
- */
3429
- cson_object * cson_cgi_env_get_obj( cson_cgi_cx * cx, char which, char createIfNeeded );
3430
-
3431
- /**
3432
- Adds the given key/value to the list of HTTP headers (replacing
3433
- any existing entry with the same name). If v is NULL then any
3434
- header with the given key is removed from the pending response.
3435
-
3436
- Returns 0 on success. On success ownership of v is transfered
3437
- to (or shared with) the internal header list. On error,
3438
- ownership of v is not modified.
3439
-
3440
- If v is not of one of the types (string, integer, double, bool,
3441
- undef, null) then the header will not be output when when
3442
- cson_cgi_response_output_headers() is called. If it is one of
3443
- those types then its stringified value will be its "natural"
3444
- form (for strings and numbers), the integer 0 or 1 for
3445
- booleans, and the number 0 for null. Note that a literal
3446
- (v==NULL) is treated differently from a JSON null - it UNSETS
3447
- the given header.
3448
-
3449
- This function should not be used for setting cookies, as they
3450
- require extra url-encoding and possibly additional
3451
- parameters. Use cson_cgi_cookie_set() and
3452
- cson_cgi_cookie_set2() to set cookie headers.
3453
- */
3454
- int cson_cgi_response_header_add( cson_cgi_cx * cx, char const * key, cson_value * v );
3455
-
3456
- /**
3457
- Returns a cson array value containing the arguments passed
3458
- to cson_cgi_init(). The returned value is owned by the cson_cgi
3459
- API and must not be destroyed by the caller.
3460
-
3461
- Only returns NULL if initialization of cson_cgi_init() fails
3462
- early on, and is almost certainly indicative of an allocation
3463
- error. If cson_cgi_init() is given a 0-length argument list
3464
- then this function will return an empty array (except in the
3465
- NULL case mentioned above).
3466
- */
3467
- cson_value * cson_cgi_argv(cson_cgi_cx * cx);
3468
-
3469
- /**
3470
- Equivalent to:
3471
-
3472
- @code
3473
- cson_value_get_array( cson_cgi_argv() );
3474
- @endcode
3475
- */
3476
- cson_array * cson_cgi_argv_array(cson_cgi_cx * cx);
3477
-
3478
- /**
3479
- Flushes all response headers set via cson_cgi_response_header_add()
3480
- to stdout. The client must output an empty line before the body
3481
- part (if any), and may output custom headers before doing so.
3482
-
3483
- Do not call this more than once.
3484
- */
3485
- int cson_cgi_response_output_headers(cson_cgi_cx * cx);
3486
-
3487
- /**
3488
- Outputs the response root object to stdout. If none has been
3489
- set, non-0 is returned.
3490
-
3491
- Returns 0 on success. On error, partial output might be
3492
- generated.
3493
-
3494
- Do not call this more than once.
3495
- */
3496
- int cson_cgi_response_output_root(cson_cgi_cx * cx);
3497
-
3498
- /**
3499
- Outputs the whole response, including headers and the root JSON
3500
- value.
3501
-
3502
- Returns 0 on success. Fails without side effects if
3503
- no root is set.
3504
-
3505
- Do not call this more than once.
3506
- */
3507
- int cson_cgi_response_output_all(cson_cgi_cx * cx);
3508
-
3509
- /**
3510
- Don't use this - i need to re-think the JSONP bits.
3511
-
3512
- Returns non-0 (true) if the GET/POST environment contains a key
3513
- named CSON_CGI_KEY_JSONP. If this is the case, then when
3514
- cson_cgi_response_output_headers() is called the Content-type
3515
- is set to "application/javascript".
3516
-
3517
- If cson_cgi_enable_jsonp() is ever called to set this option
3518
- explicitly, this function does not guess, but uses that value
3519
- instead.
3520
-
3521
- When JSONP is desired, the generated page output must be
3522
- wrapped in the appropriate JS code.
3523
- */
3524
- char cson_cgi_is_jsonp(cson_cgi_cx * cx);
3525
-
3526
- /**
3527
- Don't use this - i need to re-think the JSONP bits.
3528
-
3529
- Sets or unsets JSONP mode. If b is 0 then JSONP guessing is
3530
- explicitly disabled and output is assumed to be JSON. If it is
3531
- non-0 then cson_cgi_guess_content_type() will never return
3532
- "application/javascript".
3533
-
3534
- When JSONP is desired, the generated page output must be
3535
- wrapped in the appropriate JS code.
3536
- */
3537
- void cson_cgi_enable_jsonp( cson_cgi_cx * cx, char b );
3538
-
3539
- /**
3540
- Tries to guess the "best" Content-type header value for
3541
- the current session, based on several factors:
3542
-
3543
- - If the GET/POST data contains a variable named
3544
- CSON_CGI_KEY_JSONP then "application/javascript" is returned.
3545
-
3546
- - If the HTTP_ACCEPT environment variable is NOT set or
3547
- contains "application/json" then "application/json [possibly
3548
- charset info]" is returned.
3549
-
3550
- - If the HTTP_ACCEPT environment variable is set but does not
3551
- contain "application/json" then "text/javascript" is returned.
3552
-
3553
- - If cson_cgi_enable_jsonp() is called and passed a true value,
3554
- "application/javascript" is returned.
3555
-
3556
-
3557
- If HTTP_ACCEPT_CHARSET is NOT set or contains "utf-8" then
3558
- ";charset=utf-8" is included in the the returned string.
3559
-
3560
- The returned string is static and immutable and suitable for
3561
- use as a Content-type header value. The string is guaranteed to
3562
- be valid until the application exits. Multiple calls to this
3563
- function, with different underlying environment data, can cause
3564
- different results to be returned.
3565
-
3566
- Returns NULL if it absolutely cannot figure out what to do, but
3567
- currently it has no such logic paths.
3568
- */
3569
- char const * cson_cgi_guess_content_type(cson_cgi_cx * cx);
3570
-
3571
- /**
3572
- Sets the response content root, replacing any
3573
- existing one (and possibly cleaning it up).
3574
-
3575
- Returns 0 on success. On success, ownership of v
3576
- is transfered to (or shared with) the cson_cgi
3577
- API. It will be cleaned up at app shutdown time
3578
- or if it is subsequently replaced and has no
3579
- other open references to it.
3580
-
3581
- On error ownership of v is not modified and any previous
3582
- root is not removed.
3583
-
3584
- If v is-not-a Object or Array, nor NULL, then cson_rc.TypeError
3585
- is returned. JSON requires either an object or array for the
3586
- root node. Passing NULL will possibly free up any current root
3587
- (depending on its reference count).
3588
- */
3589
- int cson_cgi_response_root_set( cson_cgi_cx * cx, cson_value * v );
3590
-
3591
- /**
3592
- Fetches the current content root JSON value, as set via
3593
- cson_cgi_response_root_set() or (if no root has been set), as
3594
- defined by createMode, as described below.
3595
-
3596
- If a content root has been set (or previously initialized)
3597
- then the value of createMode is ignored. If no root has been
3598
- set then this function might try to create one, as described
3599
- here:
3600
-
3601
- (createMode==0) means not to create a root element if none
3602
- exists already.
3603
-
3604
- (createMode<0) means to create the root as an Array value.
3605
-
3606
- (createMode>0) means to create the root as an Object value.
3607
-
3608
- Returns NULL on allocation error or if no root has been set and
3609
- (createMode==0). On success the returned value is guaranteed to
3610
- be either an Array or Object (see cson_value_get_object() and
3611
- cson_value_get_array()) and it is owned by the cson_cgi API (so
3612
- it must not be destroyed by the caller). If the client needs to
3613
- destroy it, pass NULL to cson_cgi_response_root_set().
3614
- */
3615
- cson_value * cson_cgi_response_root_get( cson_cgi_cx * cx, char createMode );
3616
-
3617
- /**
3618
- Returns the current session ID. If session management is not
3619
- enabled then NULL is returned.
3620
-
3621
- The returned bytes are owned by the cson_cgi API and are valid
3622
- until the library is cleaned up (via cson_cgi_cleanup_lib() or
3623
- via the normal shutdown process) or the session ID is
3624
- re-generated for some reason. It is best not to hold a
3625
- reference to this, but to copy it if it will be needed later.
3626
-
3627
- If the return value is not NULL, it is guaranteed to be
3628
- NUL-terminated.
3629
- */
3630
- char const * cson_cgi_session_id(cson_cgi_cx *);
3631
-
3632
- /**
3633
- Writes a 36-byte (plus one NUL byte) random UUID value to
3634
- dest. dest must be at least 37 bytes long. If dest is NULL this
3635
- function has no side effects.
3636
-
3637
- This function uses internal RNG state and is not thread-safe.
3638
- */
3639
- void cson_cgi_generate_uuid( cson_cgi_cx * cx, char * dest );
3640
-
3641
- /**
3642
- Adds v to the API-internal cleanup mechanism. key must be a
3643
- unique key for the given element. Adding another item with that
3644
- key may free the previous one. If freeOnError is true then v is
3645
- passed to cson_value_free() if the key cannot be inserted,
3646
- otherweise ownership of v is not changed on error.
3647
-
3648
- Returns 0 on success.
3649
-
3650
- On success, ownership of v is transfered to (or shared with)
3651
- cx, and v will be valid until cx is cleaned up or its key is
3652
- replaced via another call to this function.
3653
- */
3654
- int cson_cgi_gc_add( cson_cgi_cx * cx, char const * key, cson_value * v, char freeOnError );
3655
-
3656
- /* LICENSE
3657
-
3658
-This software's source code, including accompanying documentation and
3659
-demonstration applications, are licensed under the following
3660
-conditions...
3661
-
3662
-Certain files are imported from external projects and have their own
3663
-licensing terms. Namely, the JSON_parser.* files. See their files for
3664
-their official licenses, but the summary is "do what you want [with
3665
-them] but leave the license text and copyright in place."
3666
-
3667
-The author (Stephan G. Beal [http://wanderinghorse.net/home/stephan/])
3668
-explicitly disclaims copyright in all jurisdictions which recognize
3669
-such a disclaimer. In such jurisdictions, this software is released
3670
-into the Public Domain.
3671
-
3672
-In jurisdictions which do not recognize Public Domain property
3673
-(e.g. Germany as of 2011), this software is Copyright (c) 2011 by
3674
-Stephan G. Beal, and is released under the terms of the MIT License
3675
-(see below).
3676
-
3677
-In jurisdictions which recognize Public Domain property, the user of
3678
-this software may choose to accept it either as 1) Public Domain, 2)
3679
-under the conditions of the MIT License (see below), or 3) under the
3680
-terms of dual Public Domain/MIT License conditions described here, as
3681
-they choose.
3682
-
3683
-The MIT License is about as close to Public Domain as a license can
3684
-get, and is described in clear, concise terms at:
3685
-
3686
- http://en.wikipedia.org/wiki/MIT_License
3687
-
3688
-The full text of the MIT License follows:
3689
-
---
3690
-Copyright (c) 2011 Stephan G. Beal (http://wanderinghorse.net/home/stephan/)
3691
-
3692
-Permission is hereby granted, free of charge, to any person
3693
-obtaining a copy of this software and associated documentation
3694
-files (the "Software"), to deal in the Software without
3695
-restriction, including without limitation the rights to use,
3696
-copy, modify, merge, publish, distribute, sublicense, and/or sell
3697
-copies of the Software, and to permit persons to whom the
3698
-Software is furnished to do so, subject to the following
3699
-conditions:
3700
-
3701
-The above copyright notice and this permission notice shall be
3702
-included in all copies or substantial portions of the Software.
3703
-
3704
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
3705
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
3706
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
3707
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
3708
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
3709
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
3710
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
3711
-OTHER DEALINGS IN THE SOFTWARE.
3712
-
---END OF MIT LICENSE--
3713
-
3714
-For purposes of the above license, the term "Software" includes
3715
-documentation and demonstration source code which accompanies
3716
-this software. ("Accompanies" = is contained in the Software's
3717
-primary public source code repository.)
3718
-
3719
-*/
3720
-
3721
-#if defined(__cplusplus)
3722
-} /*extern "C"*/
3723
-#endif
3724
-
3725
-#endif /* WANDERINGHORSE_NET_CSON_CGI_H_INCLUDED */
3726
-/* end file include/wh/cson/cson_cgi.h */
37272199
--- src/cson_amalgamation.h
+++ src/cson_amalgamation.h
@@ -2044,10 +2044,59 @@
2044
2045 #if defined(__cplusplus)
2046 extern "C" {
2047 #endif
2048
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2049 /**
2050 Converts the results of an sqlite3 SELECT statement to JSON,
2051 in the form of a cson_value object tree.
2052
2053 st must be a prepared, but not yet traversed, SELECT query.
@@ -2145,1586 +2194,5 @@
2145 #endif
2146
2147 #endif /* CSON_ENABLE_SQLITE3 */
2148 #endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */
2149 /* end file include/wh/cson/cson_sqlite3.h */
2150 /* begin file include/wh/cson/cson_session.h */
2151 #if !defined(WANDERINGHORSE_NET_CSON_SESSION_H_INCLUDED)
2152 #define WANDERINGHORSE_NET_CSON_SESSION_H_INCLUDED 1
2153
2154 /** @page page_cson_session cson Session API
2155
2156 The cson_session API provides a small interface,
2157 called cson_sessmgr, which defines the basic operations
2158 needed for implementent persistent application state,
2159 across application sessions, by storing the state as
2160 JSON data in "some back-end storage." The exact underlying
2161 storage is not specified by the interface, but two
2162 implementations are provided by the library:
2163
2164 - File-based sessions.
2165
2166 - Database-based sessions, using libcpdo for connection
2167 abstraction.
2168
2169 libcpdo is included, in full, in the cson source tree,
2170 but can also be found on its web page:
2171
2172 http://fossil.wanderinghorse.net/repos/cpdo/
2173
2174 @see cson_sessmgr_register()
2175 @see cson_sessmgr_load()
2176 @see cson_sessmgr_names()
2177 @see cson_sessmgr
2178 @see cson_sessmgr_api
2179 */
2180
2181
2182 #if defined(__cplusplus)
2183 extern "C" {
2184 #endif
2185
2186 typedef struct cson_sessmgr cson_sessmgr;
2187 typedef struct cson_sessmgr_api cson_sessmgr_api;
2188
2189 /** @struct cson_sessmgr_api
2190
2191 Defines operations required by "session managers." Session managers
2192 are responsible for loading and saving cson session information
2193 in the form of JSON data.
2194
2195 @see cson_sessmgr
2196 */
2197 struct cson_sessmgr_api
2198 {
2199 /**
2200 Loads/creates a session object (JSON data). The
2201 implementation must use the given identifier for loading an
2202 existing session, creating the session if createIfNeeded is
2203 true and the session data is not found. If createIfNeeded
2204 is true then the implementation must create an Object for
2205 the session root, as opposed to an Array or other JSON
2206 value. Clients are allowed to use non-Objects as their
2207 sessions but doing so would seem to have no benefit, and is
2208 not recommended.
2209
2210 If the given id cannot be found then cson_rc.NotFoundError
2211 must be returned. On success it must assign the root node
2212 of the session tree to *tgt and return 0. On error *tgt
2213 must not be modified and non-zero must be returned.
2214
2215 On success ownership of *tgt is transfered to the caller.
2216
2217 Error conditions include:
2218
2219 - self, tgt, or id are NULL: cson_rc.ArgError
2220
2221 - id is "not valid" (the meaning of "valid" is
2222 implementation-dependent): cson_rc.ArgError
2223
2224 The identifier string must be NUL-terminated. Its maximum
2225 length, if any, is implementation-dependent.
2226 */
2227 int (*load)( cson_sessmgr * self, cson_value ** tgt, char const * id );
2228
2229 /**
2230 Must save the given JSON object tree to the underlying storage, using the given identifier
2231 as its unique key. It must overwrite any existing session with that same identifier.
2232 */
2233 int (*save)( cson_sessmgr * self, cson_value const * root, char const * id );
2234
2235 /**
2236 Must remove all session data associated with the given id.
2237
2238 Must return 0 on success, non-0 on error.
2239 */
2240 int (*remove)( cson_sessmgr * self, char const * id );
2241 /**
2242 Must free up any resources used by the self object and then
2243 free self. After calling this, further use of the self
2244 object invokes undefined behaviour.
2245 */
2246 void (*finalize)( cson_sessmgr * self );
2247 };
2248
2249 /**
2250 cson_sessmgr is base interface type for concrete
2251 cson_sessmgr_api implementations. Each holds a pointer to its
2252 underlying implementation and to implementation-private
2253 data.
2254
2255 @see cson_sessmgr_register()
2256 @see cson_sessmgr_load()
2257 @see cson_sessmgr_names()
2258 @see cson_sessmgr_api
2259 */
2260 struct cson_sessmgr
2261 {
2262 /**
2263 The concrete implementation functions for this
2264 session manager instance.
2265 */
2266 const cson_sessmgr_api * api;
2267 /**
2268 Private implementation date for this session manager
2269 instance. It is owned by this object and will be freed when
2270 thisObject->api->finalize(thisObject) is called. Client
2271 code must never use nor rely on the type/contents of the
2272 memory stored here.
2273 */
2274 void * impl;
2275 };
2276
2277 /**
2278 A typedef for factory functions which instantiate cson_sessmgr
2279 instances.
2280
2281 The semantics are:
2282
2283 - tgt must be a non-NULL pointer where the result object can be
2284 stored. If it is NULL, cson_rc.ArgError must be returned.
2285
2286 - opt (configuration options) may or may not be required,
2287 depending on the manager. If it is required and not passed in,
2288 cson_rc.ArgError must be returned. If the config options are
2289 required but the passed-in object is missing certain values, or
2290 has incorrect values, the implementation may substitute
2291 sensible defaults (if possible) or return cson_rc.ArgError.
2292
2293 - On error non-0 (one of the cson_rc values) must be returned
2294 and tgt must not be modified.
2295
2296 - On success *tgt must be pointed to the new manager object,
2297 zero must be returned, and the caller takes over ownership of
2298 the *tgt value (and must eventually free it with
2299 obj->api->finalize(obj)).
2300 */
2301 typedef int (*cson_sessmgr_factory_f)( cson_sessmgr ** tgt, cson_object const * config );
2302
2303 #define cson_sessmgr_empty_m { NULL/*api*/, NULL/*impl*/ }
2304
2305 /**
2306 Registers a session manager by name. The given name must be a
2307 NUL-terminaed string shorter than some internal limit
2308 (currently 32 bytes, including the trailing NUL). f must be a
2309 function conforming to the cson_sessmgr_factory_f() interface.
2310
2311 On success returns 0.
2312
2313 On error either one of the arguments was invalid, an entry with
2314 the given name was already found, or no space is left in the
2315 internal registration list. The API guarantees that at least 10
2316 slots are initially available, and it is not anticipated that
2317 more than a small handful of them will ever be used.
2318
2319 This function is not threadsafe - do not register factories
2320 concurrently from multiple threads.
2321
2322 By default the following registrations are (possibly)
2323 pre-installed:
2324
2325 - "file" = cson_sessmgr_file()
2326
2327 - "cpdo" = cson_sessmgr_cpdo() IF this library is compiled with
2328 the macro CSON_ENABLE_CPDO set to a true value. Exactly which
2329 databases are supported by that back-end (if any) are
2330 determined by how the cpdo library code is compiled.
2331
2332 - "whio_ht" = cson_sessmgr_whio_ht() IF this library is compiled
2333 with whio support.
2334
2335 - "whio_epfs" = cson_sessmgr_whio_epfs() IF this library is
2336 compiled with whio support.
2337 */
2338 int cson_sessmgr_register( char const * name, cson_sessmgr_factory_f f );
2339
2340 /**
2341 A front-end to loading cson_sessmgr intances by their
2342 cson_session-conventional name. The first arguments must be a
2343 NUL-terminated string holding the name of the session manager
2344 driver. The other two arguments have the same semantics as for
2345 cson_sessmgr_factory_f(), so see that typedef's documentation
2346 regarding, e.g., ownership of the *tgt value.
2347
2348 This function is thread-safe with regards to itself but not
2349 with regards to cson_sessmgr_register(). That is, it is legal
2350 to call this function concurrently from multiple threads,
2351 provided the arguments themselves are not being used
2352 concurrently. However, it is not safe to call this function
2353 when cson_sessmgr_register() is being called from another
2354 thread, as that function modifies the lookup table used by this
2355 function.
2356
2357 On success 0 is returned and the ownership of *tgt is as
2358 documented for cson_sessmgr_factory_f(). On error non-0 is
2359 returned and tgt is not modified.
2360 */
2361 int cson_sessmgr_load( char const * name, cson_sessmgr ** tgt, cson_object const * opt );
2362
2363 /**
2364 Returns the list of session managers registered via
2365 cson_sessmgr_register(). This function is not thread-safe in
2366 conjunction with cson_sessmgr_register(), and results are
2367 undefined if that function is called while this function is
2368 called or the results of this function call are being used.
2369
2370 The returned array is never NULL but has a NULL as its final
2371 entry.
2372
2373 Example usage:
2374
2375 @code
2376 char const * const * mgr = cson_sessmgr_names();
2377 for( ; *mgr; ++mgr ) puts( *mgr );
2378 @endcode
2379 */
2380 char const * const * cson_sessmgr_names();
2381
2382 /**
2383 A cson_sessmgr_factory_f() implementation which returns a new
2384 session manager which uses local files for storage.
2385
2386 tgt must be a non-NULL pointer where the result can be stored.
2387
2388 The opt object may be NULL or may be a configuration object
2389 with the following structure:
2390
2391 @code
2392 {
2393 dir: string (directory to store session files in),
2394 prefix: string (prefix part of filename),
2395 suffix: string (file extension, including leading '.')
2396 }
2397 @endcode
2398
2399 Any missing options will assume (unspecified) default values.
2400 This routine does not ensure the validity of the option values,
2401 other than to make sure they are strings.
2402
2403 The returned object is owned by the caller, who must eventually
2404 free it using obj->api->finalize(obj). If it returns NULL,
2405 the error was an out-of-memory condition or tgt was NULL.
2406
2407 On error non-0 is returned, but the only error conditions are
2408 allocation errors and (NULL==tgt), which will return
2409 cson_rc.AllocError resp. cson_rc.ArgError.
2410
2411 Threading notes:
2412
2413 - As long as no two operations on these manager instances use
2414 the same JSON object and/or session ID at the same time,
2415 multi-threaded usage should be okay. All save()/load()/remove()
2416 data is local to those operations, with the exception of the
2417 input arguments (which must not be used concurrently to those
2418 calls).
2419
2420 Storage locking:
2421
2422 - No locking of input/output files is done, under the
2423 assumption that only one thread/process will be using a given
2424 session ID (which should, after all, be unique world-wide). If
2425 sessions will only be read, not written, there is little danger
2426 of something going wrong vis-a-vis locking (assuming the
2427 session files exists and can be read).
2428
2429 TODO:
2430
2431 - Add a config option to enable storage locking. If we'll
2432 re-implement this to use the whio API under the hood then we
2433 could use the (slightly simpler) whio_lock API for this.
2434 */
2435 int cson_sessmgr_file( cson_sessmgr ** tgt, cson_object const * opt );
2436
2437 /**
2438 This is only available if cson is compiled with cpdo support.
2439
2440 Implements the cson_sessmgr_factory_f() interface.
2441
2442 This function tries to create a database connection using the options
2443 supplied in the opt object. The opt object must structurarly look like:
2444
2445 @code
2446 {
2447 "dsn": "cpdo dsn string",
2448 "user": "string",
2449 "password": "string",
2450 "table": "table_name_where_sessions_are_stored",
2451 "fieldId": "field_name_for_session_id (VARCHAR/STRING)",
2452 "fieldTimestamp": "field_name_for_last_saved_timestamp (INTEGER)",
2453 "fieldSession": "field_name_for_session_data (TEXT)"
2454 }
2455 @endcode
2456
2457 On success it returns 0 and sets *tgt to the new session manager,
2458 which is owned by the caller and must eventually be freed by calling
2459 obj->api->finalize(obj).
2460
2461 This function can fail for any number of reasons:
2462
2463 - Any parameters are NULL (cson_rc.ArgError).
2464
2465 - cpdo cannot connect to the given DSN with the given
2466 username/password. Any error in establishing a connection causes
2467 cson_rc.IOError to be returned, as opposed to the underlying
2468 cpdo error code.
2469
2470 - Any of the "table" or "fieldXXX" properties are NULL. It
2471 needs these data in order to know where to load/save sessions.
2472
2473
2474 If any required options are missing, cson_rc.ArgError is
2475 returned.
2476
2477 TODO: add option "preferBlob", which can be used to set the db
2478 field type preference for the fieldSession field to
2479 blob. Currently it prefers string but will try blob operations
2480 if string ops fail. Blobs have the disadvantage of much larger
2481 encoded sizes but the advantage that the JSON data is encoded
2482 (at least by sqlite3) as a hex number stream, making it
2483 unreadable to casual observers.
2484
2485 @endcode
2486 */
2487 int cson_sessmgr_cpdo( cson_sessmgr ** tgt, cson_object const * opt );
2488
2489 /**
2490 This cson_sessmgr_factory_f() implementation might or might not
2491 be compiled in, depending on the mood of the cson
2492 maintainer. It is very niche-market, and primarily exists just
2493 to test (and show off) the whio_ht code.
2494
2495 It uses libwhio's on-storage hashtable (called whio_ht)
2496 as the underlying storage:
2497
2498 http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_ht
2499
2500 The opt object must not be NULL and must contain a single
2501 string property named "file" which contains the path to the
2502 whio_ht file to use for sessions. That file must have been
2503 previously created, either programatically using the whio_ht
2504 API or using whio-ht-tool:
2505
2506 http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_ht_tool
2507
2508 See cson_sessmgr_factory_f() for the semantics of the tgt
2509 argument and the return value.
2510
2511 Threading notes:
2512
2513 While the underlying hashtable supports a client-defined mutex,
2514 this usage of it does not set one (because we have no default
2515 one to use). What this means for clients is that they must not
2516 use this session manager from multiple threads, nor may they
2517 use multiple instances in the same process which use the same
2518 underlying hashtable file from multiple threads. How best to
2519 remedy this (allowing the client to tell this API what mutex to
2520 use) is not yet clear. Maybe a global whio_mutex object which
2521 the client must initialize before instantiating these session
2522 managers.
2523
2524 Storage Locking:
2525
2526 If the underlying filesystem reports that it supports file
2527 locking (via the whio_lock API, basically meaning POSIX
2528 fcntl()-style locking) the the session manager will use it. For
2529 the load() operation a read lock is acquired and for
2530 save()/remove() a write lock. The operations will fail if
2531 locking fails, the exception being if the device reports that
2532 it doesn't support locking, in which case we optimistically
2533 save/load/remove without locking.
2534
2535 Remember that in POSIX-style locking, a single process does not
2536 see its own locks and can overwrite locks set via other
2537 threads. This means that multi-threaded use of a given
2538 instance, or multiple instances in the same process using the
2539 same underlying hashtable file, will likely eventually corrupt
2540 the hashtable.
2541
2542 TODO:
2543
2544 - Add a config option to disable storage locking, for clients
2545 who really don't want to use it.
2546 */
2547 int cson_sessmgr_whio_ht( cson_sessmgr ** tgt, cson_object const * opt );
2548
2549 /**
2550 This cson_sessmgr_factory_f() implementation might or might not
2551 be compiled in, depending on the mood of the cson
2552 maintainer. It is very niche-market, and primarily exists just
2553 to test (and show off) the whio_epfs code.
2554
2555 It uses libwhio's embedded filesystem (called whio_epfs) as the
2556 underlying storage:
2557
2558 http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_epfs
2559
2560 The opt object must not be NULL and must contain a single
2561 string property named "file" which contains the path to the
2562 whio_epfs "container file" to use for storing sessions. That
2563 file must have been previously created, either programatically
2564 using the whio_epfs API or using whio-epfs-mkfs:
2565
2566 http://fossil.wanderinghorse.net/repos/whio/index.cgi/wiki/whio_epfs_mkfs
2567
2568 The EPFS container file MUST be created with a "namer"
2569 installed. See the above page for full details and examples.
2570
2571 See cson_sessmgr_factory_f() for the semantics of the tgt
2572 argument and the return value.
2573
2574 Threading notes:
2575
2576 - It is not legal to use this session manager from multiple threads.
2577 Doing so will eventually corrupt the underlying EFS if multiple writers
2578 work concurrently, and will also eventually _appear_ corrupt to multiple
2579 readers.
2580
2581 Storage locking:
2582
2583 The underlying storage (EFS container file) is locked (with a
2584 write lock) for the lifetime the the returned session manager
2585 IF the storage reports that it supports locking. Unlocked
2586 write access from an outside application will corrupt the EFS.
2587
2588 TODOs:
2589
2590 - Add config option to explicitly disable locking support.
2591 */
2592 int cson_sessmgr_whio_epfs( cson_sessmgr ** tgt, cson_object const * opt );
2593
2594 #if 0
2595 /** TODO? dummy manager which has no i/o support. */
2596 int cson_sessmgr_transient( cson_sessmgr ** tgt );
2597 #endif
2598
2599 /* LICENSE
2600
2601 This software's source code, including accompanying documentation and
2602 demonstration applications, are licensed under the following
2603 conditions...
2604
2605 Certain files are imported from external projects and have their own
2606 licensing terms. Namely, the JSON_parser.* files. See their files for
2607 their official licenses, but the summary is "do what you want [with
2608 them] but leave the license text and copyright in place."
2609
2610 The author (Stephan G. Beal [http://wanderinghorse.net/home/stephan/])
2611 explicitly disclaims copyright in all jurisdictions which recognize
2612 such a disclaimer. In such jurisdictions, this software is released
2613 into the Public Domain.
2614
2615 In jurisdictions which do not recognize Public Domain property
2616 (e.g. Germany as of 2011), this software is Copyright (c) 2011 by
2617 Stephan G. Beal, and is released under the terms of the MIT License
2618 (see below).
2619
2620 In jurisdictions which recognize Public Domain property, the user of
2621 this software may choose to accept it either as 1) Public Domain, 2)
2622 under the conditions of the MIT License (see below), or 3) under the
2623 terms of dual Public Domain/MIT License conditions described here, as
2624 they choose.
2625
2626 The MIT License is about as close to Public Domain as a license can
2627 get, and is described in clear, concise terms at:
2628
2629 http://en.wikipedia.org/wiki/MIT_License
2630
2631 The full text of the MIT License follows:
2632
---
2633 Copyright (c) 2011 Stephan G. Beal (http://wanderinghorse.net/home/stephan/)
2634
2635 Permission is hereby granted, free of charge, to any person
2636 obtaining a copy of this software and associated documentation
2637 files (the "Software"), to deal in the Software without
2638 restriction, including without limitation the rights to use,
2639 copy, modify, merge, publish, distribute, sublicense, and/or sell
2640 copies of the Software, and to permit persons to whom the
2641 Software is furnished to do so, subject to the following
2642 conditions:
2643
2644 The above copyright notice and this permission notice shall be
2645 included in all copies or substantial portions of the Software.
2646
2647 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2648 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
2649 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2650 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
2651 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
2652 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2653 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2654 OTHER DEALINGS IN THE SOFTWARE.
2655
---END OF MIT LICENSE--
2656
2657 For purposes of the above license, the term "Software" includes
2658 documentation and demonstration source code which accompanies
2659 this software. ("Accompanies" = is contained in the Software's
2660 primary public source code repository.)
2661
2662 */
2663
2664 #if defined(__cplusplus)
2665 } /*extern "C"*/
2666 #endif
2667
2668 #endif /* WANDERINGHORSE_NET_CSON_SESSION_H_INCLUDED */
2669 /* end file include/wh/cson/cson_session.h */
2670 /* begin file include/wh/cson/cson_cgi.h */
2671 #if !defined(WANDERINGHORSE_NET_CSON_CGI_H_INCLUDED)
2672 #define WANDERINGHORSE_NET_CSON_CGI_H_INCLUDED 1
2673
2674 /** @page page_cson_cgi cson CGI API
2675
2676 cson_cgi is a small framework encapsulating features usefor for
2677 writing JSON-only applications (primarily CGI apps) in C. It is based
2678 off of the cson JSON library:
2679
2680 http://fossil.wanderinghorse.net/repos/cson/
2681
2682 In essence, it takes care of the basic CGI-related app setup, making
2683 the data somewhat more accessible for client purposes. Clients
2684 create, as output, a single JSON object. The framework takes care of
2685 outputing it, along with any necessary HTTP headers.
2686
2687
2688 Primary features:
2689
2690 - Uses cson for its JSON handling, so it's pretty simple to use.
2691
2692 - Provides a simple-to-use mini-framework for writing CGI applications
2693 which generate only JSON output.
2694
2695 - Various sources of system data are converted by the framework to
2696 JSON for use by the client. This includes HTTP GET, OS environment,
2697 command-line arguments, HTTP cookies, and (with some limitations) HTTP
2698 POST.
2699
2700 - Can read unencoded JSON POST data (TODO: read in form-urlencoded as
2701 a JSON object).
2702
2703 - Supports an optional JSON config file.
2704
2705 - Optional persistent sessions using files, sqlite3, or MySQL5
2706 for the storage.
2707
2708
2709 Primary misfeatures:
2710
2711 - Very young and not yet complete.
2712
2713 - Intended for writing apps which ONLY generate JSON data, not HTML.
2714
2715 - JSONP output support is currently incomplete.
2716
2717 - TODO: support reading of POSTed Array data (currently only Objects
2718 work).
2719
2720 - We're missing a good number of convenience functions.
2721
2722 - Add client API for setting cookies. Currently they can be fetched
2723 or removed but not explicitly set.
2724
2725
2726 Other potential TODOs:
2727
2728 - Session support using session cookies for the IDs. For this to work
2729 we also need to add a storage back-end or two (files, db (e.g. using
2730 cpdo), etc.) and have a fast, good source of random numbers for
2731 generating UUIDs. It _seems_ that using the address of (extern char **
2732 environ) as a seed might be fairly random, but there are probably
2733 environments where that won't suffice. i want to avoid
2734 platform-specific bits, like /dev/urandom, if possible.
2735
2736 - Add client-definable i/o routines. We have this code in the whprintf
2737 tree, but i was hoping to avoid having to import that here. This would
2738 allow a client to add, e.g., gzip support.
2739
2740 */
2741
2742 /** @page page_cson_cgi_session cson CGI Sessions
2743
2744 cson_cgi_init() will initialize a persistent session if it can figure
2745 out everything it needs in order to do so. If it cannot it will simply
2746 skip session initialization. A client can tell if a session was
2747 established or not by calling cson_cgi_get_env_val(cx,'s',0). If that
2748 returns NULL then no session was created during initialization.
2749 The API currently provides no way to initialize one after the fact.
2750 That is, client code can use cson_cgi_get_env_val(cx,'s',1) to create a
2751 session object, but it won't automatically be persistent across
2752 application sessions.
2753
2754 Session management is really just the following:
2755
2756 - Load a JSON object from some persistent storage.
2757 - Save that object at shutdown.
2758
2759 "Persistent storage" is the important phrase here. How the sessions
2760 are saved is really unimportant. The library uses a generic interface
2761 (cson_sessmgr) to handle the i/o, and can use any implementation we
2762 care to provide. As of this writing (20110413) files, sqlite3, and
2763 MySQL5 are supported.
2764
2765 The session object is a JSON object available via cson_cgi_getenv(),
2766 using "s" as the environment name (the second parameter to
2767 cson_cgi_getenv()).
2768
2769 At library shutdown (typically when main() exits, but also via
2770 cson_cgi_cx_clean()), the session is saved using the current
2771 session manager. If the session is not loaded during initialization,
2772 but is created later, the library will assign a new session ID to it
2773 before saving.
2774
2775 See cson_cgi_config_file() for examples of configuring the session
2776 management.
2777 */
2778
2779
2780 #if defined(__cplusplus)
2781 extern "C" {
2782 #endif
2783
2784 /** @def CSON_CGI_ENABLE_POST_FORM_URLENCODED
2785
2786 If CSON_CGI_ENABLE_POST_FORM_URLENCODED is set to a true value
2787 then the API will try to process form-urlencoded POST data,
2788 otherwise it will not.
2789
2790 Reminder to self: this is basically a quick hack for fossil
2791 integration. We disable form encoding in that build because fossil
2792 handles that itself and we must not interfere with it.
2793 */
2794 #if !defined(CSON_CGI_ENABLE_POST_FORM_URLENCODED)
2795 #define CSON_CGI_ENABLE_POST_FORM_URLENCODED 0
2796 #endif
2797 /** @def CSON_CGI_GETENV_DEFAULT
2798
2799 The default environment name(s) to use for cson_cgi_getenv().
2800 */
2801 #define CSON_CGI_GETENV_DEFAULT "gpce"
2802 /** @def CSON_CGI_KEY_JSONP
2803
2804 TODO?: get rid of this and move cson_cgi_keys into the public
2805 API?
2806
2807 The default environment key name to use for checking whether a
2808 response should used JSONP or not.
2809 */
2810 #define CSON_CGI_KEY_JSONP "jspon"
2811
2812 #define CSON_CGI_KEY_SESSION "CSONSESSID"
2813
2814
2815 typedef struct cson_cgi_init_opt cson_cgi_init_opt;
2816 /** @struct cson_cgi_init_opt
2817
2818 A type to hold core runtime configuration information for
2819 cson_cgi.
2820 */
2821 struct cson_cgi_init_opt
2822 {
2823 /**
2824 stdin stream. If NULL, stdin is used.
2825 */
2826 FILE * inStream;
2827 /**
2828 stdout stream. If NULL, stdout is used.
2829 */
2830 FILE * outStream;
2831 /**
2832 stderr stream. If NULL, stderr is used.
2833 */
2834 FILE * errStream;
2835 /**
2836 Path to a JSON config file.
2837 */
2838 char const * configFile;
2839
2840 /**
2841 If set then the session will be forced to use this
2842 ID, otherwise one will be generated.
2843 */
2844 char const * sessionID;
2845
2846 /**
2847 Values are interpretted as:
2848
2849 0 = do not output headers when cson_cgi_response_output_all()
2850 is called.
2851
2852 (>0) = always output headers when cson_cgi_response_output_all()
2853 is called.
2854
2855 (<0) = Try to determine, based on environment variables, if
2856 we are running in CGI mode. If so, output the headers when
2857 cson_cgi_response_output_all() is called, otherwise omit
2858 them.
2859
2860 If the headers are omitted then so is the empty line
2861 which normally separates them from the response body!
2862
2863 The intention of this flag is to allow non-CGI apps
2864 to disable output of the HTTP headers.
2865 */
2866 char httpHeadersMode;
2867
2868 /**
2869 JSON output options.
2870 */
2871 cson_output_opt outOpt;
2872
2873 };
2874
2875 /** Empty-initialized cson_cgi_init_opt object. */
2876 #define cson_cgi_init_opt_empty_m { \
2877 NULL/*inStream*/, NULL/*outStream*/, NULL/*errStream*/, \
2878 NULL /*configFile*/, NULL /*sessionID*/, \
2879 -1/*httpHeadersMode*/, \
2880 cson_output_opt_empty_m /*outOpt*/ \
2881 }
2882
2883 /** Empty-initialized cson_cgi_init_opt object. */
2884 extern const cson_cgi_init_opt cson_cgi_init_opt_empty;
2885
2886
2887 /** @struct cson_cgi_env_map
2888 Holds a cson_object and its cson_value parent
2889 reference.
2890 */
2891 struct cson_cgi_env_map
2892 {
2893 /** Parent reference of jobj. */
2894 cson_value * jval;
2895 /** Object reference of jval. */
2896 cson_object * jobj;
2897 };
2898 typedef struct cson_cgi_env_map cson_cgi_env_map;
2899
2900 /** Empty cson_cgi_env_map object. */
2901 #define cson_cgi_env_map_empty_m { NULL, NULL }
2902
2903 /** @struct cson_cgi_cx
2904
2905 Internal state used by cson_cgi.
2906
2907 Clients must not rely on its internal structure. It is in the
2908 public API so that it can be stack- or custom-allocated. To
2909 properly initialize such an object, use cson_cgi_cx_empty_m
2910 cson_cgi_cx_empty, depending on the context.
2911 */
2912 struct cson_cgi_cx
2913 {
2914 /**
2915 Various key/value stores used by the framework. Each
2916 corresponds to some convention source of key/value
2917 pairs, e.g. QUERY_STRING or POST parameters.
2918 */
2919 struct {
2920 /**
2921 The system's environment variables.
2922 */
2923 cson_cgi_env_map env;
2924 /**
2925 Holds QUERY_STRING key/value pairs.
2926 */
2927 cson_cgi_env_map get;
2928 /**
2929 Holds POST form/JSON data key/value pairs.
2930 */
2931 cson_cgi_env_map post;
2932 /**
2933 Holds cookie key/value pairs.
2934 */
2935 cson_cgi_env_map cookie;
2936 /**
2937 Holds request headers.
2938 */
2939 cson_cgi_env_map headers;
2940 } request;
2941 /**
2942 Holds data related to the response JSON.
2943 */
2944 struct {
2945 /**
2946 HTTP error code to report.
2947 */
2948 int httpCode;
2949
2950 /**
2951 Root JSON object. Must be an Object or Array (per the JSON
2952 spec).
2953 */
2954 cson_value * root;
2955 /**
2956 Holds HTTP response headers as an array of key/value
2957 pairs.
2958 */
2959 cson_cgi_env_map headers;
2960 } response;
2961 /**
2962 A place to store cson_value references
2963 for cleanup purposes.
2964 */
2965 cson_cgi_env_map gc;
2966 /**
2967 Holds client-defined key/value pairs.
2968 */
2969 cson_cgi_env_map clientEnv;
2970 cson_cgi_env_map config;
2971 struct {
2972 cson_cgi_env_map env;
2973 cson_sessmgr * mgr;
2974 char * id;
2975 } session;
2976 struct {
2977 cson_array * jarr;
2978 cson_value * jval;
2979 } argv;
2980
2981 cson_cgi_init_opt opt;
2982 cson_buffer tmpBuf;
2983 struct {
2984 char isJSONP;
2985 void const * allocStamp;
2986 } misc;
2987 };
2988 typedef struct cson_cgi_cx cson_cgi_cx;
2989 /**
2990 Empty-initialized cson_cgi_cx object.
2991 */
2992 extern const cson_cgi_cx cson_cgi_cx_empty;
2993 /**
2994 Empty-initialized cson_cgi_cx object.
2995 */
2996 #define cson_cgi_cx_empty_m \
2997 { \
2998 { /*maps*/ \
2999 cson_cgi_env_map_empty_m /*env*/, \
3000 cson_cgi_env_map_empty_m /*get*/, \
3001 cson_cgi_env_map_empty_m /*post*/, \
3002 cson_cgi_env_map_empty_m /*cookie*/, \
3003 cson_cgi_env_map_empty_m /*headers*/ \
3004 }, \
3005 {/*response*/ \
3006 0 /*httpCode*/, \
3007 NULL /*root*/, \
3008 cson_cgi_env_map_empty_m /*headers*/ \
3009 }, \
3010 cson_cgi_env_map_empty_m /*gc*/, \
3011 cson_cgi_env_map_empty_m /*clientEnv*/, \
3012 cson_cgi_env_map_empty_m /*config*/, \
3013 {/*session*/ \
3014 cson_cgi_env_map_empty_m /*env*/, \
3015 NULL /*mgr*/, \
3016 NULL /*id*/ \
3017 }, \
3018 {/*argv*/ \
3019 NULL /*jarr*/, \
3020 NULL /*jval*/ \
3021 }, \
3022 cson_cgi_init_opt_empty_m /*opt*/, \
3023 cson_buffer_empty_m /* tmpBuf */, \
3024 {/*misc*/ \
3025 -1 /*isJSONP*/, \
3026 NULL/*allocStamp*/ \
3027 } \
3028 }
3029
3030 cson_cgi_cx * cson_cgi_cx_alloc();
3031 /**
3032 Cleans up all internal state of cx. IFF cx was allocated by
3033 cson_cgi_cx_alloc() then cx is also free()d, else it is assumed
3034 to have been allocated by the caller (possibly on the stack).
3035
3036 Returns 1 if cx is not NULL and this function actually frees
3037 it. If it returns 0 then either cx is NULL or this function
3038 cleaned up its internals but did not free(cx) (cx is assumed to
3039 have been allocated by the client).
3040 */
3041 char cson_cgi_cx_clean(cson_cgi_cx * cx);
3042
3043 /**
3044 Initializes the internal cson_cgi environment and must be
3045 called one time, from main(), at application startup.
3046
3047 cx must be either:
3048
3049 - Created via cson_cgi_cx_alloc().
3050
3051 - Alternately allocated (e.g. on the stack) and initialized
3052 by copying cson_cgi_cx_empty over it.
3053
3054 Any other initialization leads to undefined behaviour.
3055
3056 Returns 0 on success. On error the problem is almost certainly
3057 an allocation error. If it returns non-0 then the rest of the
3058 API will not work (and using the rest of the API invokes
3059 undefined behaviour unless documented otherwise for a specific
3060 function), so the application should exit immediately with an
3061 error code. The error codes returned by this function all come
3062 from the cson_rc object.
3063
3064 The returned object must eventually be passed to
3065 cson_cgi_cx_clean(), regardless of success or failure, to clean
3066 up any resources allocated for the object.
3067
3068 On success:
3069
3070 - 0 is returned.
3071
3072 - The cx object takes over ownership of any streams set in the
3073 opt object UNLESS they are the stdin/stdout/stderr streams (in
3074 which case ownership does not change).
3075
3076 On error non-0 is returned and ownership of the opt.xxStream
3077 STILL transfers over to cx as described above (because this
3078 simpifies client-side error handling ).
3079
3080 The 'opt' parameter can be used to tweak certain properties
3081 of the framework. It may be NULL, in which case defaults are
3082 used.
3083
3084 This function currently performs the following initialization:
3085
3086 - Parses QUERY_STRING environment variable into a JSON object.
3087
3088 - Parses the HTTP_COOKIE environment variable into a JSON object.
3089
3090 - Transforms the system environment to JSON.
3091
3092 - Copies the list of arguments (argv, in the form conventional
3093 for main()) to JSON array form for downstream use by the client
3094 application. It does not interpret these arguments in any
3095 way. Clients may use cson_cgi_argv() and
3096 cson_cgi_argv_array() to fetch the list later on in the
3097 application's lifetime (presumably outside of main()). It is
3098 legal to pass (argv==NULL) only if argc is 0 or less.
3099
3100 - If the CONTENT_TYPE env var is one of (application/json,
3101 application/javascript, or text/plain) and CONTENT_LENGTH is
3102 set then stdin is assumed to be JSON data coming in via POST.
3103 An error during that parsing is ignored for initialization purposes
3104 unless it is an allocation error, in which case it is propagated
3105 back to the caller of this function.
3106
3107 - If the CSON_CGI_CONFIG env var is set then that file is read.
3108 Errors in loading the config are silently ignored.
3109
3110 - If session management is properly configured in the
3111 configuration file and if a variable named CSON_CGI_KEY_SESSION
3112 is found in the environment (cookies, GET, POST, or system env)
3113 then the previous session is loaded. If it cannot be loaded,
3114 the error is ignored. (Note that the cookie name can be
3115 changed via the configuration file.)
3116
3117 TODOs:
3118
3119 - Add config file option to the opt object.
3120
3121 - Only read POST data when REQUEST_METHOD==POST?
3122
3123 - Convert form-urlencoded POST data to a JSON object.
3124
3125 - Potentially add an option to do automatic data type detection
3126 for numeric GET/POST/ENV/COOKIE data, such that fetching the
3127 cson_value for such a key would return a numeric value object
3128 as opposed to a string. Or we could add that option in a
3129 separate function which walks a JSON Object and performs that
3130 check/transformation on all of its entries. That currently
3131 can't be done properly with the cson_object_iterator API
3132 because changes to the object while looping invalidate the
3133 iterator. This option would also open up problems when clients
3134 pass huge strings which just happen to look like numbers.
3135
3136
3137 @see cson_cgi_config_file()
3138 */
3139 int cson_cgi_init( cson_cgi_cx * cx, int argc, char const * const * argv, cson_cgi_init_opt * options );
3140
3141 /**
3142 Searches for a value from the CGI environment. The fromWhere
3143 parameter is a NUL-terminated string which specifies which
3144 environment(s) to check, and may be made up of any of the
3145 letters [gprecl], case-insensitive. If fromWhere is NULL or its
3146 first byte is NUL (i.e. it is empty) then the default value
3147 defined in CSON_CGI_GETENV_DEFAULT is used.
3148
3149 The environments are searched in the order specified in
3150 fromWhere. The various letters mean:
3151
3152 - g = GET: key/value pairs parsed from the QUERY_STRING
3153 environment variable.
3154
3155 - p = POST: form-encoded key/value pairs parsed from stdin.
3156
3157 - r = REQUEST, equivalent to "gpc", a superset of GET/POST/COOKIE.
3158
3159 - e = ENV, e.g. via getenv(), but see cson_cgi_env_get_val()
3160 for more details.
3161
3162 - c = COOKIE: request cookies (not response cookies) parsed
3163 from the HTTP_COOKIE environment variable.
3164
3165 - a = APP: an environment namespace reserved for client app use.
3166
3167 - f = CONFIG FILE.
3168
3169 - Use key 's' for the SESSION.
3170
3171 Invalid characters are ignored.
3172
3173 The returned value is owned by the cson_cgi environment and
3174 must not be destroyed by the caller. NULL is returned if none
3175 of the requested environments contain the given key.
3176
3177 Results are undefined if fromWhere is not NULL and is not
3178 NUL-terminated.
3179
3180 TODOs:
3181
3182 - Replace CSON_CGI_GETENV_DEFAULT with a runtime-configurable
3183 value (via a config file).
3184
3185 */
3186 cson_value * cson_cgi_getenv( cson_cgi_cx * cx, char const * fromWhere, char const * key );
3187
3188 /**
3189 A convenience form of cson_cgi_getenv() which returns the given
3190 key as a string. This will return NULL if the requested key
3191 is-not-a string value. It does not convert non-string values to
3192 strings.
3193
3194 On success the string value is returned. Its bytes are owned by
3195 this API and are valid until the given key is removed/replaced
3196 from/in the environment object it was found in or that
3197 environment object is cleaned up.
3198 */
3199 char const * cson_cgi_getenv_cstr( cson_cgi_cx * cx, char const * where, char const * key );
3200
3201 /**
3202 During initialization, if the PATH_INFO environment variable is set,
3203 it is split on '/' characters into array. That array is stored in the
3204 environment with the name PATH_INFO_SPLIT. This function returns the
3205 element of the PATH_INFO at the given index, or NULL if ndx is out
3206 of bounds or if no PATH_INFO is available.
3207
3208 e.g. if PATH_INFO=/a/b/c, passing 0 to this function would return
3209 "a", passing 2 would return "c", and passing anything greater than 2
3210 would return NULL.
3211 */
3212 char const * cson_cgi_path_part_cstr( cson_cgi_cx * cx, unsigned short ndx );
3213
3214 /**
3215 Functionally equivalent to cson_cgi_path_part_cstr(), but
3216 returns the underlying value as a cson value handle. That handle
3217 is owned by the underlying PATH_INFO_SPLIT array (which is
3218 owned by the "e" environment object).
3219
3220 Unless the client has mucked with the PATH_INFO_SPLIT data, the
3221 returned value will (if it is not NULL) have a logical type of
3222 String.
3223 */
3224 cson_value * cson_cgi_path_part( cson_cgi_cx * cx, unsigned short ndx );
3225
3226 /**
3227 Sets or unsets a key in the "user" environment/namespace. If v is NULL
3228 then the value is removed, otherwise it is set/replaced.
3229
3230 Returns 0 on success. If key is NULL or has a length of 0 then
3231 cson_rc.ArgError is returned.
3232
3233 The user namespace object can be fetched via
3234 cson_cgi_env_get_val('a',...).
3235
3236 On success ownership of v is transfered to (or shared with) the
3237 cson_cgi API. On error ownership of v is not modified. Aside from
3238 */
3239 int cson_cgi_setenv( cson_cgi_cx * cx, char const * key, cson_value * v );
3240
3241 /**
3242 This function is not implemented, but exists as a convenient
3243 place to document the cson_cgi config file format.
3244
3245 cson_cgi_init() accepts the name of a configuration file
3246 (assumed to be in JSON format) to read during
3247 initialization. The library optionally uses the configuration
3248 to change certain aspects of its behaviour.
3249
3250 The following commented JSON demonstrates the configuration
3251 file options:
3252
3253 @code
3254 {
3255 "formatting": { // NOT YET HONORED. Will mimic cson_output_opt.
3256 "indentation": 1,
3257 "addNewline": true,
3258 "addSpaceAfterColon": true,
3259 "indentSingleMemberValues": true
3260 },
3261 "session": { // Options for session handling
3262 "manager": "file", // name of session manager impl. Should
3263 // have a matching entry in "managers" (below)
3264 "cookieLifetimeMinutes": 10080, // cookie lifetime in minutes
3265 "cookieName": "cson_session_id", // cookie name for session ID
3266 "managers": {
3267 "file": {
3268 "sessionDriver": "file", -- cson_cgi-internal session manager name
3269 "dir": "./SESSIONS",
3270 "prefix": "cson-session-",
3271 "suffix": ".json"
3272 },
3273 "mysql5": {
3274 "sessionDriver": "cpdo", -- cson_cgi-internal session manager name
3275 "dsn": "mysql5:dbname=cpdo;host=localhost",
3276 "user": "cpdo",
3277 "password": "cpdo",
3278 "table": "cson_session",
3279 "fieldId": "id",
3280 "fieldTimestamp": "last_saved",
3281 "fieldSession": "json"
3282 },
3283 "sqlite3": {
3284 "sessionDriver": "cpdo", -- cson_cgi-internal session manager name
3285 "dsn": "sqlite3:sessions.sqlite3",
3286 "user": null,
3287 "password": null,
3288 "table": "cson_session",
3289 "fieldId": "id",
3290 "fieldTimestamp": "last_saved",
3291 "fieldSession": "json"
3292 }
3293 }
3294 }
3295 }
3296 @endcode
3297
3298 TODO: allow initialization to take a JSON object, as opposed to
3299 a filename, so that we can embed the configuration inside client-side
3300 config data.
3301 */
3302 void cson_cgi_config_file();
3303
3304 /**
3305 Sets or (if v is NULL) unsets a cookie value.
3306
3307 v must either be of one of the types (string, integer, double,
3308 bool, null, NULL) or must be an object with the following
3309 structure:
3310
3311 @code
3312 {
3313 value: (string, integer, double, bool, or null),
3314 OPTIONAL path: string,
3315 OPTIONAL domain: string,
3316 OPTIONAL expires: integer (Unix epoch timestamp),
3317 OPTIONAL secure: bool,
3318 OPTIONAL httponly: bool
3319 }
3320 @endcode
3321
3322 For the object form, if the "value" property is missing or not of
3323 the correct type then the cookie will not be emitted in the
3324 HTTP response headers. The other properties are optional. A value
3325 of NULL or cson_value_null() will cause the expiry field (if set)
3326 to be ignored. Note, however, that removal will only work
3327 on the client side if all other cookie parameters match
3328 (e.g. domain and path).
3329
3330 Returns 0 on success, non-0 on error.
3331
3332 A duplicate cookie replaces any previous cookie with the same
3333 key.
3334
3335 On success ownership of v is shared with the cson_cgi API (via
3336 reference counting). On error ownership of v is not modified.
3337 */
3338 int cson_cgi_cookie_set( cson_cgi_cx * cx, char const * key, cson_value * v );
3339
3340 /**
3341 Sets or (if v is NULL) unsets an HTTP cookie value. key may not
3342 be NULL nor have a length of 0. v must be one of the types
3343 (string, integer, double, bool, null, NULL). Any other pointer
3344 arguments may be NULL, in which case they are not used.
3345 If v is NULL then the JSON null value is used as a placeholder
3346 value so that when the HTTP headers are generated, the cookie
3347 can be unset on the client side.
3348
3349 This function creates an object with the structure documented
3350 in cson_cgi_cookie_set() and then passes that object to
3351 cson_cgi_cookie_set(). Any parameters which have NULL/0 values
3352 are not emitted in the generated object, with the exception of
3353 (v==NULL), which causes the expiry property to be ignored and a
3354 value from a time far in the past to be used (so that the
3355 client will expire it)..
3356
3357 Returns 0 on success, non-0 on error.
3358
3359 On success ownership of v is shared with the cson_cgi API (via
3360 reference counting). On error ownership of v is not modified.
3361 */
3362 int cson_cgi_cookie_set2( cson_cgi_cx * cx, char const * key, cson_value * v,
3363 char const * domain, char const * path,
3364 unsigned int expires, char secure, char httponly );
3365
3366 /**
3367 Returns the internal "environment" JSON object corresponding
3368 to the given 'which' letter, which must be one of
3369 (case-insensitive):
3370
3371 - g = GET
3372 - p = POST
3373 - c = COOKIE
3374 - e = ENV (i.e. system environment)
3375 - s = SESSION
3376 - a = APP (application-specific)
3377
3378 TODO: s = SESSION
3379
3380 See cson_cgi_getenv() for more details about each of those.
3381
3382 Returns NULL if 'which' is not one of the above.
3383
3384 Note that in the 'e' (system environment) case, making
3385 modifications to the returned object will NOT also modify the
3386 system environment. Likewise, future updates to the system
3387 environment will not be automatically reflected in the
3388 returned object.
3389
3390 The returned object is owned by the cson_cgi environment and
3391 must not be destroyed by the caller.
3392
3393 If createIfNeeded is non-0 (true) then the requested
3394 environment object is created if it was formerly empty. In that
3395 case, a return value of NULL can indicate an invalid 'which'
3396 parameter or an allocation error.
3397
3398 To get the Object reference to this environment use
3399 cson_cgi_env_get_obj() or pass the result of this function
3400 to cson_value_get_object().
3401
3402 The returned value is owned by the cson_cgi API.
3403
3404 The public API does not provide a way for clients to modify
3405 several of the internal environment stores, e.g. HTTP GET
3406 parameters are set only by this framework. However, clients
3407 can (if needed) get around this by fetching the associated
3408 "environment object" via this function or
3409 cson_cgi_env_get_obj(), and modifying it directly. Clients are
3410 encouraged to use the other public APIs for dealing with the
3411 environment, however, and are encouraged to not directly modify
3412 "special" namespaces like the cookie/GET/POST data.
3413 */
3414 cson_value * cson_cgi_env_get_val( cson_cgi_cx * cx, char which, char createIfNeeded );
3415
3416 /**
3417 Equivalent to:
3418
3419 @code
3420 cson_value_get_object( cson_cgi_env_get_val( which, createIfNeeded ) );
3421 @endcode
3422
3423 Note, however, that it is at least theoretically possible that
3424 cson_cgi_env_get_val() return non-NULL but this function
3425 returns NULL. If that happens it means that the value returned
3426 by cson_cgi_env_get_val() is-not-a Object instance, but is
3427 something else (maybe an array?).
3428 */
3429 cson_object * cson_cgi_env_get_obj( cson_cgi_cx * cx, char which, char createIfNeeded );
3430
3431 /**
3432 Adds the given key/value to the list of HTTP headers (replacing
3433 any existing entry with the same name). If v is NULL then any
3434 header with the given key is removed from the pending response.
3435
3436 Returns 0 on success. On success ownership of v is transfered
3437 to (or shared with) the internal header list. On error,
3438 ownership of v is not modified.
3439
3440 If v is not of one of the types (string, integer, double, bool,
3441 undef, null) then the header will not be output when when
3442 cson_cgi_response_output_headers() is called. If it is one of
3443 those types then its stringified value will be its "natural"
3444 form (for strings and numbers), the integer 0 or 1 for
3445 booleans, and the number 0 for null. Note that a literal
3446 (v==NULL) is treated differently from a JSON null - it UNSETS
3447 the given header.
3448
3449 This function should not be used for setting cookies, as they
3450 require extra url-encoding and possibly additional
3451 parameters. Use cson_cgi_cookie_set() and
3452 cson_cgi_cookie_set2() to set cookie headers.
3453 */
3454 int cson_cgi_response_header_add( cson_cgi_cx * cx, char const * key, cson_value * v );
3455
3456 /**
3457 Returns a cson array value containing the arguments passed
3458 to cson_cgi_init(). The returned value is owned by the cson_cgi
3459 API and must not be destroyed by the caller.
3460
3461 Only returns NULL if initialization of cson_cgi_init() fails
3462 early on, and is almost certainly indicative of an allocation
3463 error. If cson_cgi_init() is given a 0-length argument list
3464 then this function will return an empty array (except in the
3465 NULL case mentioned above).
3466 */
3467 cson_value * cson_cgi_argv(cson_cgi_cx * cx);
3468
3469 /**
3470 Equivalent to:
3471
3472 @code
3473 cson_value_get_array( cson_cgi_argv() );
3474 @endcode
3475 */
3476 cson_array * cson_cgi_argv_array(cson_cgi_cx * cx);
3477
3478 /**
3479 Flushes all response headers set via cson_cgi_response_header_add()
3480 to stdout. The client must output an empty line before the body
3481 part (if any), and may output custom headers before doing so.
3482
3483 Do not call this more than once.
3484 */
3485 int cson_cgi_response_output_headers(cson_cgi_cx * cx);
3486
3487 /**
3488 Outputs the response root object to stdout. If none has been
3489 set, non-0 is returned.
3490
3491 Returns 0 on success. On error, partial output might be
3492 generated.
3493
3494 Do not call this more than once.
3495 */
3496 int cson_cgi_response_output_root(cson_cgi_cx * cx);
3497
3498 /**
3499 Outputs the whole response, including headers and the root JSON
3500 value.
3501
3502 Returns 0 on success. Fails without side effects if
3503 no root is set.
3504
3505 Do not call this more than once.
3506 */
3507 int cson_cgi_response_output_all(cson_cgi_cx * cx);
3508
3509 /**
3510 Don't use this - i need to re-think the JSONP bits.
3511
3512 Returns non-0 (true) if the GET/POST environment contains a key
3513 named CSON_CGI_KEY_JSONP. If this is the case, then when
3514 cson_cgi_response_output_headers() is called the Content-type
3515 is set to "application/javascript".
3516
3517 If cson_cgi_enable_jsonp() is ever called to set this option
3518 explicitly, this function does not guess, but uses that value
3519 instead.
3520
3521 When JSONP is desired, the generated page output must be
3522 wrapped in the appropriate JS code.
3523 */
3524 char cson_cgi_is_jsonp(cson_cgi_cx * cx);
3525
3526 /**
3527 Don't use this - i need to re-think the JSONP bits.
3528
3529 Sets or unsets JSONP mode. If b is 0 then JSONP guessing is
3530 explicitly disabled and output is assumed to be JSON. If it is
3531 non-0 then cson_cgi_guess_content_type() will never return
3532 "application/javascript".
3533
3534 When JSONP is desired, the generated page output must be
3535 wrapped in the appropriate JS code.
3536 */
3537 void cson_cgi_enable_jsonp( cson_cgi_cx * cx, char b );
3538
3539 /**
3540 Tries to guess the "best" Content-type header value for
3541 the current session, based on several factors:
3542
3543 - If the GET/POST data contains a variable named
3544 CSON_CGI_KEY_JSONP then "application/javascript" is returned.
3545
3546 - If the HTTP_ACCEPT environment variable is NOT set or
3547 contains "application/json" then "application/json [possibly
3548 charset info]" is returned.
3549
3550 - If the HTTP_ACCEPT environment variable is set but does not
3551 contain "application/json" then "text/javascript" is returned.
3552
3553 - If cson_cgi_enable_jsonp() is called and passed a true value,
3554 "application/javascript" is returned.
3555
3556
3557 If HTTP_ACCEPT_CHARSET is NOT set or contains "utf-8" then
3558 ";charset=utf-8" is included in the the returned string.
3559
3560 The returned string is static and immutable and suitable for
3561 use as a Content-type header value. The string is guaranteed to
3562 be valid until the application exits. Multiple calls to this
3563 function, with different underlying environment data, can cause
3564 different results to be returned.
3565
3566 Returns NULL if it absolutely cannot figure out what to do, but
3567 currently it has no such logic paths.
3568 */
3569 char const * cson_cgi_guess_content_type(cson_cgi_cx * cx);
3570
3571 /**
3572 Sets the response content root, replacing any
3573 existing one (and possibly cleaning it up).
3574
3575 Returns 0 on success. On success, ownership of v
3576 is transfered to (or shared with) the cson_cgi
3577 API. It will be cleaned up at app shutdown time
3578 or if it is subsequently replaced and has no
3579 other open references to it.
3580
3581 On error ownership of v is not modified and any previous
3582 root is not removed.
3583
3584 If v is-not-a Object or Array, nor NULL, then cson_rc.TypeError
3585 is returned. JSON requires either an object or array for the
3586 root node. Passing NULL will possibly free up any current root
3587 (depending on its reference count).
3588 */
3589 int cson_cgi_response_root_set( cson_cgi_cx * cx, cson_value * v );
3590
3591 /**
3592 Fetches the current content root JSON value, as set via
3593 cson_cgi_response_root_set() or (if no root has been set), as
3594 defined by createMode, as described below.
3595
3596 If a content root has been set (or previously initialized)
3597 then the value of createMode is ignored. If no root has been
3598 set then this function might try to create one, as described
3599 here:
3600
3601 (createMode==0) means not to create a root element if none
3602 exists already.
3603
3604 (createMode<0) means to create the root as an Array value.
3605
3606 (createMode>0) means to create the root as an Object value.
3607
3608 Returns NULL on allocation error or if no root has been set and
3609 (createMode==0). On success the returned value is guaranteed to
3610 be either an Array or Object (see cson_value_get_object() and
3611 cson_value_get_array()) and it is owned by the cson_cgi API (so
3612 it must not be destroyed by the caller). If the client needs to
3613 destroy it, pass NULL to cson_cgi_response_root_set().
3614 */
3615 cson_value * cson_cgi_response_root_get( cson_cgi_cx * cx, char createMode );
3616
3617 /**
3618 Returns the current session ID. If session management is not
3619 enabled then NULL is returned.
3620
3621 The returned bytes are owned by the cson_cgi API and are valid
3622 until the library is cleaned up (via cson_cgi_cleanup_lib() or
3623 via the normal shutdown process) or the session ID is
3624 re-generated for some reason. It is best not to hold a
3625 reference to this, but to copy it if it will be needed later.
3626
3627 If the return value is not NULL, it is guaranteed to be
3628 NUL-terminated.
3629 */
3630 char const * cson_cgi_session_id(cson_cgi_cx *);
3631
3632 /**
3633 Writes a 36-byte (plus one NUL byte) random UUID value to
3634 dest. dest must be at least 37 bytes long. If dest is NULL this
3635 function has no side effects.
3636
3637 This function uses internal RNG state and is not thread-safe.
3638 */
3639 void cson_cgi_generate_uuid( cson_cgi_cx * cx, char * dest );
3640
3641 /**
3642 Adds v to the API-internal cleanup mechanism. key must be a
3643 unique key for the given element. Adding another item with that
3644 key may free the previous one. If freeOnError is true then v is
3645 passed to cson_value_free() if the key cannot be inserted,
3646 otherweise ownership of v is not changed on error.
3647
3648 Returns 0 on success.
3649
3650 On success, ownership of v is transfered to (or shared with)
3651 cx, and v will be valid until cx is cleaned up or its key is
3652 replaced via another call to this function.
3653 */
3654 int cson_cgi_gc_add( cson_cgi_cx * cx, char const * key, cson_value * v, char freeOnError );
3655
3656 /* LICENSE
3657
3658 This software's source code, including accompanying documentation and
3659 demonstration applications, are licensed under the following
3660 conditions...
3661
3662 Certain files are imported from external projects and have their own
3663 licensing terms. Namely, the JSON_parser.* files. See their files for
3664 their official licenses, but the summary is "do what you want [with
3665 them] but leave the license text and copyright in place."
3666
3667 The author (Stephan G. Beal [http://wanderinghorse.net/home/stephan/])
3668 explicitly disclaims copyright in all jurisdictions which recognize
3669 such a disclaimer. In such jurisdictions, this software is released
3670 into the Public Domain.
3671
3672 In jurisdictions which do not recognize Public Domain property
3673 (e.g. Germany as of 2011), this software is Copyright (c) 2011 by
3674 Stephan G. Beal, and is released under the terms of the MIT License
3675 (see below).
3676
3677 In jurisdictions which recognize Public Domain property, the user of
3678 this software may choose to accept it either as 1) Public Domain, 2)
3679 under the conditions of the MIT License (see below), or 3) under the
3680 terms of dual Public Domain/MIT License conditions described here, as
3681 they choose.
3682
3683 The MIT License is about as close to Public Domain as a license can
3684 get, and is described in clear, concise terms at:
3685
3686 http://en.wikipedia.org/wiki/MIT_License
3687
3688 The full text of the MIT License follows:
3689
---
3690 Copyright (c) 2011 Stephan G. Beal (http://wanderinghorse.net/home/stephan/)
3691
3692 Permission is hereby granted, free of charge, to any person
3693 obtaining a copy of this software and associated documentation
3694 files (the "Software"), to deal in the Software without
3695 restriction, including without limitation the rights to use,
3696 copy, modify, merge, publish, distribute, sublicense, and/or sell
3697 copies of the Software, and to permit persons to whom the
3698 Software is furnished to do so, subject to the following
3699 conditions:
3700
3701 The above copyright notice and this permission notice shall be
3702 included in all copies or substantial portions of the Software.
3703
3704 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
3705 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
3706 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
3707 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
3708 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
3709 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
3710 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
3711 OTHER DEALINGS IN THE SOFTWARE.
3712
---END OF MIT LICENSE--
3713
3714 For purposes of the above license, the term "Software" includes
3715 documentation and demonstration source code which accompanies
3716 this software. ("Accompanies" = is contained in the Software's
3717 primary public source code repository.)
3718
3719 */
3720
3721 #if defined(__cplusplus)
3722 } /*extern "C"*/
3723 #endif
3724
3725 #endif /* WANDERINGHORSE_NET_CSON_CGI_H_INCLUDED */
3726 /* end file include/wh/cson/cson_cgi.h */
3727
--- src/cson_amalgamation.h
+++ src/cson_amalgamation.h
@@ -2044,10 +2044,59 @@
2044
2045 #if defined(__cplusplus)
2046 extern "C" {
2047 #endif
2048
2049 /**
2050 Converts a single value from a single 0-based column index to its JSON
2051 equivalent.
2052
2053 On success it returns a new JSON value, which will have a different concrete
2054 type depending on the field type reported by sqlite3_column_type(st,col):
2055
2056 Integer, double, null, or string (TEXT and BLOB data, though not
2057 all blob data is legal for a JSON string).
2058
2059 st must be a sqlite3_step()'d row and col must be a 0-based column
2060 index within that result row.
2061 */
2062 cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col );
2063
2064 /**
2065 Creates a JSON Array object containing the names of all columns
2066 of the given prepared statement handle.
2067
2068 Returns a new array value on success, which the caller owns. Its elements
2069 are in the same order as in the underlying query.
2070
2071 On error NULL is returned.
2072
2073 st is not traversed or freed by this function - only the column
2074 count and names are read.
2075 */
2076 cson_value * cson_sqlite3_column_names( sqlite3_stmt * st );
2077
2078 /**
2079 Creates a JSON Object containing key/value pairs corresponding
2080 to the result columns in the current row of the given statement
2081 handle. st must be a sqlite3_step()'d row result.
2082
2083 On success a new Object is returned which is owned by the
2084 caller. On error NULL is returned.
2085
2086 cson_sqlite3_column_to_value() is used to convert each column to a
2087 JSON value, and the column names are taken from
2088 sqlite3_column_name().
2089 */
2090 cson_value * cson_sqlite3_row_to_object( sqlite3_stmt * st );
2091
2092 /**
2093 Similar to cson_sqlite3_row_to_object(), but creates an Array
2094 value which contains the JSON-form values of the given result
2095 set row.
2096 */
2097 cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st );
2098 /**
2099 Converts the results of an sqlite3 SELECT statement to JSON,
2100 in the form of a cson_value object tree.
2101
2102 st must be a prepared, but not yet traversed, SELECT query.
@@ -2145,1586 +2194,5 @@
2194 #endif
2195
2196 #endif /* CSON_ENABLE_SQLITE3 */
2197 #endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */
2198 /* end file include/wh/cson/cson_sqlite3.h */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
---END OF MIT LICENSE--
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
---END OF MIT LICENSE--
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2199
+117 -48
--- src/json.c
+++ src/json.c
@@ -63,10 +63,19 @@
6363
** It is imperitive that NO callback functions EVER output ANYTHING to
6464
** stdout, as that will effectively corrupt any HTTP output.
6565
*/
6666
typedef cson_value * (*fossil_json_f)();
6767
68
+
69
+/*
70
+** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
71
+** (but planned) pages/commands.
72
+*/
73
+static cson_value * json_page_nyi(void){
74
+ g.json.resultCode = FSL_JSON_E_NYI;
75
+ return NULL;
76
+}
6877
6978
/*
7079
** Holds keys used for various JSON API properties.
7180
*/
7281
static const struct FossilJsonKeys_{
@@ -95,14 +104,14 @@
95104
C(UNKNOWN,"Unknown error");
96105
C(RESOURCE_NOT_FOUND,"Resource not found");
97106
C(TIMEOUT,"Timeout reached");
98107
C(ASSERT,"Assertion failed");
99108
C(ALLOC,"Resource allocation failed");
100
- C(NYI,"Not yet implemented.");
109
+ C(NYI,"Not yet implemented");
101110
C(AUTH,"Authentication error");
102111
C(LOGIN_FAILED,"Login failed");
103
- C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed.");
112
+ C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed");
104113
C(LOGIN_FAILED_NONAME,"Login failed - name not supplied");
105114
C(LOGIN_FAILED_NOPW,"Login failed - password not supplied");
106115
C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found");
107116
C(MISSING_AUTH,"Authentication info missing from request");
108117
C(DENIED,"Access denied");
@@ -115,10 +124,11 @@
115124
C(DB,"Database error");
116125
C(STMT_PREP,"Statement preparation failed");
117126
C(STMT_BIND,"Statement parameter binding failed");
118127
C(STMT_EXEC,"Statement execution/stepping failed");
119128
C(DB_LOCKED,"Database is locked");
129
+ C(DB_NEEDS_REBUILD,"Fossil repository needs to be rebuilt");
120130
#undef C
121131
default:
122132
return "Unknown Error";
123133
}
124134
}
@@ -189,19 +199,25 @@
189199
}
190200
191201
/*
192202
** Adds v to the API-internal cleanup mechanism. key must be a unique
193203
** key for the given element. Adding another item with that key may
194
-** free the previous one. If freeOnError is true then v is passed to
195
-** cson_value_free() if the key cannot be inserted, otherweise
196
-** ownership of v is not changed on error.
204
+** free the previous one (depending on its reference count). If
205
+** freeOnError is true then v is passed to cson_value_free() if the
206
+** key cannot be inserted, otherweise ownership of v is not changed on
207
+** error. Failure to insert a key may be caused by any of the
208
+** following:
209
+**
210
+** - Allocation error.
211
+** - g.json.gc.o is NULL
212
+** - key is NULL or empty.
197213
**
198214
** Returns 0 on success.
199215
**
200
-*** On success, ownership of v is transfered to (or shared with)
201
-*** g.json.gc, and v will be valid until that object is cleaned up or
202
-*** its key is replaced via another call to this function.
216
+** On success, ownership of v is transfered to (or shared with)
217
+** g.json.gc, and v will be valid until that object is cleaned up or
218
+** its key is replaced via another call to this function.
203219
*/
204220
int json_gc_add( char const * key, cson_value * v, char freeOnError ){
205221
int const rc = cson_object_set( g.json.gc.o, key, v );
206222
assert( NULL != g.json.gc.o );
207223
if( (0 != rc) && freeOnError ){
@@ -252,14 +268,15 @@
252268
return NULL;
253269
}
254270
255271
256272
/*
257
-** Returns the string form of a json_getenv() value, but ONLY
258
-** If that value is-a String. Non-strings are not converted
259
-** to strings for this purpose. Returned memory is owned by
260
-** g.json or fossil..
273
+** Returns the string form of a json_getenv() value, but ONLY If that
274
+** value is-a String. Non-strings are not converted to strings for
275
+** this purpose. Returned memory is owned by g.json or fossil and is
276
+** valid until end-of-app or the given key is replaced in fossil's
277
+** internals via cgi_replace_parameter() and friends or json_setenv().
261278
*/
262279
static char const * json_getenv_cstr( char const * zKey ){
263280
return cson_value_get_cstr( json_getenv(zKey) );
264281
}
265282
@@ -325,13 +342,15 @@
325342
/*
326343
** Returns the current request's JSON authentication token, or NULL if
327344
** none is found. The token's memory is owned by (or shared with)
328345
** g.json.
329346
**
330
-** If an auth token is found in the GET/POST JSON request data then
331
-** fossil is given that data for use in authentication for this
332
-** session.
347
+** If an auth token is found in the GET/POST request data then fossil
348
+** is given that data for use in authentication for this
349
+** session. i.e. the GET/POST data overrides fossil's authentication
350
+** cookie value (if any) and also works with clients which do not
351
+** support cookies.
333352
**
334353
** Must be called once before login_check_credentials() is called or
335354
** we will not be able to replace fossil's internal idea of the auth
336355
** info in time (and future changes to that state may cause unexpected
337356
** results).
@@ -385,14 +404,26 @@
385404
** Initializes g.json.gc and g.json.param.
386405
*/
387406
void json_main_bootstrap(){
388407
cson_value * v;
389408
assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" );
409
+
410
+ /* g.json.gc is our "garbage collector" - where we put JSON values
411
+ which need a long lifetime but don't have a logical parent to put
412
+ them in.
413
+ */
390414
v = cson_value_new_object();
391415
g.json.gc.v = v;
392416
g.json.gc.o = cson_value_get_object(v);
393417
418
+ /*
419
+ g.json.param holds the JSONized counterpart of fossil's
420
+ cgi_parameter_xxx() family of data. We store them as JSON, as
421
+ opposed to using fossil's data directly, because we can retain
422
+ full type information for data this way (as opposed to it always
423
+ being of type string).
424
+ */
394425
v = cson_value_new_object();
395426
g.json.param.v = v;
396427
g.json.param.o = cson_value_get_object(v);
397428
json_gc_add("$PARAMS", v, 1);
398429
}
@@ -497,20 +528,21 @@
497528
}
498529
}
499530
500531
/* g.json.reqPayload exists only to simplify some of our access to
501532
the request payload. We currently only use this in the context of
502
- Object payloads, not Arrays, strings, etc. */
533
+ Object payloads, not Arrays, strings, etc.
534
+ */
503535
g.json.reqPayload.v = cson_object_get( g.json.post.o, "payload" );
504536
if( g.json.reqPayload.v ){
505537
g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
506538
/* g.json.reqPayload.o may legally be NULL, which means only that
507539
g.json.reqPayload.v is-not-a Object.
508540
*/;
509541
}
510542
511
- do{/* set up JSON out formatting options. */
543
+ do{/* set up JSON output formatting options. */
512544
unsigned char indent = g.isCGI ? 0 : 1;
513545
cson_value const * indentV = json_getenv("indent");
514546
if(indentV){
515547
if(cson_value_is_string(indentV)){
516548
int const n = atoi(cson_string_cstr(cson_value_get_string(indentV)));
@@ -524,12 +556,14 @@
524556
}
525557
g.json.outOpt.indentation = indent;
526558
g.json.outOpt.addNewline = g.isCGI ? 0 : 1;
527559
}while(0);
528560
529
- json_auth_token()/* will copy our auth token, if any, to fossil's core. */;
530561
if( g.isCGI ){
562
+ json_auth_token()/* will copy our auth token, if any, to fossil's
563
+ core, which we need before we call
564
+ login_check_credentials(). */;
531565
login_check_credentials()/* populates g.perm */;
532566
}
533567
else{
534568
db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
535569
}
@@ -933,11 +967,14 @@
933967
/*
934968
** Implementation of the /json/login page.
935969
**
936970
*/
937971
cson_value * json_page_login(void){
938
- static char preciseErrors =
972
+ static char preciseErrors = /* if true, "complete" JSON error codes are used,
973
+ else they are "dumbed down" to a generic login
974
+ error code.
975
+ */
939976
#if 0
940977
g.json.errorDetailParanoia ? 0 : 1
941978
#else
942979
0
943980
#endif
@@ -1197,38 +1234,79 @@
11971234
cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
11981235
return jv;
11991236
#undef SETBUF
12001237
}
12011238
1239
+
1240
+static cson_value * json_wiki_list(void);
1241
+
1242
+/*
1243
+** Mapping of /json/wiki/XXX commands/paths to callbacks.
1244
+*/
1245
+static const JsonPageDef JsonPageDefs_Wiki[] = {
1246
+{"get", json_page_nyi, 0},
1247
+{"list", json_wiki_list, 0},
1248
+{"save", json_page_nyi, 1},
1249
+/* Last entry MUST have a NULL name. */
1250
+{NULL,NULL,0}
1251
+};
1252
+
12021253
/*
12031254
** Implements the /json/wiki family of pages/commands. Far from
12041255
** complete.
12051256
**
12061257
*/
1207
-cson_value * json_page_wiki(void){
1208
- cson_value * jlist = NULL;
1209
- cson_value * rows = NULL;
1210
- Stmt q;
1211
- wiki_prepare_page_list(&q);
1212
- cson_sqlite3_stmt_to_json( q.pStmt, &jlist, 1 );
1213
- db_finalize(&q);
1214
- assert( NULL != jlist );
1215
- rows = cson_object_take( cson_value_get_object(jlist), "rows" );
1216
- assert( NULL != rows );
1217
- cson_value_free( jlist );
1218
- return rows;
1258
+static cson_value * json_page_wiki(void){
1259
+ JsonPageDef const * def;
1260
+ char const * cmd = json_command_arg(2);
1261
+ if( ! cmd ){
1262
+ g.json.resultCode = FSL_JSON_E_MISSING_AUTH;
1263
+ return NULL;
1264
+ }
1265
+ def = json_handler_for_name( cmd, &JsonPageDefs_Wiki[0] );
1266
+ if(!def){
1267
+ g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND;
1268
+ return NULL;
1269
+ }
1270
+ else{
1271
+ return (*def->func)();
1272
+ }
12191273
}
12201274
12211275
/*
1222
-** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
1223
-** (but planned) pages/commands.
1276
+** INTERIM implementation of /json/wiki/list
12241277
*/
1225
-static cson_value * json_page_nyi(void){
1226
- g.json.resultCode = FSL_JSON_E_NYI;
1227
- return NULL;
1278
+static cson_value * json_wiki_list(void){
1279
+ cson_value * listV = NULL;
1280
+ cson_array * list = NULL;
1281
+ Stmt q;
1282
+ db_prepare(&q,"SELECT"
1283
+ " substr(tagname,6) as name"
1284
+ " FROM tag WHERE tagname GLOB 'wiki-*'"
1285
+ " ORDER BY lower(name)");
1286
+ listV = cson_value_new_array();
1287
+ list = cson_value_get_array(listV);
1288
+ while( SQLITE_ROW == db_step(&q) ){
1289
+ cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0);
1290
+ if(!v){
1291
+ cson_value_free(listV);
1292
+ goto error;
1293
+ }
1294
+ if( 0 != cson_array_append( list, v ) ){
1295
+ cson_value_free(v);
1296
+ goto error;
1297
+ }
1298
+ }
1299
+ db_finalize(&q);
1300
+ goto end;
1301
+ error:
1302
+ g.json.resultCode = FSL_JSON_E_UNKNOWN;
1303
+ cson_value_free(listV);
1304
+ listV = NULL;
1305
+ end:
1306
+ return listV;
12281307
}
1229
-
12301308
12311309
/*
12321310
** Mapping of names to JSON pages/commands. Each name is a subpath of
12331311
** /json (in CGI mode) or a subcommand of the json command in CLI mode
12341312
*/
@@ -1235,10 +1313,11 @@
12351313
static const JsonPageDef JsonPageDefs[] = {
12361314
/* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */
12371315
{"anonymousPassword",json_page_anon_password, 1},
12381316
{"branch", json_page_nyi,0},
12391317
{"cap", json_page_cap, 0},
1318
+{"dir", json_page_nyi, 0},
12401319
{"HAI",json_page_version,0},
12411320
{"login",json_page_login,1},
12421321
{"logout",json_page_logout,1},
12431322
{"stat",json_page_stat,0},
12441323
{"tag", json_page_nyi,0},
@@ -1249,20 +1328,10 @@
12491328
{"wiki",json_page_wiki,0},
12501329
/* Last entry MUST have a NULL name. */
12511330
{NULL,NULL,0}
12521331
};
12531332
1254
-/*
1255
-** Mapping of /json/wiki/XXX commands/paths to callbacks.
1256
-*/
1257
-static const JsonPageDef JsonPageDefs_Wiki[] = {
1258
-{"get", json_page_nyi, 0},
1259
-{"list", json_page_nyi, 0},
1260
-{"save", json_page_nyi, 1},
1261
-/* Last entry MUST have a NULL name. */
1262
-{NULL,NULL,0}
1263
-};
12641333
12651334
/*
12661335
** Mapping of /json/ticket/XXX commands/paths to callbacks.
12671336
*/
12681337
static const JsonPageDef JsonPageDefs_Ticket[] = {
@@ -1398,11 +1467,11 @@
13981467
return;
13991468
}else if( pageDef->runMode > 0 /*HTTP only*/){
14001469
rc = FSL_JSON_E_WRONG_MODE;
14011470
}else{
14021471
rc = 0;
1403
- payload = (pageDef->func)();
1472
+ payload = (*pageDef->func)();
14041473
}
14051474
if( g.json.resultCode ){
14061475
json_err(g.json.resultCode, NULL, 1);
14071476
}else{
14081477
payload = json_create_response(rc, payload, NULL);
14091478
--- src/json.c
+++ src/json.c
@@ -63,10 +63,19 @@
63 ** It is imperitive that NO callback functions EVER output ANYTHING to
64 ** stdout, as that will effectively corrupt any HTTP output.
65 */
66 typedef cson_value * (*fossil_json_f)();
67
 
 
 
 
 
 
 
 
 
68
69 /*
70 ** Holds keys used for various JSON API properties.
71 */
72 static const struct FossilJsonKeys_{
@@ -95,14 +104,14 @@
95 C(UNKNOWN,"Unknown error");
96 C(RESOURCE_NOT_FOUND,"Resource not found");
97 C(TIMEOUT,"Timeout reached");
98 C(ASSERT,"Assertion failed");
99 C(ALLOC,"Resource allocation failed");
100 C(NYI,"Not yet implemented.");
101 C(AUTH,"Authentication error");
102 C(LOGIN_FAILED,"Login failed");
103 C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed.");
104 C(LOGIN_FAILED_NONAME,"Login failed - name not supplied");
105 C(LOGIN_FAILED_NOPW,"Login failed - password not supplied");
106 C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found");
107 C(MISSING_AUTH,"Authentication info missing from request");
108 C(DENIED,"Access denied");
@@ -115,10 +124,11 @@
115 C(DB,"Database error");
116 C(STMT_PREP,"Statement preparation failed");
117 C(STMT_BIND,"Statement parameter binding failed");
118 C(STMT_EXEC,"Statement execution/stepping failed");
119 C(DB_LOCKED,"Database is locked");
 
120 #undef C
121 default:
122 return "Unknown Error";
123 }
124 }
@@ -189,19 +199,25 @@
189 }
190
191 /*
192 ** Adds v to the API-internal cleanup mechanism. key must be a unique
193 ** key for the given element. Adding another item with that key may
194 ** free the previous one. If freeOnError is true then v is passed to
195 ** cson_value_free() if the key cannot be inserted, otherweise
196 ** ownership of v is not changed on error.
 
 
 
 
 
 
197 **
198 ** Returns 0 on success.
199 **
200 *** On success, ownership of v is transfered to (or shared with)
201 *** g.json.gc, and v will be valid until that object is cleaned up or
202 *** its key is replaced via another call to this function.
203 */
204 int json_gc_add( char const * key, cson_value * v, char freeOnError ){
205 int const rc = cson_object_set( g.json.gc.o, key, v );
206 assert( NULL != g.json.gc.o );
207 if( (0 != rc) && freeOnError ){
@@ -252,14 +268,15 @@
252 return NULL;
253 }
254
255
256 /*
257 ** Returns the string form of a json_getenv() value, but ONLY
258 ** If that value is-a String. Non-strings are not converted
259 ** to strings for this purpose. Returned memory is owned by
260 ** g.json or fossil..
 
261 */
262 static char const * json_getenv_cstr( char const * zKey ){
263 return cson_value_get_cstr( json_getenv(zKey) );
264 }
265
@@ -325,13 +342,15 @@
325 /*
326 ** Returns the current request's JSON authentication token, or NULL if
327 ** none is found. The token's memory is owned by (or shared with)
328 ** g.json.
329 **
330 ** If an auth token is found in the GET/POST JSON request data then
331 ** fossil is given that data for use in authentication for this
332 ** session.
 
 
333 **
334 ** Must be called once before login_check_credentials() is called or
335 ** we will not be able to replace fossil's internal idea of the auth
336 ** info in time (and future changes to that state may cause unexpected
337 ** results).
@@ -385,14 +404,26 @@
385 ** Initializes g.json.gc and g.json.param.
386 */
387 void json_main_bootstrap(){
388 cson_value * v;
389 assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" );
 
 
 
 
 
390 v = cson_value_new_object();
391 g.json.gc.v = v;
392 g.json.gc.o = cson_value_get_object(v);
393
 
 
 
 
 
 
 
394 v = cson_value_new_object();
395 g.json.param.v = v;
396 g.json.param.o = cson_value_get_object(v);
397 json_gc_add("$PARAMS", v, 1);
398 }
@@ -497,20 +528,21 @@
497 }
498 }
499
500 /* g.json.reqPayload exists only to simplify some of our access to
501 the request payload. We currently only use this in the context of
502 Object payloads, not Arrays, strings, etc. */
 
503 g.json.reqPayload.v = cson_object_get( g.json.post.o, "payload" );
504 if( g.json.reqPayload.v ){
505 g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
506 /* g.json.reqPayload.o may legally be NULL, which means only that
507 g.json.reqPayload.v is-not-a Object.
508 */;
509 }
510
511 do{/* set up JSON out formatting options. */
512 unsigned char indent = g.isCGI ? 0 : 1;
513 cson_value const * indentV = json_getenv("indent");
514 if(indentV){
515 if(cson_value_is_string(indentV)){
516 int const n = atoi(cson_string_cstr(cson_value_get_string(indentV)));
@@ -524,12 +556,14 @@
524 }
525 g.json.outOpt.indentation = indent;
526 g.json.outOpt.addNewline = g.isCGI ? 0 : 1;
527 }while(0);
528
529 json_auth_token()/* will copy our auth token, if any, to fossil's core. */;
530 if( g.isCGI ){
 
 
 
531 login_check_credentials()/* populates g.perm */;
532 }
533 else{
534 db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
535 }
@@ -933,11 +967,14 @@
933 /*
934 ** Implementation of the /json/login page.
935 **
936 */
937 cson_value * json_page_login(void){
938 static char preciseErrors =
 
 
 
939 #if 0
940 g.json.errorDetailParanoia ? 0 : 1
941 #else
942 0
943 #endif
@@ -1197,38 +1234,79 @@
1197 cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
1198 return jv;
1199 #undef SETBUF
1200 }
1201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1202 /*
1203 ** Implements the /json/wiki family of pages/commands. Far from
1204 ** complete.
1205 **
1206 */
1207 cson_value * json_page_wiki(void){
1208 cson_value * jlist = NULL;
1209 cson_value * rows = NULL;
1210 Stmt q;
1211 wiki_prepare_page_list(&q);
1212 cson_sqlite3_stmt_to_json( q.pStmt, &jlist, 1 );
1213 db_finalize(&q);
1214 assert( NULL != jlist );
1215 rows = cson_object_take( cson_value_get_object(jlist), "rows" );
1216 assert( NULL != rows );
1217 cson_value_free( jlist );
1218 return rows;
 
 
 
1219 }
1220
1221 /*
1222 ** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
1223 ** (but planned) pages/commands.
1224 */
1225 static cson_value * json_page_nyi(void){
1226 g.json.resultCode = FSL_JSON_E_NYI;
1227 return NULL;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1228 }
1229
1230
1231 /*
1232 ** Mapping of names to JSON pages/commands. Each name is a subpath of
1233 ** /json (in CGI mode) or a subcommand of the json command in CLI mode
1234 */
@@ -1235,10 +1313,11 @@
1235 static const JsonPageDef JsonPageDefs[] = {
1236 /* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */
1237 {"anonymousPassword",json_page_anon_password, 1},
1238 {"branch", json_page_nyi,0},
1239 {"cap", json_page_cap, 0},
 
1240 {"HAI",json_page_version,0},
1241 {"login",json_page_login,1},
1242 {"logout",json_page_logout,1},
1243 {"stat",json_page_stat,0},
1244 {"tag", json_page_nyi,0},
@@ -1249,20 +1328,10 @@
1249 {"wiki",json_page_wiki,0},
1250 /* Last entry MUST have a NULL name. */
1251 {NULL,NULL,0}
1252 };
1253
1254 /*
1255 ** Mapping of /json/wiki/XXX commands/paths to callbacks.
1256 */
1257 static const JsonPageDef JsonPageDefs_Wiki[] = {
1258 {"get", json_page_nyi, 0},
1259 {"list", json_page_nyi, 0},
1260 {"save", json_page_nyi, 1},
1261 /* Last entry MUST have a NULL name. */
1262 {NULL,NULL,0}
1263 };
1264
1265 /*
1266 ** Mapping of /json/ticket/XXX commands/paths to callbacks.
1267 */
1268 static const JsonPageDef JsonPageDefs_Ticket[] = {
@@ -1398,11 +1467,11 @@
1398 return;
1399 }else if( pageDef->runMode > 0 /*HTTP only*/){
1400 rc = FSL_JSON_E_WRONG_MODE;
1401 }else{
1402 rc = 0;
1403 payload = (pageDef->func)();
1404 }
1405 if( g.json.resultCode ){
1406 json_err(g.json.resultCode, NULL, 1);
1407 }else{
1408 payload = json_create_response(rc, payload, NULL);
1409
--- src/json.c
+++ src/json.c
@@ -63,10 +63,19 @@
63 ** It is imperitive that NO callback functions EVER output ANYTHING to
64 ** stdout, as that will effectively corrupt any HTTP output.
65 */
66 typedef cson_value * (*fossil_json_f)();
67
68
69 /*
70 ** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
71 ** (but planned) pages/commands.
72 */
73 static cson_value * json_page_nyi(void){
74 g.json.resultCode = FSL_JSON_E_NYI;
75 return NULL;
76 }
77
78 /*
79 ** Holds keys used for various JSON API properties.
80 */
81 static const struct FossilJsonKeys_{
@@ -95,14 +104,14 @@
104 C(UNKNOWN,"Unknown error");
105 C(RESOURCE_NOT_FOUND,"Resource not found");
106 C(TIMEOUT,"Timeout reached");
107 C(ASSERT,"Assertion failed");
108 C(ALLOC,"Resource allocation failed");
109 C(NYI,"Not yet implemented");
110 C(AUTH,"Authentication error");
111 C(LOGIN_FAILED,"Login failed");
112 C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed");
113 C(LOGIN_FAILED_NONAME,"Login failed - name not supplied");
114 C(LOGIN_FAILED_NOPW,"Login failed - password not supplied");
115 C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found");
116 C(MISSING_AUTH,"Authentication info missing from request");
117 C(DENIED,"Access denied");
@@ -115,10 +124,11 @@
124 C(DB,"Database error");
125 C(STMT_PREP,"Statement preparation failed");
126 C(STMT_BIND,"Statement parameter binding failed");
127 C(STMT_EXEC,"Statement execution/stepping failed");
128 C(DB_LOCKED,"Database is locked");
129 C(DB_NEEDS_REBUILD,"Fossil repository needs to be rebuilt");
130 #undef C
131 default:
132 return "Unknown Error";
133 }
134 }
@@ -189,19 +199,25 @@
199 }
200
201 /*
202 ** Adds v to the API-internal cleanup mechanism. key must be a unique
203 ** key for the given element. Adding another item with that key may
204 ** free the previous one (depending on its reference count). If
205 ** freeOnError is true then v is passed to cson_value_free() if the
206 ** key cannot be inserted, otherweise ownership of v is not changed on
207 ** error. Failure to insert a key may be caused by any of the
208 ** following:
209 **
210 ** - Allocation error.
211 ** - g.json.gc.o is NULL
212 ** - key is NULL or empty.
213 **
214 ** Returns 0 on success.
215 **
216 ** On success, ownership of v is transfered to (or shared with)
217 ** g.json.gc, and v will be valid until that object is cleaned up or
218 ** its key is replaced via another call to this function.
219 */
220 int json_gc_add( char const * key, cson_value * v, char freeOnError ){
221 int const rc = cson_object_set( g.json.gc.o, key, v );
222 assert( NULL != g.json.gc.o );
223 if( (0 != rc) && freeOnError ){
@@ -252,14 +268,15 @@
268 return NULL;
269 }
270
271
272 /*
273 ** Returns the string form of a json_getenv() value, but ONLY If that
274 ** value is-a String. Non-strings are not converted to strings for
275 ** this purpose. Returned memory is owned by g.json or fossil and is
276 ** valid until end-of-app or the given key is replaced in fossil's
277 ** internals via cgi_replace_parameter() and friends or json_setenv().
278 */
279 static char const * json_getenv_cstr( char const * zKey ){
280 return cson_value_get_cstr( json_getenv(zKey) );
281 }
282
@@ -325,13 +342,15 @@
342 /*
343 ** Returns the current request's JSON authentication token, or NULL if
344 ** none is found. The token's memory is owned by (or shared with)
345 ** g.json.
346 **
347 ** If an auth token is found in the GET/POST request data then fossil
348 ** is given that data for use in authentication for this
349 ** session. i.e. the GET/POST data overrides fossil's authentication
350 ** cookie value (if any) and also works with clients which do not
351 ** support cookies.
352 **
353 ** Must be called once before login_check_credentials() is called or
354 ** we will not be able to replace fossil's internal idea of the auth
355 ** info in time (and future changes to that state may cause unexpected
356 ** results).
@@ -385,14 +404,26 @@
404 ** Initializes g.json.gc and g.json.param.
405 */
406 void json_main_bootstrap(){
407 cson_value * v;
408 assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" );
409
410 /* g.json.gc is our "garbage collector" - where we put JSON values
411 which need a long lifetime but don't have a logical parent to put
412 them in.
413 */
414 v = cson_value_new_object();
415 g.json.gc.v = v;
416 g.json.gc.o = cson_value_get_object(v);
417
418 /*
419 g.json.param holds the JSONized counterpart of fossil's
420 cgi_parameter_xxx() family of data. We store them as JSON, as
421 opposed to using fossil's data directly, because we can retain
422 full type information for data this way (as opposed to it always
423 being of type string).
424 */
425 v = cson_value_new_object();
426 g.json.param.v = v;
427 g.json.param.o = cson_value_get_object(v);
428 json_gc_add("$PARAMS", v, 1);
429 }
@@ -497,20 +528,21 @@
528 }
529 }
530
531 /* g.json.reqPayload exists only to simplify some of our access to
532 the request payload. We currently only use this in the context of
533 Object payloads, not Arrays, strings, etc.
534 */
535 g.json.reqPayload.v = cson_object_get( g.json.post.o, "payload" );
536 if( g.json.reqPayload.v ){
537 g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
538 /* g.json.reqPayload.o may legally be NULL, which means only that
539 g.json.reqPayload.v is-not-a Object.
540 */;
541 }
542
543 do{/* set up JSON output formatting options. */
544 unsigned char indent = g.isCGI ? 0 : 1;
545 cson_value const * indentV = json_getenv("indent");
546 if(indentV){
547 if(cson_value_is_string(indentV)){
548 int const n = atoi(cson_string_cstr(cson_value_get_string(indentV)));
@@ -524,12 +556,14 @@
556 }
557 g.json.outOpt.indentation = indent;
558 g.json.outOpt.addNewline = g.isCGI ? 0 : 1;
559 }while(0);
560
 
561 if( g.isCGI ){
562 json_auth_token()/* will copy our auth token, if any, to fossil's
563 core, which we need before we call
564 login_check_credentials(). */;
565 login_check_credentials()/* populates g.perm */;
566 }
567 else{
568 db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
569 }
@@ -933,11 +967,14 @@
967 /*
968 ** Implementation of the /json/login page.
969 **
970 */
971 cson_value * json_page_login(void){
972 static char preciseErrors = /* if true, "complete" JSON error codes are used,
973 else they are "dumbed down" to a generic login
974 error code.
975 */
976 #if 0
977 g.json.errorDetailParanoia ? 0 : 1
978 #else
979 0
980 #endif
@@ -1197,38 +1234,79 @@
1234 cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
1235 return jv;
1236 #undef SETBUF
1237 }
1238
1239
1240 static cson_value * json_wiki_list(void);
1241
1242 /*
1243 ** Mapping of /json/wiki/XXX commands/paths to callbacks.
1244 */
1245 static const JsonPageDef JsonPageDefs_Wiki[] = {
1246 {"get", json_page_nyi, 0},
1247 {"list", json_wiki_list, 0},
1248 {"save", json_page_nyi, 1},
1249 /* Last entry MUST have a NULL name. */
1250 {NULL,NULL,0}
1251 };
1252
1253 /*
1254 ** Implements the /json/wiki family of pages/commands. Far from
1255 ** complete.
1256 **
1257 */
1258 static cson_value * json_page_wiki(void){
1259 JsonPageDef const * def;
1260 char const * cmd = json_command_arg(2);
1261 if( ! cmd ){
1262 g.json.resultCode = FSL_JSON_E_MISSING_AUTH;
1263 return NULL;
1264 }
1265 def = json_handler_for_name( cmd, &JsonPageDefs_Wiki[0] );
1266 if(!def){
1267 g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND;
1268 return NULL;
1269 }
1270 else{
1271 return (*def->func)();
1272 }
1273 }
1274
1275 /*
1276 ** INTERIM implementation of /json/wiki/list
 
1277 */
1278 static cson_value * json_wiki_list(void){
1279 cson_value * listV = NULL;
1280 cson_array * list = NULL;
1281 Stmt q;
1282 db_prepare(&q,"SELECT"
1283 " substr(tagname,6) as name"
1284 " FROM tag WHERE tagname GLOB 'wiki-*'"
1285 " ORDER BY lower(name)");
1286 listV = cson_value_new_array();
1287 list = cson_value_get_array(listV);
1288 while( SQLITE_ROW == db_step(&q) ){
1289 cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0);
1290 if(!v){
1291 cson_value_free(listV);
1292 goto error;
1293 }
1294 if( 0 != cson_array_append( list, v ) ){
1295 cson_value_free(v);
1296 goto error;
1297 }
1298 }
1299 db_finalize(&q);
1300 goto end;
1301 error:
1302 g.json.resultCode = FSL_JSON_E_UNKNOWN;
1303 cson_value_free(listV);
1304 listV = NULL;
1305 end:
1306 return listV;
1307 }
 
1308
1309 /*
1310 ** Mapping of names to JSON pages/commands. Each name is a subpath of
1311 ** /json (in CGI mode) or a subcommand of the json command in CLI mode
1312 */
@@ -1235,10 +1313,11 @@
1313 static const JsonPageDef JsonPageDefs[] = {
1314 /* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */
1315 {"anonymousPassword",json_page_anon_password, 1},
1316 {"branch", json_page_nyi,0},
1317 {"cap", json_page_cap, 0},
1318 {"dir", json_page_nyi, 0},
1319 {"HAI",json_page_version,0},
1320 {"login",json_page_login,1},
1321 {"logout",json_page_logout,1},
1322 {"stat",json_page_stat,0},
1323 {"tag", json_page_nyi,0},
@@ -1249,20 +1328,10 @@
1328 {"wiki",json_page_wiki,0},
1329 /* Last entry MUST have a NULL name. */
1330 {NULL,NULL,0}
1331 };
1332
 
 
 
 
 
 
 
 
 
 
1333
1334 /*
1335 ** Mapping of /json/ticket/XXX commands/paths to callbacks.
1336 */
1337 static const JsonPageDef JsonPageDefs_Ticket[] = {
@@ -1398,11 +1467,11 @@
1467 return;
1468 }else if( pageDef->runMode > 0 /*HTTP only*/){
1469 rc = FSL_JSON_E_WRONG_MODE;
1470 }else{
1471 rc = 0;
1472 payload = (*pageDef->func)();
1473 }
1474 if( g.json.resultCode ){
1475 json_err(g.json.resultCode, NULL, 1);
1476 }else{
1477 payload = json_create_response(rc, payload, NULL);
1478

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button