| | @@ -5133,384 +5133,10 @@ |
| 5133 | 5133 | } |
| 5134 | 5134 | } |
| 5135 | 5135 | cson_kvp_list_reserve(self,0); |
| 5136 | 5136 | } |
| 5137 | 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 | 5138 | /* begin file ./cson_sqlite3.c */ |
| 5513 | 5139 | /** @file cson_sqlite3.c |
| 5514 | 5140 | |
| 5515 | 5141 | This file contains the implementation code for the cson |
| 5516 | 5142 | sqlite3-to-JSON API. |
| | @@ -5532,11 +5158,11 @@ |
| 5532 | 5158 | |
| 5533 | 5159 | #if defined(__cplusplus) |
| 5534 | 5160 | extern "C" { |
| 5535 | 5161 | #endif |
| 5536 | 5162 | |
| 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 ) |
| 5538 | 5164 | { |
| 5539 | 5165 | if( ! st ) return NULL; |
| 5540 | 5166 | else |
| 5541 | 5167 | { |
| 5542 | 5168 | #if 0 |
| | @@ -5546,39 +5172,29 @@ |
| 5546 | 5172 | #else |
| 5547 | 5173 | int const vtype = sqlite3_column_type(st,col); |
| 5548 | 5174 | #endif |
| 5549 | 5175 | switch( vtype ) |
| 5550 | 5176 | { |
| 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. */ |
| 5554 | 5181 | 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 ) |
| 5580 | 5196 | { |
| 5581 | 5197 | cson_value * aryV = NULL; |
| 5582 | 5198 | cson_array * ary = NULL; |
| 5583 | 5199 | char const * colName = NULL; |
| 5584 | 5200 | int i = 0; |
| | @@ -5608,10 +5224,78 @@ |
| 5608 | 5224 | cson_value_free(aryV); |
| 5609 | 5225 | return NULL; |
| 5610 | 5226 | } |
| 5611 | 5227 | } |
| 5612 | 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 | + |
| 5613 | 5297 | /** |
| 5614 | 5298 | Internal impl of cson_sqlite3_stmt_to_json() when the 'fat' |
| 5615 | 5299 | parameter is non-0. |
| 5616 | 5300 | */ |
| 5617 | 5301 | static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt ) |
| | @@ -5624,20 +5308,16 @@ |
| 5624 | 5308 | cson_object * root = NULL; |
| 5625 | 5309 | cson_value * colsV = NULL; |
| 5626 | 5310 | cson_value * rowsV = NULL; |
| 5627 | 5311 | cson_array * rows = NULL; |
| 5628 | 5312 | cson_value * objV = NULL; |
| 5629 | | - cson_object * obj = NULL; |
| 5630 | | - cson_value * currentValue = NULL; |
| 5631 | | - char const * colName = NULL; |
| 5632 | 5313 | int rc = 0; |
| 5633 | | - int i = 0; |
| 5634 | 5314 | int const colCount = sqlite3_column_count(st); |
| 5635 | 5315 | if( colCount <= 0 ) return cson_rc.ArgError; |
| 5636 | 5316 | rootV = cson_value_new_object(); |
| 5637 | 5317 | if( ! rootV ) return cson_rc.AllocError; |
| 5638 | | - colsV = cson_sqlite3_stmt_cols(st); |
| 5318 | + colsV = cson_sqlite3_column_names(st); |
| 5639 | 5319 | if( ! colsV ) |
| 5640 | 5320 | { |
| 5641 | 5321 | cson_value_free( rootV ); |
| 5642 | 5322 | RETURN(cson_rc.AllocError); |
| 5643 | 5323 | } |
| | @@ -5646,13 +5326,11 @@ |
| 5646 | 5326 | if( rc ) |
| 5647 | 5327 | { |
| 5648 | 5328 | cson_value_free( colsV ); |
| 5649 | 5329 | RETURN(rc); |
| 5650 | 5330 | } |
| 5651 | | - |
| 5652 | 5331 | colsV = NULL; |
| 5653 | | - |
| 5654 | 5332 | rowsV = cson_value_new_array(); |
| 5655 | 5333 | if( ! rowsV ) RETURN(cson_rc.AllocError); |
| 5656 | 5334 | rc = cson_object_set( root, "rows", rowsV ); |
| 5657 | 5335 | if( rc ) |
| 5658 | 5336 | { |
| | @@ -5661,32 +5339,18 @@ |
| 5661 | 5339 | } |
| 5662 | 5340 | rows = cson_value_get_array(rowsV); |
| 5663 | 5341 | assert(rows); |
| 5664 | 5342 | while( SQLITE_ROW == sqlite3_step(st) ) |
| 5665 | 5343 | { |
| 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); |
| 5668 | 5346 | rc = cson_array_append( rows, objV ); |
| 5669 | 5347 | if( rc ) |
| 5670 | 5348 | { |
| 5671 | 5349 | cson_value_free( objV ); |
| 5672 | 5350 | RETURN(rc); |
| 5673 | 5351 | } |
| 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 | 5352 | } |
| 5689 | 5353 | *tgt = rootV; |
| 5690 | 5354 | return 0; |
| 5691 | 5355 | } |
| 5692 | 5356 | #undef RETURN |
| | @@ -5706,18 +5370,16 @@ |
| 5706 | 5370 | cson_object * root = NULL; |
| 5707 | 5371 | cson_value * aryV = NULL; |
| 5708 | 5372 | cson_array * ary = NULL; |
| 5709 | 5373 | cson_value * rowsV = NULL; |
| 5710 | 5374 | cson_array * rows = NULL; |
| 5711 | | - cson_value * colV = NULL; |
| 5712 | 5375 | int rc = 0; |
| 5713 | | - int i = 0; |
| 5714 | 5376 | int const colCount = sqlite3_column_count(st); |
| 5715 | 5377 | if( colCount <= 0 ) return cson_rc.ArgError; |
| 5716 | 5378 | rootV = cson_value_new_object(); |
| 5717 | 5379 | if( ! rootV ) return cson_rc.AllocError; |
| 5718 | | - aryV = cson_sqlite3_stmt_cols(st); |
| 5380 | + aryV = cson_sqlite3_column_names(st); |
| 5719 | 5381 | if( ! aryV ) |
| 5720 | 5382 | { |
| 5721 | 5383 | cson_value_free( rootV ); |
| 5722 | 5384 | RETURN(cson_rc.AllocError); |
| 5723 | 5385 | } |
| | @@ -5740,32 +5402,18 @@ |
| 5740 | 5402 | } |
| 5741 | 5403 | rows = cson_value_get_array(rowsV); |
| 5742 | 5404 | assert(rows); |
| 5743 | 5405 | while( SQLITE_ROW == sqlite3_step(st) ) |
| 5744 | 5406 | { |
| 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); |
| 5747 | 5409 | rc = cson_array_append( rows, aryV ); |
| 5748 | 5410 | if( 0 != rc ) |
| 5749 | 5411 | { |
| 5750 | 5412 | cson_value_free( aryV ); |
| 5751 | 5413 | RETURN(rc); |
| 5752 | 5414 | } |
| 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 | 5415 | } |
| 5768 | 5416 | *tgt = rootV; |
| 5769 | 5417 | return 0; |
| 5770 | 5418 | } |
| 5771 | 5419 | #undef RETURN |
| | @@ -5797,2721 +5445,5 @@ |
| 5797 | 5445 | } /*extern "C"*/ |
| 5798 | 5446 | #endif |
| 5799 | 5447 | #undef MARKER |
| 5800 | 5448 | #endif /* CSON_ENABLE_SQLITE3 */ |
| 5801 | 5449 | /* 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 | 5450 | |