Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
| SHA1 Hash: | b7f3a32d4e67a900c0ac5e9b2bec23d412ef597c |
|---|---|
| Date: | 2012-04-04 16:20:14 |
| User: | viriketo |
| Comment: | Updating from trunk to get the latest trunk fixes. |
- branch=annotate_links inherited from [ef5d2176f9]
- sym-annotate_links inherited from [ef5d2176f9]
Changes to ajax/i-test/rhino-test.js
1 var TestApp = { 1 var TestApp = { 2 serverUrl: 2 serverUrl: 3 'http://localhost:8080' 3 'http://localhost:8080' 4 //'http://fjson/cgi-bin/fossil-json.cgi' 4 //'http://fjson/cgi-bin/fossil-json.cgi' 5 //'http://192.168.1.62:8080' 5 //'http://192.168.1.62:8080' 6 //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi' 6 //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi' 7 , 7 , 8 verbose:false, | 8 verbose:true, 9 fossilBinary:'fossil', 9 fossilBinary:'fossil', 10 wiki:{} 10 wiki:{} 11 }; 11 }; 12 (function bootstrap() { 12 (function bootstrap() { 13 var srcdir = '../js/'; 13 var srcdir = '../js/'; 14 var includes = [srcdir+'json2.js', 14 var includes = [srcdir+'json2.js', 15 srcdir+'whajaj.js', 15 srcdir+'whajaj.js', ................................................................................................................................................................................ 169 assertResponseOK(rs); 169 assertResponseOK(rs); 170 assert(rs.payload.name == TestApp.wiki.list[0], "Fetched page name matches e 170 assert(rs.payload.name == TestApp.wiki.list[0], "Fetched page name matches e 171 print("Got first wiki page: "+WhAjaj.stringify(rs.payload)); 171 print("Got first wiki page: "+WhAjaj.stringify(rs.payload)); 172 172 173 } 173 } 174 testAnonWiki.description = 'Fetch wiki list as anonymous user.'; 174 testAnonWiki.description = 'Fetch wiki list as anonymous user.'; 175 175 > 176 function testFetchCheckinArtifact(){ > 177 var art = '18dd383e5e7684ece'; > 178 var rs; > 179 TestApp.fossil.sendCommand('/json/artifact',{ > 180 'name': art > 181 }, > 182 { > 183 onResponse:function(resp,req){ > 184 rs = resp; > 185 } > 186 }); > 187 assertResponseOK(rs); > 188 assert(3 == rs.payload.artifact.parents.length, 'Got 3 parent artifacts.'); > 189 } > 190 testFetchCheckinArtifact.description = '/json/artifact/CHECKIN'; > 191 176 function testAnonLogout(){ 192 function testAnonLogout(){ 177 var rs; 193 var rs; 178 TestApp.fossil.logout({ 194 TestApp.fossil.logout({ 179 onResponse:function(resp,req){ 195 onResponse:function(resp,req){ 180 rs = resp; 196 rs = resp; 181 } 197 } 182 }); 198 }); ................................................................................................................................................................................ 237 253 238 (function runAllTests(){ 254 (function runAllTests(){ 239 var testList = [ 255 var testList = [ 240 testHAI, 256 testHAI, 241 testIAmNobody, 257 testIAmNobody, 242 testAnonymousLogin, 258 testAnonymousLogin, 243 testAnonWiki, 259 testAnonWiki, > 260 testFetchCheckinArtifact, 244 testAnonLogout, 261 testAnonLogout, 245 testExternalProcess, 262 testExternalProcess, 246 testExternalProcessHandler 263 testExternalProcessHandler 247 ]; 264 ]; 248 var i, f; 265 var i, f; 249 for( i = 0; i < testList.length; ++i ){ 266 for( i = 0; i < testList.length; ++i ){ 250 f = testList[i]; 267 f = testList[i];
Changes to ajax/index.html
231 <br/> 231 <br/> 232 232 233 <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/ 233 <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/ 234 <input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/ 234 <input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/ 235 <input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/jso 235 <input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/jso 236 <input type='button' value='timeline/ticket' onclick='TheApp.cgi.sendCommand("/j 236 <input type='button' value='timeline/ticket' onclick='TheApp.cgi.sendCommand("/j 237 <input type='button' value='timeline/branch' onclick='TheApp.cgi.sendCommand("/j 237 <input type='button' value='timeline/branch' onclick='TheApp.cgi.sendCommand("/j > 238 > 239 <br/> > 240 238 <input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wi 241 <input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wi 239 <input type='button' value='wiki/list verbose' onclick='TheApp.cgi.sendCommand(" 242 <input type='button' value='wiki/list verbose' onclick='TheApp.cgi.sendCommand(" 240 <input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/j 243 <input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/j 241 <input type='button' value='wiki/get/Fossil' onclick='TheApp.cgi.sendCommand("/j 244 <input type='button' value='wiki/get/Fossil' onclick='TheApp.cgi.sendCommand("/j > 245 <input type='button' value='wiki/diff' onclick='TheApp.cgi.sendCommand("/json/wi 242 246 243 <br/> 247 <br/> 244 248 245 <input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/us 249 <input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/us 246 <input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/use 250 <input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/use 247 <input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag 251 <input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag 248 <input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/jso 252 <input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/jso
Changes to src/add.c
239 239 240 /* Load the names of all files that are to be added into sfile temp table */ 240 /* Load the names of all files that are to be added into sfile temp table */ 241 for(i=2; i<g.argc; i++){ 241 for(i=2; i<g.argc; i++){ 242 char *zName; 242 char *zName; 243 int isDir; 243 int isDir; 244 Blob fullName; 244 Blob fullName; 245 245 246 file_canonical_name(g.argv[i], &fullName); | 246 file_canonical_name(g.argv[i], &fullName, 0); 247 zName = blob_str(&fullName); 247 zName = blob_str(&fullName); 248 isDir = file_wd_isdir(zName); 248 isDir = file_wd_isdir(zName); 249 if( isDir==1 ){ 249 if( isDir==1 ){ 250 vfile_scan(&fullName, nRoot-1, includeDotFiles, pIgnore); 250 vfile_scan(&fullName, nRoot-1, includeDotFiles, pIgnore); 251 }else if( isDir==0 ){ 251 }else if( isDir==0 ){ 252 fossil_fatal("not found: %s", zName); 252 fossil_fatal("not found: %s", zName); 253 }else if( file_access(zName, R_OK) ){ 253 }else if( file_access(zName, R_OK) ){
Changes to src/allrepo.c
166 db_unset(zRepo, 1); 166 db_unset(zRepo, 1); 167 free(zRepo); 167 free(zRepo); 168 }else if( !file_is_canonical(zFilename) ){ 168 }else if( !file_is_canonical(zFilename) ){ 169 Blob cname; 169 Blob cname; 170 char *zRepo = mprintf("repo:%s", zFilename); 170 char *zRepo = mprintf("repo:%s", zFilename); 171 db_unset(zRepo, 1); 171 db_unset(zRepo, 1); 172 free(zRepo); 172 free(zRepo); 173 file_canonical_name(zFilename, &cname); | 173 file_canonical_name(zFilename, &cname, 0); 174 zRepo = mprintf("repo:%s", blob_str(&cname)); 174 zRepo = mprintf("repo:%s", blob_str(&cname)); 175 db_set(zRepo, "1", 1); 175 db_set(zRepo, "1", 1); 176 free(zRepo); 176 free(zRepo); 177 } 177 } 178 } 178 } 179 db_reset(&q); 179 db_reset(&q); 180 db_end_transaction(0); 180 db_end_transaction(0); 181 } 181 } 182 db_finalize(&q); 182 db_finalize(&q); 183 } 183 }
Changes to src/blob.c
88 */ 88 */ 89 int fossil_islower(char c){ return c>='a' && c<='z'; } 89 int fossil_islower(char c){ return c>='a' && c<='z'; } 90 int fossil_isupper(char c){ return c>='A' && c<='Z'; } 90 int fossil_isupper(char c){ return c>='A' && c<='Z'; } 91 int fossil_isdigit(char c){ return c>='0' && c<='9'; } 91 int fossil_isdigit(char c){ return c>='0' && c<='9'; } 92 int fossil_tolower(char c){ 92 int fossil_tolower(char c){ 93 return fossil_isupper(c) ? c - 'A' + 'a' : c; 93 return fossil_isupper(c) ? c - 'A' + 'a' : c; 94 } 94 } > 95 int fossil_toupper(char c){ > 96 return fossil_islower(c) ? c - 'a' + 'A' : c; > 97 } 95 int fossil_isalpha(char c){ 98 int fossil_isalpha(char c){ 96 return (c>='a' && c<='z') || (c>='A' && c<='Z'); 99 return (c>='a' && c<='z') || (c>='A' && c<='Z'); 97 } 100 } 98 int fossil_isalnum(char c){ 101 int fossil_isalnum(char c){ 99 return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); 102 return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); 100 } 103 } 101 104 ................................................................................................................................................................................ 789 nName = strlen(zFilename); 792 nName = strlen(zFilename); 790 if( nName>=sizeof(zBuf) ){ 793 if( nName>=sizeof(zBuf) ){ 791 zName = mprintf("%s", zFilename); 794 zName = mprintf("%s", zFilename); 792 }else{ 795 }else{ 793 zName = zBuf; 796 zName = zBuf; 794 memcpy(zName, zFilename, nName+1); 797 memcpy(zName, zFilename, nName+1); 795 } 798 } 796 nName = file_simplify_name(zName, nName); | 799 nName = file_simplify_name(zName, nName, 0); 797 for(i=1; i<nName; i++){ 800 for(i=1; i<nName; i++){ 798 if( zName[i]=='/' ){ 801 if( zName[i]=='/' ){ 799 zName[i] = 0; 802 zName[i] = 0; 800 #if defined(_WIN32) 803 #if defined(_WIN32) 801 /* 804 /* 802 ** On Windows, local path looks like: C:/develop/project/file.txt 805 ** On Windows, local path looks like: C:/develop/project/file.txt 803 ** The if stops us from trying to create a directory of a drive letter 806 ** The if stops us from trying to create a directory of a drive letter
Changes to src/browse.c
32 ** 32 ** 33 ** Examples: 33 ** Examples: 34 ** 34 ** 35 ** pathelement('abc/pqr/xyz', 4) -> '/pqr' 35 ** pathelement('abc/pqr/xyz', 4) -> '/pqr' 36 ** pathelement('abc/pqr', 4) -> 'pqr' 36 ** pathelement('abc/pqr', 4) -> 'pqr' 37 ** pathelement('abc/pqr/xyz', 0) -> '/abc' 37 ** pathelement('abc/pqr/xyz', 0) -> '/abc' 38 */ 38 */ 39 static void pathelementFunc( | 39 void pathelementFunc( 40 sqlite3_context *context, 40 sqlite3_context *context, 41 int argc, 41 int argc, 42 sqlite3_value **argv 42 sqlite3_value **argv 43 ){ 43 ){ 44 const unsigned char *z; 44 const unsigned char *z; 45 int len, n, i; 45 int len, n, i; 46 char *zOut; 46 char *zOut;
Changes to src/cgi.c
736 ** 736 ** 737 ** If contentLen is 0 then the whole file is read. 737 ** If contentLen is 0 then the whole file is read. 738 */ 738 */ 739 void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){ 739 void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){ 740 cson_value * jv = NULL; 740 cson_value * jv = NULL; 741 int rc; 741 int rc; 742 CgiPostReadState state; 742 CgiPostReadState state; > 743 cson_parse_opt popt = cson_parse_opt_empty; > 744 cson_parse_info pinfo = cson_parse_info_empty; > 745 popt.maxDepth = 15; 743 state.fh = zIn; 746 state.fh = zIn; 744 state.len = contentLen; 747 state.len = contentLen; 745 state.pos = 0; 748 state.pos = 0; 746 rc = cson_parse( &jv, 749 rc = cson_parse( &jv, 747 contentLen ? cson_data_source_FILE_n : cson_data_source_FILE, 750 contentLen ? cson_data_source_FILE_n : cson_data_source_FILE, 748 contentLen ? (void *)&state : (void *)zIn, NULL, NULL ); | 751 contentLen ? (void *)&state : (void *)zIn, &popt, &pinfo ); 749 if(rc){ 752 if(rc){ 750 goto invalidRequest; 753 goto invalidRequest; 751 }else{ 754 }else{ 752 json_gc_add( "POST.JSON", jv ); 755 json_gc_add( "POST.JSON", jv ); 753 g.json.post.v = jv; 756 g.json.post.v = jv; 754 g.json.post.o = cson_value_get_object( jv ); 757 g.json.post.o = cson_value_get_object( jv ); 755 if( !g.json.post.o ){ /* we don't support non-Object (Array) requests */ 758 if( !g.json.post.o ){ /* we don't support non-Object (Array) requests */ 756 goto invalidRequest; 759 goto invalidRequest; 757 } 760 } 758 } 761 } 759 return; 762 return; 760 invalidRequest: 763 invalidRequest: 761 cgi_set_content_type(json_guess_content_type()); 764 cgi_set_content_type(json_guess_content_type()); > 765 if(0 != pinfo.errorCode){ /* fancy error message */ > 766 char * msg = mprintf("JSON parse error at line %u, column %u, " > 767 "byte offset %u: %s", > 768 pinfo.line, pinfo.col, pinfo.length, > 769 cson_rc_string(pinfo.errorCode)); > 770 json_err( FSL_JSON_E_INVALID_REQUEST, msg, 1 ); > 771 free(msg); > 772 }else if(jv && !g.json.post.o){ > 773 json_err( FSL_JSON_E_INVALID_REQUEST, > 774 "Request envelope must be a JSON Object (not array).", 1 ); > 775 }else{ /* generic error message */ 762 json_err( FSL_JSON_E_INVALID_REQUEST, NULL, 1 ); | 776 json_err( FSL_JSON_E_INVALID_REQUEST, NULL, 1 ); > 777 } 763 fossil_exit( g.isHTTP ? 0 : 1); 778 fossil_exit( g.isHTTP ? 0 : 1); 764 } 779 } 765 #endif /* FOSSIL_ENABLE_JSON */ 780 #endif /* FOSSIL_ENABLE_JSON */ 766 781 767 782 768 /* 783 /* 769 ** Initialize the query parameter database. Information is pulled from 784 ** Initialize the query parameter database. Information is pulled from
Changes to src/checkin.c
53 const char *zDisplayName = zPathname; 53 const char *zDisplayName = zPathname; 54 int isDeleted = db_column_int(&q, 1); 54 int isDeleted = db_column_int(&q, 1); 55 int isChnged = db_column_int(&q,2); 55 int isChnged = db_column_int(&q,2); 56 int isNew = db_column_int(&q,3)==0; 56 int isNew = db_column_int(&q,3)==0; 57 int isRenamed = db_column_int(&q,4); 57 int isRenamed = db_column_int(&q,4); 58 char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); 58 char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); 59 if( cwdRelative ){ 59 if( cwdRelative ){ 60 file_relative_name(zFullName, &rewrittenPathname); | 60 file_relative_name(zFullName, &rewrittenPathname, 0); 61 zDisplayName = blob_str(&rewrittenPathname); 61 zDisplayName = blob_str(&rewrittenPathname); 62 if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){ 62 if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){ 63 zDisplayName += 2; /* no unnecessary ./ prefix */ 63 zDisplayName += 2; /* no unnecessary ./ prefix */ 64 } 64 } 65 } 65 } 66 blob_append(report, zPrefix, nPrefix); 66 blob_append(report, zPrefix, nPrefix); 67 if( isDeleted ){ 67 if( isDeleted ){ ................................................................................................................................................................................ 311 } 311 } 312 db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); 312 db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); 313 blob_zero(&rewrittenPathname); 313 blob_zero(&rewrittenPathname); 314 while( db_step(&q)==SQLITE_ROW ){ 314 while( db_step(&q)==SQLITE_ROW ){ 315 zDisplayName = zPathname = db_column_text(&q, 0); 315 zDisplayName = zPathname = db_column_text(&q, 0); 316 if( cwdRelative ) { 316 if( cwdRelative ) { 317 char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); 317 char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); 318 file_relative_name(zFullName, &rewrittenPathname); | 318 file_relative_name(zFullName, &rewrittenPathname, 0); 319 free(zFullName); 319 free(zFullName); 320 zDisplayName = blob_str(&rewrittenPathname); 320 zDisplayName = blob_str(&rewrittenPathname); 321 if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){ 321 if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){ 322 zDisplayName += 2; /* no unnecessary ./ prefix */ 322 zDisplayName += 2; /* no unnecessary ./ prefix */ 323 } 323 } 324 } 324 } 325 fossil_print("%s\n", zDisplayName); 325 fossil_print("%s\n", zDisplayName); ................................................................................................................................................................................ 630 Blob *pComment, /* Check-in comment text */ 630 Blob *pComment, /* Check-in comment text */ 631 int vid, /* blob-id of the parent manifest */ 631 int vid, /* blob-id of the parent manifest */ 632 int verifyDate, /* Verify that child is younger */ 632 int verifyDate, /* Verify that child is younger */ 633 Blob *pCksum, /* Repository checksum. May be 0 */ 633 Blob *pCksum, /* Repository checksum. May be 0 */ 634 const char *zDateOvrd, /* Date override. If 0 then use 'now' */ 634 const char *zDateOvrd, /* Date override. If 0 then use 'now' */ 635 const char *zUserOvrd, /* User override. If 0 then use g.zLogin */ 635 const char *zUserOvrd, /* User override. If 0 then use g.zLogin */ 636 const char *zBranch, /* Branch name. May be 0 */ 636 const char *zBranch, /* Branch name. May be 0 */ 637 const char *zBgColor, /* Background color. May be 0 */ | 637 const char *zColor, /* One-time gackground color. May be 0 */ > 638 const char *zBrClr, /* Persistent branch color. May be 0 */ 638 const char *zTag, /* Tag to apply to this check-in */ | 639 const char **azTag, /* Tags to apply to this check-in */ 639 int *pnFBcard /* Number of generated B- and F-cards */ 640 int *pnFBcard /* Number of generated B- and F-cards */ 640 ){ 641 ){ 641 char *zDate; /* Date of the check-in */ 642 char *zDate; /* Date of the check-in */ 642 char *zParentUuid; /* UUID of parent check-in */ 643 char *zParentUuid; /* UUID of parent check-in */ 643 Blob filename; /* A single filename */ 644 Blob filename; /* A single filename */ 644 int nBasename; /* Size of base filename */ 645 int nBasename; /* Size of base filename */ 645 Stmt q; /* Query of files changed */ 646 Stmt q; /* Query of files changed */ 646 Stmt q2; /* Query of merge parents */ 647 Stmt q2; /* Query of merge parents */ 647 Blob mcksum; /* Manifest checksum */ 648 Blob mcksum; /* Manifest checksum */ 648 ManifestFile *pFile; /* File from the baseline */ 649 ManifestFile *pFile; /* File from the baseline */ 649 int nFBcard = 0; /* Number of B-cards and F-cards */ 650 int nFBcard = 0; /* Number of B-cards and F-cards */ > 651 int i; /* Loop counter */ 650 652 651 assert( pBaseline==0 || pBaseline->zBaseline==0 ); 653 assert( pBaseline==0 || pBaseline->zBaseline==0 ); 652 assert( pBaseline==0 || zBaselineUuid!=0 ); 654 assert( pBaseline==0 || zBaselineUuid!=0 ); 653 blob_zero(pOut); 655 blob_zero(pOut); 654 zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); 656 zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); 655 if( pBaseline ){ 657 if( pBaseline ){ 656 blob_appendf(pOut, "B %s\n", zBaselineUuid); 658 blob_appendf(pOut, "B %s\n", zBaselineUuid); ................................................................................................................................................................................ 754 db_finalize(&q2); 756 db_finalize(&q2); 755 free(zDate); 757 free(zDate); 756 758 757 blob_appendf(pOut, "\n"); 759 blob_appendf(pOut, "\n"); 758 if( pCksum ) blob_appendf(pOut, "R %b\n", pCksum); 760 if( pCksum ) blob_appendf(pOut, "R %b\n", pCksum); 759 if( zBranch && zBranch[0] ){ 761 if( zBranch && zBranch[0] ){ 760 /* Set tags for the new branch */ 762 /* Set tags for the new branch */ 761 if( zBgColor && zBgColor[0] ){ | 763 if( zBrClr && zBrClr[0] ){ > 764 zColor = 0; 762 blob_appendf(pOut, "T *bgcolor * %F\n", zBgColor); | 765 blob_appendf(pOut, "T *bgcolor * %F\n", zBrClr); 763 } 766 } 764 blob_appendf(pOut, "T *branch * %F\n", zBranch); 767 blob_appendf(pOut, "T *branch * %F\n", zBranch); 765 blob_appendf(pOut, "T *sym-%F *\n", zBranch); 768 blob_appendf(pOut, "T *sym-%F *\n", zBranch); 766 } 769 } > 770 if( zColor && zColor[0] ){ > 771 /* One-time background color */ > 772 blob_appendf(pOut, "T +bgcolor * %F\n", zColor); > 773 } 767 if( g.markPrivate ){ 774 if( g.markPrivate ){ 768 /* If this manifest is private, mark it as such */ 775 /* If this manifest is private, mark it as such */ 769 blob_appendf(pOut, "T +private *\n"); 776 blob_appendf(pOut, "T +private *\n"); 770 } 777 } 771 if( zTag && zTag[0] ){ | 778 if( azTag ){ > 779 for(i=0; azTag[i]; i++){ 772 /* Add a symbolic tag to this check-in */ | 780 /* Add a symbolic tag to this check-in. The tag names have already > 781 ** been sorted and converted using the %F format */ 773 blob_appendf(pOut, "T +sym-%F *\n", zTag); | 782 blob_appendf(pOut, "T +sym-%s *\n", azTag[i]); > 783 } 774 } 784 } 775 if( zBranch && zBranch[0] ){ 785 if( zBranch && zBranch[0] ){ 776 /* For a new branch, cancel all prior propagating tags */ 786 /* For a new branch, cancel all prior propagating tags */ 777 Stmt q; 787 Stmt q; 778 db_prepare(&q, 788 db_prepare(&q, 779 "SELECT tagname FROM tagxref, tag" 789 "SELECT tagname FROM tagxref, tag" 780 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" 790 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" 781 " AND tagtype==2 AND tagname GLOB 'sym-*'" 791 " AND tagtype==2 AND tagname GLOB 'sym-*'" 782 " AND tagname!='sym-'||%Q" 792 " AND tagname!='sym-'||%Q" 783 " ORDER BY tagname", 793 " ORDER BY tagname", 784 vid, zBranch); 794 vid, zBranch); 785 while( db_step(&q)==SQLITE_ROW ){ 795 while( db_step(&q)==SQLITE_ROW ){ 786 const char *zTag = db_column_text(&q, 0); | 796 const char *zBrTag = db_column_text(&q, 0); 787 blob_appendf(pOut, "T -%F *\n", zTag); | 797 blob_appendf(pOut, "T -%F *\n", zBrTag); 788 } 798 } 789 db_finalize(&q); 799 db_finalize(&q); 790 } 800 } 791 blob_appendf(pOut, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); 801 blob_appendf(pOut, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); 792 md5sum_blob(pOut, &mcksum); 802 md5sum_blob(pOut, &mcksum); 793 blob_appendf(pOut, "Z %b\n", &mcksum); 803 blob_appendf(pOut, "Z %b\n", &mcksum); 794 if( pnFBcard ) *pnFBcard = nFBcard; 804 if( pnFBcard ) *pnFBcard = nFBcard; ................................................................................................................................................................................ 824 }else{ 834 }else{ 825 lastNl++; 835 lastNl++; 826 if( lastNl>1000 ) return; /* Binary if any line longer than 1000 */ 836 if( lastNl>1000 ) return; /* Binary if any line longer than 1000 */ 827 } 837 } 828 } 838 } 829 if( nCrNl ){ 839 if( nCrNl ){ 830 char c; 840 char c; 831 file_relative_name(zFilename, &fname); | 841 file_relative_name(zFilename, &fname, 0); 832 blob_zero(&ans); 842 blob_zero(&ans); 833 zMsg = mprintf( 843 zMsg = mprintf( 834 "%s contains CR/NL line endings; commit anyhow (yes/no/all)?", 844 "%s contains CR/NL line endings; commit anyhow (yes/no/all)?", 835 blob_str(&fname)); 845 blob_str(&fname)); 836 prompt_user(zMsg, &ans); 846 prompt_user(zMsg, &ans); 837 fossil_free(zMsg); 847 fossil_free(zMsg); 838 c = blob_str(&ans)[0]; 848 c = blob_str(&ans)[0]; ................................................................................................................................................................................ 842 fossil_fatal("Abandoning commit due to CR+NL line endings in %s", 852 fossil_fatal("Abandoning commit due to CR+NL line endings in %s", 843 blob_str(&fname)); 853 blob_str(&fname)); 844 } 854 } 845 blob_reset(&ans); 855 blob_reset(&ans); 846 blob_reset(&fname); 856 blob_reset(&fname); 847 } 857 } 848 } 858 } > 859 > 860 /* > 861 ** qsort() comparison routine for an array of pointers to strings. > 862 */ > 863 static int tagCmp(const void *a, const void *b){ > 864 char **pA = (char**)a; > 865 char **pB = (char**)b; > 866 return fossil_strcmp(pA[0], pB[0]); > 867 } 849 868 850 /* 869 /* 851 ** COMMAND: ci* 870 ** COMMAND: ci* 852 ** COMMAND: commit 871 ** COMMAND: commit 853 ** 872 ** 854 ** Usage: %fossil commit ?OPTIONS? ?FILE...? 873 ** Usage: %fossil commit ?OPTIONS? ?FILE...? 855 ** 874 ** ................................................................................................................................................................................ 860 ** "editor" fossil option (see %fossil help set) will be used, or from 879 ** "editor" fossil option (see %fossil help set) will be used, or from 861 ** the "VISUAL" or "EDITOR" environment variables (in that order) if 880 ** the "VISUAL" or "EDITOR" environment variables (in that order) if 862 ** no editor is set. 881 ** no editor is set. 863 ** 882 ** 864 ** All files that have changed will be committed unless some subset of 883 ** All files that have changed will be committed unless some subset of 865 ** files is specified on the command line. 884 ** files is specified on the command line. 866 ** 885 ** 867 ** The --branch option followed by a branch name causes the new check-in | 886 ** The --branch option followed by a branch name causes the new 868 ** to be placed in the named branch. The --bgcolor option can be followed | 887 ** check-in to be placed in a newly-created branch with the name > 888 ** passed to the --branch option. > 889 ** > 890 ** Use the --branchcolor option followed by a color name (ex: 869 ** by a color name (ex: '#ffc0c0') to specify the background color of | 891 ** '#ffc0c0') to specify the background color of entries in the new 870 ** entries in the new branch when shown in the web timeline interface. | 892 ** branch when shown in the web timeline interface. The use of > 893 ** the --branchcolor option is not recommend. Instead, let Fossil > 894 ** choose the branch color automatically. > 895 ** > 896 ** The --bgcolor option works like --branchcolor but only sets the > 897 ** background color for a single check-in. Subsequent check-ins revert > 898 ** to the default color. 871 ** 899 ** 872 ** A check-in is not permitted to fork unless the --force or -f 900 ** A check-in is not permitted to fork unless the --force or -f 873 ** option appears. A check-in is not allowed against a closed leaf. 901 ** option appears. A check-in is not allowed against a closed leaf. 874 ** 902 ** 875 ** The --private option creates a private check-in that is never synced. 903 ** The --private option creates a private check-in that is never synced. 876 ** Children of private check-ins are automatically private. 904 ** Children of private check-ins are automatically private. 877 ** 905 ** 878 ** the --tag option applies the symbolic tag name to the check-in. 906 ** the --tag option applies the symbolic tag name to the check-in. 879 ** 907 ** 880 ** Options: 908 ** Options: 881 ** --baseline use a baseline manifest in the commit process 909 ** --baseline use a baseline manifest in the commit process 882 ** --bgcolor COLOR apply given COLOR to the branch | 910 ** --bgcolor COLOR apply COLOR to this one check-in only 883 ** --branch NEW-BRANCH-NAME check in to this new branch 911 ** --branch NEW-BRANCH-NAME check in to this new branch > 912 ** --branchcolor COLOR apply given COLOR to the branch 884 ** --comment|-m COMMENT-TEXT use COMMENT-TEXT as commit comment 913 ** --comment|-m COMMENT-TEXT use COMMENT-TEXT as commit comment 885 ** --delta use a delta manifest in the commit process 914 ** --delta use a delta manifest in the commit process 886 ** --force|-f allow forking with this commit 915 ** --force|-f allow forking with this commit 887 ** --message-file|-M FILE read the commit comment from given file 916 ** --message-file|-M FILE read the commit comment from given file 888 ** --nosign do not attempt to sign this commit with gpg 917 ** --nosign do not attempt to sign this commit with gpg 889 ** --private do not sync changes and their descendants 918 ** --private do not sync changes and their descendants 890 ** --tag TAG-NAME assign given tag TAG-NAME to the checkin 919 ** --tag TAG-NAME assign given tag TAG-NAME to the checkin ................................................................................................................................................................................ 906 int forceDelta = 0; /* Force a delta-manifest */ 935 int forceDelta = 0; /* Force a delta-manifest */ 907 int forceBaseline = 0; /* Force a baseline-manifest */ 936 int forceBaseline = 0; /* Force a baseline-manifest */ 908 char *zManifestFile; /* Name of the manifest file */ 937 char *zManifestFile; /* Name of the manifest file */ 909 int useCksum; /* True if checksums should be computed and verified */ 938 int useCksum; /* True if checksums should be computed and verified */ 910 int outputManifest; /* True to output "manifest" and "manifest.uuid" */ 939 int outputManifest; /* True to output "manifest" and "manifest.uuid" */ 911 int testRun; /* True for a test run. Debugging only */ 940 int testRun; /* True for a test run. Debugging only */ 912 const char *zBranch; /* Create a new branch with this name */ 941 const char *zBranch; /* Create a new branch with this name */ 913 const char *zBgColor; /* Set background color when branching */ | 942 const char *zBrClr; /* Set background color when branching */ > 943 const char *zColor; /* One-time check-in color */ 914 const char *zDateOvrd; /* Override date string */ 944 const char *zDateOvrd; /* Override date string */ 915 const char *zUserOvrd; /* Override user name */ 945 const char *zUserOvrd; /* Override user name */ 916 const char *zComFile; /* Read commit message from this file */ 946 const char *zComFile; /* Read commit message from this file */ > 947 int nTag = 0; /* Number of --tag arguments */ 917 const char *zTag; /* Symbolic tag to apply to this check-in */ | 948 const char *zTag; /* A single --tag argument */ > 949 const char **azTag = 0;/* Array of all --tag arguments */ 918 Blob manifest; /* Manifest in baseline form */ 950 Blob manifest; /* Manifest in baseline form */ 919 Blob muuid; /* Manifest uuid */ 951 Blob muuid; /* Manifest uuid */ 920 Blob cksum1, cksum2; /* Before and after commit checksums */ 952 Blob cksum1, cksum2; /* Before and after commit checksums */ 921 Blob cksum1b; /* Checksum recorded in the manifest */ 953 Blob cksum1b; /* Checksum recorded in the manifest */ 922 int szD; /* Size of the delta manifest */ 954 int szD; /* Size of the delta manifest */ 923 int szB; /* Size of the baseline manifest */ 955 int szB; /* Size of the baseline manifest */ 924 956 ................................................................................................................................................................................ 929 if( forceDelta && forceBaseline ){ 961 if( forceDelta && forceBaseline ){ 930 fossil_fatal("cannot use --delta and --baseline together"); 962 fossil_fatal("cannot use --delta and --baseline together"); 931 } 963 } 932 testRun = find_option("test",0,0)!=0; 964 testRun = find_option("test",0,0)!=0; 933 zComment = find_option("comment","m",1); 965 zComment = find_option("comment","m",1); 934 forceFlag = find_option("force", "f", 0)!=0; 966 forceFlag = find_option("force", "f", 0)!=0; 935 zBranch = find_option("branch","b",1); 967 zBranch = find_option("branch","b",1); 936 zBgColor = find_option("bgcolor",0,1); | 968 zColor = find_option("bgcolor",0,1); > 969 zBrClr = find_option("branchcolor",0,1); 937 zTag = find_option("tag",0,1); | 970 while( (zTag = find_option("tag",0,1))!=0 ){ > 971 if( zTag[0]==0 ) continue; > 972 azTag = fossil_realloc(azTag, sizeof(char*)*(nTag+2)); > 973 azTag[nTag++] = zTag; > 974 azTag[nTag] = 0; > 975 } 938 zComFile = find_option("message-file", "M", 1); 976 zComFile = find_option("message-file", "M", 1); 939 if( find_option("private",0,0) ){ 977 if( find_option("private",0,0) ){ 940 g.markPrivate = 1; 978 g.markPrivate = 1; 941 if( zBranch==0 ) zBranch = "private"; 979 if( zBranch==0 ) zBranch = "private"; 942 if( zBgColor==0 ) zBgColor = "#fec084"; /* Orange */ | 980 if( zBrClr==0 && zColor==0 ) zBrClr = "#fec084"; /* Orange */ 943 } 981 } 944 zDateOvrd = find_option("date-override",0,1); 982 zDateOvrd = find_option("date-override",0,1); 945 zUserOvrd = find_option("user-override",0,1); 983 zUserOvrd = find_option("user-override",0,1); 946 db_must_be_within_tree(); 984 db_must_be_within_tree(); 947 noSign = db_get_boolean("omitsign", 0)|noSign; 985 noSign = db_get_boolean("omitsign", 0)|noSign; 948 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } 986 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } 949 useCksum = db_get_boolean("repo-cksum", 1); 987 useCksum = db_get_boolean("repo-cksum", 1); 950 outputManifest = db_get_boolean("manifest", 0); 988 outputManifest = db_get_boolean("manifest", 0); 951 verify_all_options(); 989 verify_all_options(); > 990 > 991 /* Escape special characters in tags and put all tags in sorted order */ > 992 if( nTag ){ > 993 int i; > 994 for(i=0; i<nTag; i++) azTag[i] = mprintf("%F", azTag[i]); > 995 qsort((void*)azTag, nTag, sizeof(azTag[0]), tagCmp); > 996 } 952 997 953 /* So that older versions of Fossil (that do not understand delta- 998 /* So that older versions of Fossil (that do not understand delta- 954 ** manifest) can continue to use this repository, do not create a new 999 ** manifest) can continue to use this repository, do not create a new 955 ** delta-manifest unless this repository already contains one or more 1000 ** delta-manifest unless this repository already contains one or more 956 ** delta-manifets, or unless the delta-manifest is explicitly requested 1001 ** delta-manifets, or unless the delta-manifest is explicitly requested 957 ** by the --delta option. 1002 ** by the --delta option. 958 */ 1003 */ ................................................................................................................................................................................ 1124 blob_append(&comment, "(no comment)", -1); 1169 blob_append(&comment, "(no comment)", -1); 1125 } 1170 } 1126 if( forceDelta ){ 1171 if( forceDelta ){ 1127 blob_zero(&manifest); 1172 blob_zero(&manifest); 1128 }else{ 1173 }else{ 1129 create_manifest(&manifest, 0, 0, &comment, vid, 1174 create_manifest(&manifest, 0, 0, &comment, vid, 1130 !forceFlag, useCksum ? &cksum1 : 0, 1175 !forceFlag, useCksum ? &cksum1 : 0, 1131 zDateOvrd, zUserOvrd, zBranch, zBgColor, zTag, &szB); | 1176 zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr, > 1177 azTag, &szB); 1132 } 1178 } 1133 1179 1134 /* See if a delta-manifest would be more appropriate */ 1180 /* See if a delta-manifest would be more appropriate */ 1135 if( !forceBaseline ){ 1181 if( !forceBaseline ){ 1136 const char *zBaselineUuid; 1182 const char *zBaselineUuid; 1137 Manifest *pParent; 1183 Manifest *pParent; 1138 Manifest *pBaseline; 1184 Manifest *pBaseline; ................................................................................................................................................................................ 1144 zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); 1190 zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); 1145 pBaseline = pParent; 1191 pBaseline = pParent; 1146 } 1192 } 1147 if( pBaseline ){ 1193 if( pBaseline ){ 1148 Blob delta; 1194 Blob delta; 1149 create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid, 1195 create_manifest(&delta, zBaselineUuid, pBaseline, &comment, vid, 1150 !forceFlag, useCksum ? &cksum1 : 0, 1196 !forceFlag, useCksum ? &cksum1 : 0, 1151 zDateOvrd, zUserOvrd, zBranch, zBgColor, zTag, &szD); | 1197 zDateOvrd, zUserOvrd, zBranch, zColor, zBrClr, > 1198 azTag, &szD); 1152 /* 1199 /* 1153 ** At this point, two manifests have been constructed, either of 1200 ** At this point, two manifests have been constructed, either of 1154 ** which would work for this checkin. The first manifest (held 1201 ** which would work for this checkin. The first manifest (held 1155 ** in the "manifest" variable) is a baseline manifest and the second 1202 ** in the "manifest" variable) is a baseline manifest and the second 1156 ** (held in variable named "delta") is a delta manifest. The 1203 ** (held in variable named "delta") is a delta manifest. The 1157 ** question now is: which manifest should we use? 1204 ** question now is: which manifest should we use? 1158 ** 1205 **
Changes to src/clone.c
148 db_set("content-schema", CONTENT_SCHEMA, 0); 148 db_set("content-schema", CONTENT_SCHEMA, 0); 149 db_set("aux-schema", AUX_SCHEMA, 0); 149 db_set("aux-schema", AUX_SCHEMA, 0); 150 db_set("last-sync-url", g.argv[2], 0); 150 db_set("last-sync-url", g.argv[2], 0); 151 if( g.zSSLIdentity!=0 ){ 151 if( g.zSSLIdentity!=0 ){ 152 /* If the --ssl-identity option was specified, store it as a setting */ 152 /* If the --ssl-identity option was specified, store it as a setting */ 153 Blob fn; 153 Blob fn; 154 blob_zero(&fn); 154 blob_zero(&fn); 155 file_canonical_name(g.zSSLIdentity, &fn); | 155 file_canonical_name(g.zSSLIdentity, &fn, 0); 156 db_set("ssl-identity", blob_str(&fn), 0); 156 db_set("ssl-identity", blob_str(&fn), 0); 157 blob_reset(&fn); 157 blob_reset(&fn); 158 } 158 } 159 db_multi_exec( 159 db_multi_exec( 160 "REPLACE INTO config(name,value,mtime)" 160 "REPLACE INTO config(name,value,mtime)" 161 " VALUES('server-code', lower(hex(randomblob(20))), now());" 161 " VALUES('server-code', lower(hex(randomblob(20))), now());" 162 ); 162 );
Changes to src/configure.c
76 int groupMask; /* Which config groups is it part of */ 76 int groupMask; /* Which config groups is it part of */ 77 } aConfig[] = { 77 } aConfig[] = { 78 { "css", CONFIGSET_SKIN }, 78 { "css", CONFIGSET_SKIN }, 79 { "header", CONFIGSET_SKIN }, 79 { "header", CONFIGSET_SKIN }, 80 { "footer", CONFIGSET_SKIN }, 80 { "footer", CONFIGSET_SKIN }, 81 { "logo-mimetype", CONFIGSET_SKIN }, 81 { "logo-mimetype", CONFIGSET_SKIN }, 82 { "logo-image", CONFIGSET_SKIN }, 82 { "logo-image", CONFIGSET_SKIN }, > 83 { "background-mimetype", CONFIGSET_SKIN }, > 84 { "background-image", CONFIGSET_SKIN }, > 85 { "index-page", CONFIGSET_SKIN }, > 86 { "timeline-block-markup", CONFIGSET_SKIN }, > 87 { "timeline-max-comment", CONFIGSET_SKIN }, > 88 #ifdef FOSSIL_ENABLE_TCL > 89 { "tcl", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER }, > 90 #endif > 91 83 { "project-name", CONFIGSET_PROJ }, 92 { "project-name", CONFIGSET_PROJ }, 84 { "project-description", CONFIGSET_PROJ }, 93 { "project-description", CONFIGSET_PROJ }, 85 { "manifest", CONFIGSET_PROJ }, 94 { "manifest", CONFIGSET_PROJ }, 86 { "ignore-glob", CONFIGSET_PROJ }, 95 { "ignore-glob", CONFIGSET_PROJ }, 87 { "crnl-glob", CONFIGSET_PROJ }, 96 { "crnl-glob", CONFIGSET_PROJ }, 88 { "empty-dirs", CONFIGSET_PROJ }, 97 { "empty-dirs", CONFIGSET_PROJ }, 89 { "allow-symlinks", CONFIGSET_PROJ }, 98 { "allow-symlinks", CONFIGSET_PROJ }, 90 { "index-page", CONFIGSET_SKIN }, < 91 #ifdef FOSSIL_ENABLE_TCL < 92 { "tcl", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER }, < 93 #endif < 94 { "timeline-block-markup", CONFIGSET_SKIN }, < 95 { "timeline-max-comment", CONFIGSET_SKIN }, < > 99 96 { "ticket-table", CONFIGSET_TKT }, 100 { "ticket-table", CONFIGSET_TKT }, 97 { "ticket-common", CONFIGSET_TKT }, 101 { "ticket-common", CONFIGSET_TKT }, 98 { "ticket-change", CONFIGSET_TKT }, 102 { "ticket-change", CONFIGSET_TKT }, 99 { "ticket-newpage", CONFIGSET_TKT }, 103 { "ticket-newpage", CONFIGSET_TKT }, 100 { "ticket-viewpage", CONFIGSET_TKT }, 104 { "ticket-viewpage", CONFIGSET_TKT }, 101 { "ticket-editpage", CONFIGSET_TKT }, 105 { "ticket-editpage", CONFIGSET_TKT }, 102 { "ticket-reportlist", CONFIGSET_TKT }, 106 { "ticket-reportlist", CONFIGSET_TKT }, 103 { "ticket-report-template", CONFIGSET_TKT }, 107 { "ticket-report-template", CONFIGSET_TKT }, 104 { "ticket-key-template", CONFIGSET_TKT }, 108 { "ticket-key-template", CONFIGSET_TKT }, 105 { "ticket-title-expr", CONFIGSET_TKT }, 109 { "ticket-title-expr", CONFIGSET_TKT }, 106 { "ticket-closed-expr", CONFIGSET_TKT }, 110 { "ticket-closed-expr", CONFIGSET_TKT }, 107 { "@reportfmt", CONFIGSET_TKT }, 111 { "@reportfmt", CONFIGSET_TKT }, > 112 108 { "@user", CONFIGSET_USER }, 113 { "@user", CONFIGSET_USER }, > 114 109 { "@concealed", CONFIGSET_ADDR }, 115 { "@concealed", CONFIGSET_ADDR }, > 116 110 { "@shun", CONFIGSET_SHUN }, 117 { "@shun", CONFIGSET_SHUN }, > 118 111 { "xfer-common-script", CONFIGSET_XFER }, 119 { "xfer-common-script", CONFIGSET_XFER }, 112 { "xfer-push-script", CONFIGSET_XFER }, 120 { "xfer-push-script", CONFIGSET_XFER }, > 121 113 }; 122 }; 114 static int iConfig = 0; 123 static int iConfig = 0; 115 124 116 /* 125 /* 117 ** Return name of first configuration property matching the given mask. 126 ** Return name of first configuration property matching the given mask. 118 */ 127 */ 119 const char *configure_first_name(int iMask){ 128 const char *configure_first_name(int iMask){
Changes to src/cson_amalgamation.c
1425 1425 1426 1426 1427 /** 1427 /** 1428 Type IDs corresponding to JavaScript/JSON types. 1428 Type IDs corresponding to JavaScript/JSON types. 1429 */ 1429 */ 1430 enum cson_type_id { 1430 enum cson_type_id { 1431 /** 1431 /** 1432 The special "null" value constant. | 1432 The special "undefined" value constant. 1433 1433 1434 Its value must be 0 for internal reasons. 1434 Its value must be 0 for internal reasons. 1435 */ 1435 */ 1436 CSON_TYPE_UNDEF = 0, 1436 CSON_TYPE_UNDEF = 0, 1437 /** 1437 /** 1438 The special "null" value constant. 1438 The special "null" value constant. 1439 */ 1439 */ ................................................................................................................................................................................ 1595 static const cson_value cson_value_bool_empty = { &cson_value_api_bool, NULL, 0 1595 static const cson_value cson_value_bool_empty = { &cson_value_api_bool, NULL, 0 1596 static const cson_value cson_value_integer_empty = { &cson_value_api_integer, NU 1596 static const cson_value cson_value_integer_empty = { &cson_value_api_integer, NU 1597 static const cson_value cson_value_double_empty = { &cson_value_api_double, NULL 1597 static const cson_value cson_value_double_empty = { &cson_value_api_double, NULL 1598 static const cson_value cson_value_string_empty = { &cson_value_api_string, NULL 1598 static const cson_value cson_value_string_empty = { &cson_value_api_string, NULL 1599 static const cson_value cson_value_array_empty = { &cson_value_api_array, NULL, 1599 static const cson_value cson_value_array_empty = { &cson_value_api_array, NULL, 1600 static const cson_value cson_value_object_empty = { &cson_value_api_object, NULL 1600 static const cson_value cson_value_object_empty = { &cson_value_api_object, NULL 1601 1601 > 1602 /** > 1603 Strings are allocated as an instances of this class with N+1 > 1604 trailing bytes, where N is the length of the string being > 1605 allocated. To convert a cson_string to c-string we simply increment > 1606 the cson_string pointer. To do the opposite we use (cstr - > 1607 sizeof(cson_string)). Zero-length strings are a special case > 1608 handled by a couple of the cson_string functions. > 1609 */ 1602 struct cson_string 1610 struct cson_string 1603 { 1611 { 1604 unsigned int length; 1612 unsigned int length; 1605 }; 1613 }; 1606 #define cson_string_empty_m {0/*length*/} 1614 #define cson_string_empty_m {0/*length*/} 1607 static const cson_string cson_string_empty = cson_string_empty_m; 1615 static const cson_string cson_string_empty = cson_string_empty_m; 1608 1616 1609 1617 1610 1618 1611 #define CSON_CAST(T,V) ((T*)((V)->value)) 1619 #define CSON_CAST(T,V) ((T*)((V)->value)) 1612 #define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)) 1620 #define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)) > 1621 > 1622 #if CSON_VOID_PTR_IS_BIG > 1623 # define CSON_INT(V) ((cson_int_t*)(&((V)->value))) > 1624 #else 1613 #define CSON_INT(V) ((cson_int_t*)(V)->value) | 1625 # define CSON_INT(V) ((cson_int_t*)(V)->value) > 1626 #endif > 1627 1614 #define CSON_DBL(V) CSON_CAST(cson_double_t,(V)) 1628 #define CSON_DBL(V) CSON_CAST(cson_double_t,(V)) 1615 #define CSON_STR(V) CSON_CAST(cson_string,(V)) 1629 #define CSON_STR(V) CSON_CAST(cson_string,(V)) 1616 #define CSON_OBJ(V) CSON_CAST(cson_object,(V)) 1630 #define CSON_OBJ(V) CSON_CAST(cson_object,(V)) 1617 #define CSON_ARRAY(V) CSON_CAST(cson_array,(V)) 1631 #define CSON_ARRAY(V) CSON_CAST(cson_array,(V)) 1618 1632 1619 /** 1633 /** 1620 1634 ................................................................................................................................................................................ 1666 { &cson_value_api_undef, NULL, 0 }, /* UNDEF */ 1680 { &cson_value_api_undef, NULL, 0 }, /* UNDEF */ 1667 { &cson_value_api_null, NULL, 0 }, /* NULL */ 1681 { &cson_value_api_null, NULL, 0 }, /* NULL */ 1668 { &cson_value_api_bool, &CSON_EMPTY_HOLDER.trueValue, 0 }, /* TRUE */ 1682 { &cson_value_api_bool, &CSON_EMPTY_HOLDER.trueValue, 0 }, /* TRUE */ 1669 { &cson_value_api_bool, NULL, 0 }, /* FALSE */ 1683 { &cson_value_api_bool, NULL, 0 }, /* FALSE */ 1670 { &cson_value_api_integer, NULL, 0 }, /* INT_0 */ 1684 { &cson_value_api_integer, NULL, 0 }, /* INT_0 */ 1671 { &cson_value_api_double, NULL, 0 }, /* DBL_0 */ 1685 { &cson_value_api_double, NULL, 0 }, /* DBL_0 */ 1672 { &cson_value_api_string, &CSON_EMPTY_HOLDER.stringValue, 0 }, /* STR_EMPTY */ 1686 { &cson_value_api_string, &CSON_EMPTY_HOLDER.stringValue, 0 }, /* STR_EMPTY */ 1673 { 0, NULL, 0 } | 1687 { NULL, NULL, 0 } 1674 }; 1688 }; 1675 1689 1676 1690 1677 /** 1691 /** 1678 Returns non-0 (true) if m is one of our special 1692 Returns non-0 (true) if m is one of our special 1679 "built-in" values, e.g. from CSON_SPECIAL_VALUES and some 1693 "built-in" values, e.g. from CSON_SPECIAL_VALUES and some 1680 "empty" values. 1694 "empty" values. ................................................................................................................................................................................ 1914 /** 1928 /** 1915 Fetches v's string value as a non-const string. 1929 Fetches v's string value as a non-const string. 1916 1930 1917 cson_strings are supposed to be immutable, but this form provides 1931 cson_strings are supposed to be immutable, but this form provides 1918 access to the immutable bits, which are v->length bytes long. A 1932 access to the immutable bits, which are v->length bytes long. A 1919 length-0 string is returned as NULL from here, as opposed to 1933 length-0 string is returned as NULL from here, as opposed to 1920 "". (This is a side-effect of the string allocation mechanism.) 1934 "". (This is a side-effect of the string allocation mechanism.) 1921 Returns NULL if !v. | 1935 Returns NULL if !v or if v is the internal empty-string singleton. 1922 */ 1936 */ 1923 static char * cson_string_str(cson_string *v) 1937 static char * cson_string_str(cson_string *v) 1924 { 1938 { 1925 /* 1939 /* 1926 See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thr 1940 See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thr 1927 */ 1941 */ 1928 #if 1 1942 #if 1 ................................................................................................................................................................................ 1946 { 1960 { 1947 /* 1961 /* 1948 See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thr 1962 See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thr 1949 */ 1963 */ 1950 #if 1 1964 #if 1 1951 if( ! v ) return NULL; 1965 if( ! v ) return NULL; 1952 else if( v == &CSON_EMPTY_HOLDER.stringValue ) return ""; 1966 else if( v == &CSON_EMPTY_HOLDER.stringValue ) return ""; > 1967 else { > 1968 assert((0 < v->length) && "How do we have a non-singleton empty string?" 1953 else return (char *)((unsigned char *)(v+1)); | 1969 return (char const *)((unsigned char const *)(v+1)); > 1970 } 1954 #else 1971 #else 1955 return (NULL == v) 1972 return (NULL == v) 1956 ? NULL 1973 ? NULL 1957 : (v->length 1974 : (v->length 1958 ? (char const *) ((unsigned char const *)(v+1)) 1975 ? (char const *) ((unsigned char const *)(v+1)) 1959 : ""); 1976 : ""); 1960 #endif 1977 #endif ................................................................................................................................................................................ 2026 cson_value * key; 2043 cson_value * key; 2027 cson_value * value; 2044 cson_value * value; 2028 }; 2045 }; 2029 #define cson_kvp_empty_m {NULL,NULL} 2046 #define cson_kvp_empty_m {NULL,NULL} 2030 static const cson_kvp cson_kvp_empty = cson_kvp_empty_m; 2047 static const cson_kvp cson_kvp_empty = cson_kvp_empty_m; 2031 2048 2032 /** @def CSON_OBJECT_PROPS_SORT 2049 /** @def CSON_OBJECT_PROPS_SORT > 2050 > 2051 Don't use this - it has not been updated to account for internal > 2052 changes in cson_object. 2033 2053 2034 If CSON_OBJECT_PROPS_SORT is set to a true value then 2054 If CSON_OBJECT_PROPS_SORT is set to a true value then 2035 qsort() and bsearch() are used to sort (upon insertion) 2055 qsort() and bsearch() are used to sort (upon insertion) 2036 and search cson_object::kvp property lists. This costs us 2056 and search cson_object::kvp property lists. This costs us 2037 a re-sort on each insertion but searching is O(log n) 2057 a re-sort on each insertion but searching is O(log n) 2038 average/worst case (and O(1) best-case). 2058 average/worst case (and O(1) best-case). 2039 2059 ................................................................................................................................................................................ 2208 2228 2209 extra is only valid for type CSON_TYPE_STRING, and must be the length 2229 extra is only valid for type CSON_TYPE_STRING, and must be the length 2210 of the string to allocate + 1 byte (for the NUL). 2230 of the string to allocate + 1 byte (for the NUL). 2211 2231 2212 The returned value->api member will be set appropriately and 2232 The returned value->api member will be set appropriately and 2213 val->value will be set to point to the memory allocated to hold the 2233 val->value will be set to point to the memory allocated to hold the 2214 native value type. Use the internal CSON_CAST() family of macros to 2234 native value type. Use the internal CSON_CAST() family of macros to 2215 convert them. | 2235 convert the cson_values to their corresponding native > 2236 representation. 2216 2237 2217 Returns NULL on allocation error. 2238 Returns NULL on allocation error. 2218 2239 2219 @see cson_value_new_array() 2240 @see cson_value_new_array() 2220 @see cson_value_new_object() 2241 @see cson_value_new_object() 2221 @see cson_value_new_string() 2242 @see cson_value_new_string() 2222 @see cson_value_new_integer() 2243 @see cson_value_new_integer() 2223 @see cson_value_new_double() 2244 @see cson_value_new_double() 2224 @see cson_value_new_bool() 2245 @see cson_value_new_bool() 2225 @see cson_value_free() 2246 @see cson_value_free() 2226 */ 2247 */ 2227 static cson_value * cson_value_new(cson_type_id t, size_t extra); | 2248 static cson_value * cson_value_new(cson_type_id t, size_t extra) 2228 < 2229 cson_value * cson_value_new(cson_type_id t, size_t extra) < 2230 { 2249 { 2231 static const size_t vsz = sizeof(cson_value); 2250 static const size_t vsz = sizeof(cson_value); 2232 const size_t sz = vsz + extra; 2251 const size_t sz = vsz + extra; 2233 size_t tx = 0; 2252 size_t tx = 0; 2234 cson_value def = cson_value_undef; 2253 cson_value def = cson_value_undef; 2235 cson_value * v = NULL; 2254 cson_value * v = NULL; 2236 char const * reason = "cson_value_new"; 2255 char const * reason = "cson_value_new"; ................................................................................................................................................................................ 2246 assert( 0 == extra ); 2265 assert( 0 == extra ); 2247 def = cson_value_double_empty; 2266 def = cson_value_double_empty; 2248 tx = sizeof(cson_double_t); 2267 tx = sizeof(cson_double_t); 2249 reason = "cson_value:double"; 2268 reason = "cson_value:double"; 2250 break; 2269 break; 2251 case CSON_TYPE_INTEGER: 2270 case CSON_TYPE_INTEGER: 2252 assert( 0 == extra ); 2271 assert( 0 == extra ); 2253 /* FIXME: if sizeof(void*) >= sizeof(cson_int_t) then store < 2254 the int value directly in the void pointer (requires no < 2255 extra alloc). The current behaviour requires 32 < 2256 bytes(!!!) on 64-bit builds. < 2257 */ < 2258 def = cson_value_integer_empty; 2272 def = cson_value_integer_empty; > 2273 #if !CSON_VOID_PTR_IS_BIG 2259 tx = sizeof(cson_int_t); 2274 tx = sizeof(cson_int_t); > 2275 #endif 2260 reason = "cson_value:int"; 2276 reason = "cson_value:int"; 2261 break; 2277 break; 2262 case CSON_TYPE_STRING: 2278 case CSON_TYPE_STRING: 2263 assert( 0 != extra ); 2279 assert( 0 != extra ); 2264 def = cson_value_string_empty; 2280 def = cson_value_string_empty; 2265 tx = sizeof(cson_string); 2281 tx = sizeof(cson_string); 2266 reason = "cson_value:string"; 2282 reason = "cson_value:string"; ................................................................................................................................................................................ 2646 i = (cson_int_t)d; 2662 i = (cson_int_t)d; 2647 break; 2663 break; 2648 } 2664 } 2649 case CSON_TYPE_STRING: 2665 case CSON_TYPE_STRING: 2650 case CSON_TYPE_ARRAY: 2666 case CSON_TYPE_ARRAY: 2651 case CSON_TYPE_OBJECT: 2667 case CSON_TYPE_OBJECT: 2652 default: 2668 default: > 2669 rc = cson_rc.TypeError; 2653 break; | 2670 break; 2654 } 2671 } 2655 if(v) *v = i; | 2672 if(!rc && v) *v = i; 2656 return rc; 2673 return rc; 2657 } 2674 } 2658 } 2675 } 2659 2676 2660 cson_int_t cson_value_get_integer( cson_value const * val ) 2677 cson_int_t cson_value_get_integer( cson_value const * val ) 2661 { 2678 { 2662 cson_int_t i = 0; 2679 cson_int_t i = 0; ................................................................................................................................................................................ 2861 2878 2862 cson_value * cson_value_new_integer( cson_int_t v ) 2879 cson_value * cson_value_new_integer( cson_int_t v ) 2863 { 2880 { 2864 if( 0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_INT_0]; 2881 if( 0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_INT_0]; 2865 else 2882 else 2866 { 2883 { 2867 cson_value * c = cson_value_new(CSON_TYPE_INTEGER,0); 2884 cson_value * c = cson_value_new(CSON_TYPE_INTEGER,0); 2868 | 2885 #if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG > 2886 assert( sizeof(cson_int_t) <= sizeof(void *) ); > 2887 #endif 2869 if( c ) 2888 if( c ) 2870 { 2889 { 2871 *CSON_INT(c) = v; 2890 *CSON_INT(c) = v; 2872 } 2891 } 2873 return c; 2892 return c; 2874 } 2893 } 2875 } 2894 } ................................................................................................................................................................................ 3140 cson_value_free( kvp->value ); 3159 cson_value_free( kvp->value ); 3141 cson_refcount_incr( v ); 3160 cson_refcount_incr( v ); 3142 kvp->value = v; 3161 kvp->value = v; 3143 } 3162 } 3144 return 0; 3163 return 0; 3145 } 3164 } 3146 if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) 3165 if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) 3147 { | 3166 { /* reserve space */ 3148 unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6; 3167 unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6; 3149 if( n > cson_kvp_list_reserve( &obj->kvp, n ) ) 3168 if( n > cson_kvp_list_reserve( &obj->kvp, n ) ) 3150 { 3169 { 3151 return cson_rc.AllocError; 3170 return cson_rc.AllocError; 3152 } 3171 } 3153 } 3172 } 3154 { /* insert new item... */ 3173 { /* insert new item... */ ................................................................................................................................................................................ 3279 kvp = cson_kvp_alloc(); 3298 kvp = cson_kvp_alloc(); 3280 if( ! kvp ) 3299 if( ! kvp ) 3281 { 3300 { 3282 cson_value_free(val); 3301 cson_value_free(val); 3283 return cson_rc.AllocError; 3302 return cson_rc.AllocError; 3284 } 3303 } 3285 kvp->key = cson_string_value(p->ckey)/*transfer ownership*/; 3304 kvp->key = cson_string_value(p->ckey)/*transfer ownership*/; > 3305 assert(0 == kvp->key->refcount); > 3306 cson_refcount_incr(kvp->key); 3286 p->ckey = NULL; 3307 p->ckey = NULL; 3287 kvp->value = val; 3308 kvp->value = val; 3288 cson_refcount_incr( val ); 3309 cson_refcount_incr( val ); 3289 rc = cson_kvp_list_append( &obj->kvp, kvp ); 3310 rc = cson_kvp_list_append( &obj->kvp, kvp ); 3290 if( 0 != rc ) 3311 if( 0 != rc ) 3291 { 3312 { 3292 cson_kvp_free( kvp ); 3313 cson_kvp_free( kvp ); ................................................................................................................................................................................ 3359 case JSON_T_OBJECT_BEGIN: { 3380 case JSON_T_OBJECT_BEGIN: { 3360 cson_value * obja = (JSON_T_ARRAY_BEGIN == type) 3381 cson_value * obja = (JSON_T_ARRAY_BEGIN == type) 3361 ? cson_value_new_array() 3382 ? cson_value_new_array() 3362 : cson_value_new_object(); 3383 : cson_value_new_object(); 3363 if( ! obja ) 3384 if( ! obja ) 3364 { 3385 { 3365 p->errNo = cson_rc.AllocError; 3386 p->errNo = cson_rc.AllocError; 3366 return 0; | 3387 break; 3367 } 3388 } 3368 if( 0 != rc ) break; 3389 if( 0 != rc ) break; 3369 if( ! p->root ) 3390 if( ! p->root ) 3370 { 3391 { 3371 p->root = p->node = obja; 3392 p->root = p->node = obja; 3372 rc = cson_array_append( &p->stack, obja ); 3393 rc = cson_array_append( &p->stack, obja ); 3373 if( 0 != rc ) 3394 if( 0 != rc ) ................................................................................................................................................................................ 3382 ; 3403 ; 3383 ++p->totalValueCount; 3404 ++p->totalValueCount; 3384 } 3405 } 3385 } 3406 } 3386 else 3407 else 3387 { 3408 { 3388 rc = cson_array_append( &p->stack, obja ); 3409 rc = cson_array_append( &p->stack, obja ); > 3410 if(rc) cson_value_free( obja ); > 3411 else > 3412 { 3389 if( 0 == rc ) rc = cson_parser_push_value( p, obja ); | 3413 rc = cson_parser_push_value( p, obja ); 3390 if( 0 == rc ) p->node = obja; | 3414 if( 0 == rc ) p->node = obja; > 3415 } 3391 } 3416 } 3392 break; 3417 break; 3393 } 3418 } 3394 case JSON_T_ARRAY_END: 3419 case JSON_T_ARRAY_END: 3395 case JSON_T_OBJECT_END: { 3420 case JSON_T_OBJECT_END: { 3396 if( 0 == p->stack.list.count ) 3421 if( 0 == p->stack.list.count ) 3397 { 3422 { ................................................................................................................................................................................ 4576 cson_value * cson_object_get_sub2( cson_object const * obj, char const * path ) 4601 cson_value * cson_object_get_sub2( cson_object const * obj, char const * path ) 4577 { 4602 { 4578 cson_value * v = NULL; 4603 cson_value * v = NULL; 4579 cson_object_fetch_sub2( obj, &v, path ); 4604 cson_object_fetch_sub2( obj, &v, path ); 4580 return v; 4605 return v; 4581 } 4606 } 4582 4607 > 4608 > 4609 /** > 4610 If v is-a Object or Array then this function returns a deep > 4611 clone, otherwise it returns v. In either case, the refcount > 4612 of the returned value is increased by 1 by this call. > 4613 */ > 4614 static cson_value * cson_value_clone_ref( cson_value * v ) > 4615 { > 4616 cson_value * rc = NULL; > 4617 #define TRY_SHARING 1 > 4618 #if TRY_SHARING > 4619 if(!v ) return rc; > 4620 else if( cson_value_is_object(v) > 4621 || cson_value_is_array(v)) > 4622 { > 4623 rc = cson_value_clone( v ); > 4624 } > 4625 else > 4626 { > 4627 rc = v; > 4628 } > 4629 #else > 4630 rc = cson_value_clone(v); > 4631 #endif > 4632 #undef TRY_SHARING > 4633 cson_value_add_reference(rc); > 4634 return rc; > 4635 } > 4636 4583 static cson_value * cson_value_clone_array( cson_value const * orig ) 4637 static cson_value * cson_value_clone_array( cson_value const * orig ) 4584 { 4638 { 4585 unsigned int i = 0; 4639 unsigned int i = 0; 4586 cson_array const * asrc = cson_value_get_array( orig ); 4640 cson_array const * asrc = cson_value_get_array( orig ); 4587 unsigned int alen = cson_array_length_get( asrc ); 4641 unsigned int alen = cson_array_length_get( asrc ); 4588 cson_value * destV = NULL; 4642 cson_value * destV = NULL; 4589 cson_array * destA = NULL; 4643 cson_array * destA = NULL; ................................................................................................................................................................................ 4598 return NULL; 4652 return NULL; 4599 } 4653 } 4600 for( ; i < alen; ++i ) 4654 for( ; i < alen; ++i ) 4601 { 4655 { 4602 cson_value * ch = cson_array_get( asrc, i ); 4656 cson_value * ch = cson_array_get( asrc, i ); 4603 if( NULL != ch ) 4657 if( NULL != ch ) 4604 { 4658 { 4605 cson_value * cl = cson_value_clone( ch ); | 4659 cson_value * cl = cson_value_clone_ref( ch ); 4606 if( NULL == cl ) 4660 if( NULL == cl ) 4607 { 4661 { 4608 cson_value_free( destV ); 4662 cson_value_free( destV ); 4609 return NULL; 4663 return NULL; 4610 } 4664 } 4611 if( 0 != cson_array_set( destA, i, cl ) ) 4665 if( 0 != cson_array_set( destA, i, cl ) ) 4612 { 4666 { 4613 cson_value_free( cl ); 4667 cson_value_free( cl ); 4614 cson_value_free( destV ); 4668 cson_value_free( destV ); 4615 return NULL; 4669 return NULL; 4616 } 4670 } > 4671 cson_value_free(cl)/*remove our artificial reference */; 4617 } 4672 } 4618 } 4673 } 4619 return destV; 4674 return destV; 4620 } 4675 } 4621 | 4676 4622 static cson_value * cson_value_clone_object( cson_value const * orig ) 4677 static cson_value * cson_value_clone_object( cson_value const * orig ) 4623 { 4678 { 4624 cson_object const * src = cson_value_get_object( orig ); 4679 cson_object const * src = cson_value_get_object( orig ); 4625 cson_value * destV = NULL; 4680 cson_value * destV = NULL; 4626 cson_object * dest = NULL; 4681 cson_object * dest = NULL; 4627 cson_kvp const * kvp = NULL; 4682 cson_kvp const * kvp = NULL; 4628 cson_object_iterator iter = cson_object_iterator_empty; 4683 cson_object_iterator iter = cson_object_iterator_empty; ................................................................................................................................................................................ 4637 assert( dest ); 4692 assert( dest ); 4638 if( src->kvp.count > cson_kvp_list_reserve( &dest->kvp, src->kvp.count ) ){ 4693 if( src->kvp.count > cson_kvp_list_reserve( &dest->kvp, src->kvp.count ) ){ 4639 cson_value_free( destV ); 4694 cson_value_free( destV ); 4640 return NULL; 4695 return NULL; 4641 } 4696 } 4642 while( (kvp = cson_object_iter_next( &iter )) ) 4697 while( (kvp = cson_object_iter_next( &iter )) ) 4643 { 4698 { 4644 /* < 4645 FIXME: refcount the keys! We first need a setter which takes < 4646 a cson_string or cson_value key type. < 4647 */ < 4648 cson_value * key = NULL; 4699 cson_value * key = NULL; 4649 cson_value * val = NULL; 4700 cson_value * val = NULL; > 4701 assert( kvp->key && (kvp->key->refcount>0) ); 4650 key = cson_value_clone(kvp->key); | 4702 key = cson_value_clone_ref(kvp->key); 4651 val = key ? cson_value_clone( kvp->value ) : NULL; | 4703 val = key ? cson_value_clone_ref(kvp->value) : NULL; 4652 if( ! key || !val ){ 4704 if( ! key || !val ){ 4653 cson_value_free(key); < 4654 cson_value_free(val); < 4655 cson_value_free(destV); < 4656 return NULL; < > 4705 goto error; 4657 } 4706 } 4658 assert( CSON_STR(key) ); 4707 assert( CSON_STR(key) ); 4659 if( 0 != cson_object_set_s( dest, CSON_STR(key), val ) ) 4708 if( 0 != cson_object_set_s( dest, CSON_STR(key), val ) ) 4660 { 4709 { > 4710 goto error; > 4711 } > 4712 /* remove our references */ 4661 cson_value_free(key); | 4713 cson_value_free(key); 4662 cson_value_free(val); | 4714 cson_value_free(val); > 4715 continue; > 4716 error: > 4717 cson_value_free(key); > 4718 cson_value_free(val); 4663 cson_value_free(destV); | 4719 cson_value_free(destV); 4664 return NULL; | 4720 destV = NULL; 4665 } < > 4721 break; 4666 } 4722 } 4667 return destV; 4723 return destV; 4668 } 4724 } 4669 4725 4670 cson_value * cson_value_clone( cson_value const * orig ) 4726 cson_value * cson_value_clone( cson_value const * orig ) 4671 { 4727 { 4672 if( NULL == orig ) return NULL; 4728 if( NULL == orig ) return NULL; ................................................................................................................................................................................ 4731 if(x) cson_value_free(cson_object_value(x)); 4787 if(x) cson_value_free(cson_object_value(x)); 4732 } 4788 } 4733 void cson_free_array(cson_array *x) 4789 void cson_free_array(cson_array *x) 4734 { 4790 { 4735 if(x) cson_value_free(cson_array_value(x)); 4791 if(x) cson_value_free(cson_array_value(x)); 4736 } 4792 } 4737 4793 4738 void cson_free_string(cson_string const *x) | 4794 void cson_free_string(cson_string *x) 4739 { 4795 { 4740 if(x) cson_value_free(cson_string_value(x)); 4796 if(x) cson_value_free(cson_string_value(x)); 4741 } 4797 } 4742 void cson_free_value(cson_value *x) 4798 void cson_free_value(cson_value *x) 4743 { 4799 { 4744 cson_value_free(x); | 4800 if(x) cson_value_free(x); 4745 } 4801 } 4746 4802 4747 4803 4748 #if 0 4804 #if 0 4749 /* i'm not happy with this... */ 4805 /* i'm not happy with this... */ 4750 char * cson_pod_to_string( cson_value const * orig ) 4806 char * cson_pod_to_string( cson_value const * orig ) 4751 { 4807 { ................................................................................................................................................................................ 4905 case CSON_TYPE_NULL: 4961 case CSON_TYPE_NULL: 4906 case CSON_TYPE_BOOL: 4962 case CSON_TYPE_BOOL: 4907 assert( 0 && "Should have been caught by is-builtin check!" ); 4963 assert( 0 && "Should have been caught by is-builtin check!" ); 4908 break; 4964 break; 4909 default: 4965 default: 4910 assert(0 && "Invalid typeID!"); 4966 assert(0 && "Invalid typeID!"); 4911 return 0; 4967 return 0; 4912 | 4968 #undef RCCHECK 4913 } 4969 } 4914 return rc; 4970 return rc; 4915 } 4971 } 4916 } 4972 } 4917 4973 4918 int cson_object_merge( cson_object * dest, cson_object const * src, int flags ){ 4974 int cson_object_merge( cson_object * dest, cson_object const * src, int flags ){ 4919 cson_object_iterator iter = cson_object_iterator_empty; 4975 cson_object_iterator iter = cson_object_iterator_empty; ................................................................................................................................................................................ 4952 else continue; 5008 else continue; 4953 } 5009 } 4954 return 0; 5010 return 0; 4955 } 5011 } 4956 5012 4957 static cson_value * cson_guess_arg_type(char const *arg){ 5013 static cson_value * cson_guess_arg_type(char const *arg){ 4958 char * end = NULL; 5014 char * end = NULL; 4959 if(('0'<=*arg) && ('9'>=*arg)){ | 5015 if(!arg || !*arg) return cson_value_null(); > 5016 else if(('0'>*arg) || ('9'<*arg)){ 4960 goto do_string; 5017 goto do_string; 4961 } 5018 } 4962 { < > 5019 else{ /* try numbers... */ 4963 long const val = strtol(arg, &end, 10); 5020 long const val = strtol(arg, &end, 10); 4964 if(!*end){ 5021 if(!*end){ 4965 return cson_value_new_integer( (cson_int_t)val); 5022 return cson_value_new_integer( (cson_int_t)val); 4966 } 5023 } > 5024 else if( '.' != *end ) { > 5025 goto do_string; 4967 } | 5026 } 4968 { < > 5027 else { 4969 double const val = strtod(arg, &end); | 5028 double const val = strtod(arg, &end); 4970 if(!*end){ | 5029 if(!*end){ 4971 return cson_value_new_double(val); | 5030 return cson_value_new_double(val); 4972 } | 5031 } 4973 } | 5032 } 4974 | 5033 } 4975 < 4976 do_string: 5034 do_string: 4977 return cson_value_new_string(arg, strlen(arg)); 5035 return cson_value_new_string(arg, strlen(arg)); 4978 } 5036 } 4979 5037 4980 5038 4981 int cson_parse_argv_flags( int argc, char const * const * argv, 5039 int cson_parse_argv_flags( int argc, char const * const * argv, 4982 cson_object ** tgt, unsigned int * count ){ 5040 cson_object ** tgt, unsigned int * count ){ ................................................................................................................................................................................ 5003 break; 5061 break; 5004 } 5062 } 5005 if(!*pos){ /** --key */ 5063 if(!*pos){ /** --key */ 5006 v = cson_value_true(); 5064 v = cson_value_true(); 5007 }else{ /** --key=...*/ 5065 }else{ /** --key=...*/ 5008 assert('=' == *pos); 5066 assert('=' == *pos); 5009 ++pos /*skip '='*/; 5067 ++pos /*skip '='*/; 5010 v = *pos < 5011 ? cson_guess_arg_type(pos) | 5068 v = cson_guess_arg_type(pos); 5012 : cson_value_null(); < 5013 } 5069 } 5014 if(0 != (rc=cson_object_set_s(o, k, v))){ 5070 if(0 != (rc=cson_object_set_s(o, k, v))){ 5015 cson_free_string(k); 5071 cson_free_string(k); 5016 cson_value_free(v); 5072 cson_value_free(v); 5017 break; 5073 break; 5018 } 5074 } 5019 else if(count) ++*count; 5075 else if(count) ++*count; ................................................................................................................................................................................ 5571 int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL ); 5627 int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL ); 5572 if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */; 5628 if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */; 5573 rc = cson_sqlite3_stmt_to_json( st, tgt, fat ); 5629 rc = cson_sqlite3_stmt_to_json( st, tgt, fat ); 5574 sqlite3_finalize( st ); 5630 sqlite3_finalize( st ); 5575 return rc; 5631 return rc; 5576 } 5632 } 5577 } 5633 } > 5634 > 5635 int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v ) > 5636 { > 5637 int rc = 0; > 5638 char convertErr = 0; > 5639 if(!st) return cson_rc.ArgError; > 5640 else if( ndx < 1 ) { > 5641 rc = cson_rc.RangeError; > 5642 } > 5643 else if( cson_value_is_array(v) ){ > 5644 cson_array * ar = cson_value_get_array(v); > 5645 unsigned int len = cson_array_length_get(ar); > 5646 unsigned int i; > 5647 assert(NULL != ar); > 5648 for( i = 0; !rc && (i < len); ++i ){ > 5649 rc = cson_sqlite3_bind_value( st, (int)i+ndx, > 5650 cson_array_get(ar, i)); > 5651 } > 5652 } > 5653 else if(!v || cson_value_is_null(v)){ > 5654 rc = sqlite3_bind_null(st,ndx); > 5655 convertErr = 1; > 5656 } > 5657 else if( cson_value_is_double(v) ){ > 5658 rc = sqlite3_bind_double( st, ndx, cson_value_get_double(v) ); > 5659 convertErr = 1; > 5660 } > 5661 else if( cson_value_is_bool(v) ){ > 5662 rc = sqlite3_bind_int( st, ndx, cson_value_get_bool(v) ? 1 : 0 ); > 5663 convertErr = 1; > 5664 } > 5665 else if( cson_value_is_integer(v) ){ > 5666 rc = sqlite3_bind_int64( st, ndx, cson_value_get_integer(v) ); > 5667 convertErr = 1; > 5668 } > 5669 else if( cson_value_is_string(v) ){ > 5670 cson_string const * s = cson_value_get_string(v); > 5671 rc = sqlite3_bind_text( st, ndx, > 5672 cson_string_cstr(s), > 5673 cson_string_length_bytes(s), > 5674 SQLITE_TRANSIENT); > 5675 convertErr = 1; > 5676 } > 5677 else { > 5678 rc = cson_rc.TypeError; > 5679 } > 5680 if(convertErr && rc) switch(rc){ > 5681 case SQLITE_TOOBIG: > 5682 case SQLITE_RANGE: rc = cson_rc.RangeError; break; > 5683 case SQLITE_NOMEM: rc = cson_rc.AllocError; break; > 5684 case SQLITE_IOERR: rc = cson_rc.IOError; break; > 5685 default: rc = cson_rc.UnknownError; break; > 5686 }; > 5687 return rc; > 5688 } > 5689 5578 5690 5579 #if defined(__cplusplus) 5691 #if defined(__cplusplus) 5580 } /*extern "C"*/ 5692 } /*extern "C"*/ 5581 #endif 5693 #endif 5582 #undef MARKER 5694 #undef MARKER 5583 #endif /* CSON_ENABLE_SQLITE3 */ 5695 #endif /* CSON_ENABLE_SQLITE3 */ 5584 /* end file ./cson_sqlite3.c */ 5696 /* end file ./cson_sqlite3.c */ 5585 #endif /* FOSSIL_ENABLE_JSON */ 5697 #endif /* FOSSIL_ENABLE_JSON */
Changes to src/cson_amalgamation.h
72 #define CSON_DOUBLE_T_SFMT "Lf" 72 #define CSON_DOUBLE_T_SFMT "Lf" 73 #define CSON_DOUBLE_T_PFMT "Lf" 73 #define CSON_DOUBLE_T_PFMT "Lf" 74 #else 74 #else 75 typedef double cson_double_t; 75 typedef double cson_double_t; 76 #define CSON_DOUBLE_T_SFMT "f" 76 #define CSON_DOUBLE_T_SFMT "f" 77 #define CSON_DOUBLE_T_PFMT "f" 77 #define CSON_DOUBLE_T_PFMT "f" 78 #endif 78 #endif > 79 > 80 /** @def CSON_VOID_PTR_IS_BIG > 81 > 82 ONLY define this to a true value if you know that > 83 > 84 (sizeof(cson_int_t) <= sizeof(void*)) > 85 > 86 If that is the case, cson does not need to dynamically > 87 allocate integers. However, enabling this may cause > 88 compilation warnings in 32-bit builds even though the code > 89 being warned about cannot ever be called. To get around such > 90 warnings, when building on a 64-bit environment you can define > 91 this to 1 to get "big" integer support. HOWEVER, all clients must > 92 also use the same value for this macro. If i knew a halfway reliable > 93 way to determine this automatically at preprocessor-time, i would > 94 automate this. We might be able to do halfway reliably by looking > 95 for a large INT_MAX value? > 96 */ > 97 #if !defined(CSON_VOID_PTR_IS_BIG) > 98 > 99 /* Largely taken from http://predef.sourceforge.net/prearch.html > 100 > 101 See also: http://poshlib.hookatooka.com/poshlib/trac.cgi/browser/posh.h > 102 */ > 103 # if defined(_WIN64) || defined(__LP64__)/*gcc*/ \ > 104 || defined(_M_X64) || defined(__amd64__) || defined(__amd64) \ > 105 || defined(__x86_64__) || defined(__x86_64) \ > 106 || defined(__ia64__) || defined(__ia64) || defined(_IA64) || defined(__IA64_ > 107 || defined(_M_IA64) \ > 108 || defined(__sparc_v9__) || defined(__sparcv9) || defined(_ADDR64) \ > 109 || defined(__64BIT__) > 110 # define CSON_VOID_PTR_IS_BIG 1 > 111 # else > 112 # define CSON_VOID_PTR_IS_BIG 0 > 113 # endif > 114 #endif 79 115 80 /** @def CSON_INT_T_SFMT 116 /** @def CSON_INT_T_SFMT 81 117 82 scanf()-compatible format token for cson_int_t. 118 scanf()-compatible format token for cson_int_t. 83 */ 119 */ 84 120 85 /** @def CSON_INT_T_PFMT 121 /** @def CSON_INT_T_PFMT ................................................................................................................................................................................ 926 cson_string * cson_value_get_string( cson_value const * val ); 962 cson_string * cson_value_get_string( cson_value const * val ); 927 963 928 /** 964 /** 929 Returns a pointer to the NULL-terminated string bytes of str. 965 Returns a pointer to the NULL-terminated string bytes of str. 930 The bytes are owned by string and will be invalided when it 966 The bytes are owned by string and will be invalided when it 931 is cleaned up. 967 is cleaned up. 932 968 933 If str is NULL then NULL is returned. | 969 If str is NULL then NULL is returned. If the string has a length > 970 of 0 then "" is returned. 934 971 935 @see cson_string_length_bytes() 972 @see cson_string_length_bytes() 936 @see cson_value_get_string() 973 @see cson_value_get_string() 937 */ 974 */ 938 char const * cson_string_cstr( cson_string const * str ); 975 char const * cson_string_cstr( cson_string const * str ); 939 976 940 /** 977 /** ................................................................................................................................................................................ 1259 Equivalent to cson_value_free(cson_array_value(x)). 1296 Equivalent to cson_value_free(cson_array_value(x)). 1260 */ 1297 */ 1261 void cson_free_array(cson_array *x); 1298 void cson_free_array(cson_array *x); 1262 1299 1263 /** 1300 /** 1264 Equivalent to cson_value_free(cson_string_value(x)). 1301 Equivalent to cson_value_free(cson_string_value(x)). 1265 */ 1302 */ 1266 void cson_free_string(cson_string const *x); | 1303 void cson_free_string(cson_string *x); 1267 1304 1268 1305 1269 /** 1306 /** 1270 Allocates a new "array" value and transfers ownership of it to the 1307 Allocates a new "array" value and transfers ownership of it to the 1271 caller. It must eventually be destroyed, by the caller or its 1308 caller. It must eventually be destroyed, by the caller or its 1272 owning container, by passing it to cson_value_free(). 1309 owning container, by passing it to cson_value_free(). 1273 1310 ................................................................................................................................................................................ 2035 int cson_value_refcount_set( cson_value * v, unsigned short rc ); 2072 int cson_value_refcount_set( cson_value * v, unsigned short rc ); 2036 #endif 2073 #endif 2037 2074 2038 /** 2075 /** 2039 Deeply copies a JSON value, be it an object/array or a "plain" 2076 Deeply copies a JSON value, be it an object/array or a "plain" 2040 value (e.g. number/string/boolean). If cv is not NULL then this 2077 value (e.g. number/string/boolean). If cv is not NULL then this 2041 function makes a deep clone of it and returns that clone. Ownership 2078 function makes a deep clone of it and returns that clone. Ownership 2042 of the clone is transfered to the caller, who must eventually free | 2079 of the clone is identical t transfered to the caller, who must 2043 the value using cson_value_free() or add it to a container | 2080 eventually free the value using cson_value_free() or add it to a 2044 object/array to transfer ownership to the container. The returned | 2081 container object/array to transfer ownership to the container. The 2045 object will be of the same logical type as orig. | 2082 returned object will be of the same logical type as orig. 2046 2083 2047 ACHTUNG: if orig contains any cyclic references at any depth level 2084 ACHTUNG: if orig contains any cyclic references at any depth level 2048 this function will endlessly recurse. (Having _any_ cyclic 2085 this function will endlessly recurse. (Having _any_ cyclic 2049 references violates this library's requirements.) 2086 references violates this library's requirements.) 2050 2087 2051 Returns NULL if orig is NULL or if cloning fails. Assuming that 2088 Returns NULL if orig is NULL or if cloning fails. Assuming that 2052 orig is in a valid state, the only "likely" error case is that an 2089 orig is in a valid state, the only "likely" error case is that an 2053 allocation fails while constructing the clone. In other words, if 2090 allocation fails while constructing the clone. In other words, if 2054 cloning fails due to something other than an allocation error then 2091 cloning fails due to something other than an allocation error then 2055 either orig is in an invalid state or there is a bug. 2092 either orig is in an invalid state or there is a bug. > 2093 > 2094 When this function clones Objects or Arrays it shares any immutable > 2095 values (including object keys) between the parent and the > 2096 clone. Mutable values (Objects and Arrays) are copied, however. > 2097 For example, if we clone: > 2098 > 2099 @code > 2100 { a: 1, b: 2, c:["hi"] } > 2101 @endcode > 2102 > 2103 The cloned object and the array "c" would be a new Object/Array > 2104 instances but the object keys (a,b,b) and the values of (a,b), as > 2105 well as the string value within the "c" array, would be shared > 2106 between the original and the clone. The "c" array itself would be > 2107 deeply cloned, such that future changes to the clone are not > 2108 visible to the parent, and vice versa, but immutable values within > 2109 the array are shared (in this case the string "hi"). The > 2110 justification for this heuristic is that immutable values can never > 2111 be changed, so there is no harm in sharing them across > 2112 clones. Additionally, such types can never contribute to cycles in > 2113 a JSON tree, so they are safe to share this way. Objects and > 2114 Arrays, on the other hand, can be modified later and can contribute > 2115 to cycles, and thus the clone needs to be an independent instance. > 2116 Note, however, that if this function directly passed a > 2117 non-Object/Array, that value is deeply cloned. The sharing > 2118 behaviour only applies when traversing Objects/Arrays. 2056 */ 2119 */ 2057 cson_value * cson_value_clone( cson_value const * orig ); 2120 cson_value * cson_value_clone( cson_value const * orig ); 2058 2121 2059 /** 2122 /** 2060 Returns the value handle associated with s. The handle itself owns 2123 Returns the value handle associated with s. The handle itself owns 2061 s, and ownership of the handle is not changed by calling this 2124 s, and ownership of the handle is not changed by calling this 2062 function. If the returned handle is part of a container, calling 2125 function. If the returned handle is part of a container, calling 2063 cson_value_free() on the returned handle invoked undefined 2126 cson_value_free() on the returned handle invoked undefined 2064 behaviour (quite possibly downstream when the container tries to 2127 behaviour (quite possibly downstream when the container tries to 2065 use it). 2128 use it). 2066 2129 2067 This function only returns NULL if s. is NULL. | 2130 This function only returns NULL if s is NULL. The length of the > 2131 returned string is cson_string_length_bytes(). 2068 */ 2132 */ 2069 cson_value * cson_string_value(cson_string const * s); 2133 cson_value * cson_string_value(cson_string const * s); 2070 /** 2134 /** 2071 The Object form of cson_string_value(). See that function 2135 The Object form of cson_string_value(). See that function 2072 for full details. 2136 for full details. 2073 */ 2137 */ 2074 cson_value * cson_object_value(cson_object const * s); 2138 cson_value * cson_object_value(cson_object const * s); ................................................................................................................................................................................ 2077 The Array form of cson_string_value(). See that function 2141 The Array form of cson_string_value(). See that function 2078 for full details. 2142 for full details. 2079 */ 2143 */ 2080 cson_value * cson_array_value(cson_array const * s); 2144 cson_value * cson_array_value(cson_array const * s); 2081 2145 2082 2146 2083 /** 2147 /** 2084 Calculates the in-memory-allocated size of v, recursively if it is | 2148 Calculates the approximate in-memory-allocated size of v, 2085 a container type, with the following caveats and limitations: | 2149 recursively if it is a container type, with the following caveats > 2150 and limitations: 2086 2151 2087 If a given value is reference counted and multiple times within a | 2152 If a given value is reference counted then it is only and multiple 2088 traversed container, each reference is counted at full cost. We | 2153 times within a traversed container, each reference is counted at 2089 have no what of knowing if a given reference has been visited | 2154 full cost. We have no way of knowing if a given reference has been 2090 already and whether it should or should not be counted, so we | 2155 visited already and whether it should or should not be counted, so 2091 pessimistically count them even though the _might_ not really count | 2156 we pessimistically count them even though the _might_ not really 2092 for the given object tree (it depends on where the other open | 2157 count for the given object tree (it depends on where the other open 2093 references live). 2158 references live). 2094 2159 2095 This function returns 0 if any of the following are true: 2160 This function returns 0 if any of the following are true: 2096 2161 2097 - v is NULL 2162 - v is NULL 2098 2163 2099 - v is one of the special singleton values (null, bools, empty 2164 - v is one of the special singleton values (null, bools, empty ................................................................................................................................................................................ 2427 /** 2492 /** 2428 A convenience wrapper around cson_sqlite3_stmt_to_json(), which 2493 A convenience wrapper around cson_sqlite3_stmt_to_json(), which 2429 takes SQL instead of a sqlite3_stmt object. It has the same 2494 takes SQL instead of a sqlite3_stmt object. It has the same 2430 return value and argument semantics as that function. 2495 return value and argument semantics as that function. 2431 */ 2496 */ 2432 int cson_sqlite3_sql_to_json( sqlite3 * db, cson_value ** tgt, char const * sql, 2497 int cson_sqlite3_sql_to_json( sqlite3 * db, cson_value ** tgt, char const * sql, 2433 2498 > 2499 /** > 2500 Binds a JSON value to a 1-based parameter index in a prepared SQL > 2501 statement. v must be NULL or one of one of the types (null, string, > 2502 integer, double, boolean, array). Booleans are bound as integer 0 > 2503 or 1. NULL or null are bound as SQL NULL. Integers are bound as > 2504 64-bit ints. Strings are bound using sqlite3_bind_text() (as > 2505 opposed to text16), but we could/should arguably bind them as > 2506 blobs. > 2507 > 2508 If v is an Array then ndx is is used as a starting position > 2509 (1-based) and each item in the array is bound to the next parameter > 2510 position (starting and ndx, though the array uses 0-based offsets). > 2511 > 2512 TODO: add Object support for named parameters. > 2513 > 2514 Returns 0 on success, non-0 on error. > 2515 */ > 2516 int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v ); > 2517 2434 #if defined(__cplusplus) 2518 #if defined(__cplusplus) 2435 } /*extern "C"*/ 2519 } /*extern "C"*/ 2436 #endif 2520 #endif 2437 2521 2438 #endif /* CSON_ENABLE_SQLITE3 */ 2522 #endif /* CSON_ENABLE_SQLITE3 */ 2439 #endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */ 2523 #endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */ 2440 /* end file include/wh/cson/cson_sqlite3.h */ 2524 /* end file include/wh/cson/cson_sqlite3.h */ 2441 #endif /* FOSSIL_ENABLE_JSON */ 2525 #endif /* FOSSIL_ENABLE_JSON */
Changes to src/db.c
1033 if( g.argc!=3 ){ 1033 if( g.argc!=3 ){ 1034 usage("PATHNAME"); 1034 usage("PATHNAME"); 1035 } 1035 } 1036 if( db_open_local()==0 ){ 1036 if( db_open_local()==0 ){ 1037 fossil_fatal("not in a local checkout"); 1037 fossil_fatal("not in a local checkout"); 1038 return; 1038 return; 1039 } 1039 } 1040 file_canonical_name(g.argv[2], &repo); | 1040 file_canonical_name(g.argv[2], &repo, 0); 1041 zRepo = blob_str(&repo); 1041 zRepo = blob_str(&repo); 1042 if( file_access(zRepo, 0) ){ 1042 if( file_access(zRepo, 0) ){ 1043 fossil_fatal("no such file: %s", zRepo); 1043 fossil_fatal("no such file: %s", zRepo); 1044 } 1044 } 1045 db_open_or_attach(zRepo, "test_repo"); 1045 db_open_or_attach(zRepo, "test_repo"); 1046 db_lset("repository", blob_str(&repo)); 1046 db_lset("repository", blob_str(&repo)); 1047 db_close(1); 1047 db_close(1); ................................................................................................................................................................................ 1696 */ 1696 */ 1697 void db_record_repository_filename(const char *zName){ 1697 void db_record_repository_filename(const char *zName){ 1698 Blob full; 1698 Blob full; 1699 if( zName==0 ){ 1699 if( zName==0 ){ 1700 if( !g.localOpen ) return; 1700 if( !g.localOpen ) return; 1701 zName = db_repository_filename(); 1701 zName = db_repository_filename(); 1702 } 1702 } 1703 file_canonical_name(zName, &full); | 1703 file_canonical_name(zName, &full, 0); 1704 db_swap_connections(); 1704 db_swap_connections(); 1705 db_multi_exec( 1705 db_multi_exec( 1706 "INSERT OR IGNORE INTO global_config(name,value)" 1706 "INSERT OR IGNORE INTO global_config(name,value)" 1707 "VALUES('repo:%q',1)", 1707 "VALUES('repo:%q',1)", 1708 blob_str(&full) 1708 blob_str(&full) 1709 ); 1709 ); 1710 if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){ 1710 if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){ > 1711 Blob localRoot; > 1712 file_canonical_name(g.zLocalRoot, &localRoot, 1); 1711 db_multi_exec( 1713 db_multi_exec( 1712 "REPLACE INTO global_config(name, value)" 1714 "REPLACE INTO global_config(name, value)" 1713 "VALUES('ckout:%q','%q');", 1715 "VALUES('ckout:%q','%q');", 1714 g.zLocalRoot, blob_str(&full) | 1716 blob_str(&localRoot), blob_str(&full) 1715 ); 1717 ); > 1718 blob_reset(&localRoot); 1716 } 1719 } 1717 db_swap_connections(); 1720 db_swap_connections(); 1718 blob_reset(&full); 1721 blob_reset(&full); 1719 } 1722 } 1720 1723 1721 /* 1724 /* 1722 ** COMMAND: open 1725 ** COMMAND: open ................................................................................................................................................................................ 1747 allowNested = find_option("nested",0,0)!=0; 1750 allowNested = find_option("nested",0,0)!=0; 1748 if( g.argc!=3 && g.argc!=4 ){ 1751 if( g.argc!=3 && g.argc!=4 ){ 1749 usage("REPOSITORY-FILENAME ?VERSION?"); 1752 usage("REPOSITORY-FILENAME ?VERSION?"); 1750 } 1753 } 1751 if( !allowNested && db_open_local() ){ 1754 if( !allowNested && db_open_local() ){ 1752 fossil_panic("already within an open tree rooted at %s", g.zLocalRoot); 1755 fossil_panic("already within an open tree rooted at %s", g.zLocalRoot); 1753 } 1756 } 1754 file_canonical_name(g.argv[2], &path); | 1757 file_canonical_name(g.argv[2], &path, 0); 1755 db_open_repository(blob_str(&path)); 1758 db_open_repository(blob_str(&path)); 1756 db_init_database("./_FOSSIL_", zLocalSchema, (char*)0); 1759 db_init_database("./_FOSSIL_", zLocalSchema, (char*)0); 1757 db_delete_on_failure("./_FOSSIL_"); 1760 db_delete_on_failure("./_FOSSIL_"); 1758 db_open_local(); 1761 db_open_local(); 1759 db_lset("repository", g.argv[2]); 1762 db_lset("repository", g.argv[2]); 1760 db_record_repository_filename(blob_str(&path)); 1763 db_record_repository_filename(blob_str(&path)); 1761 vid = db_int(0, "SELECT pid FROM plink y" 1764 vid = db_int(0, "SELECT pid FROM plink y"
Changes to src/diff.c
725 while( nA>0 && fossil_isspace(zA[nA-1]) ){ nA--; } 725 while( nA>0 && fossil_isspace(zA[nA-1]) ){ nA--; } 726 while( nB>0 && fossil_isspace(zB[0]) ){ nB--; zB++; } 726 while( nB>0 && fossil_isspace(zB[0]) ){ nB--; zB++; } 727 while( nB>0 && fossil_isspace(zB[nB-1]) ){ nB--; } 727 while( nB>0 && fossil_isspace(zB[nB-1]) ){ nB--; } 728 if( nA>250 ) nA = 250; 728 if( nA>250 ) nA = 250; 729 if( nB>250 ) nB = 250; 729 if( nB>250 ) nB = 250; 730 avg = (nA+nB)/2; 730 avg = (nA+nB)/2; 731 if( avg==0 ) return 0; 731 if( avg==0 ) return 0; > 732 if( nA==nB && memcmp(zA, zB, nA)==0 ) return 0; 732 memset(aFirst, 0, sizeof(aFirst)); 733 memset(aFirst, 0, sizeof(aFirst)); 733 zA--; zB--; /* Make both zA[] and zB[] 1-indexed */ 734 zA--; zB--; /* Make both zA[] and zB[] 1-indexed */ 734 for(i=nB; i>0; i--){ 735 for(i=nB; i>0; i--){ 735 c = (unsigned char)zB[i]; 736 c = (unsigned char)zB[i]; 736 aNext[i] = aFirst[c]; 737 aNext[i] = aFirst[c]; 737 aFirst[c] = i; 738 aFirst[c] = i; 738 } 739 } ................................................................................................................................................................................ 884 int na, nb; /* Number of lines shown from A and B */ 885 int na, nb; /* Number of lines shown from A and B */ 885 int i, j; /* Loop counters */ 886 int i, j; /* Loop counters */ 886 int m, ma, mb;/* Number of lines to output */ 887 int m, ma, mb;/* Number of lines to output */ 887 int skip; /* Number of lines to skip */ 888 int skip; /* Number of lines to skip */ 888 int nChunk = 0; /* Number of chunks of diff output seen so far */ 889 int nChunk = 0; /* Number of chunks of diff output seen so far */ 889 SbsLine s; /* Output line buffer */ 890 SbsLine s; /* Output line buffer */ 890 891 > 892 memset(&s, 0, sizeof(s)); 891 s.zLine = fossil_malloc( 10*width + 200 ); 893 s.zLine = fossil_malloc( 10*width + 200 ); 892 if( s.zLine==0 ) return; 894 if( s.zLine==0 ) return; 893 s.width = width; 895 s.width = width; 894 s.escHtml = escHtml; 896 s.escHtml = escHtml; 895 s.iStart = -1; 897 s.iStart = -1; 896 s.iStart2 = 0; 898 s.iStart2 = 0; 897 s.iEnd = -1; 899 s.iEnd = -1;
Changes to src/diffcmd.c
17 ** 17 ** 18 ** This file contains code used to implement the "diff" command 18 ** This file contains code used to implement the "diff" command 19 */ 19 */ 20 #include "config.h" 20 #include "config.h" 21 #include "diffcmd.h" 21 #include "diffcmd.h" 22 #include <assert.h> 22 #include <assert.h> 23 23 > 24 /* > 25 ** Use the right null device for the platform. > 26 */ > 27 #if defined(_WIN32) > 28 # define NULL_DEVICE "NUL" > 29 #else > 30 # define NULL_DEVICE "/dev/null" > 31 #endif > 32 24 /* 33 /* 25 ** Print the "Index:" message that patches wants to see at the top of a diff. 34 ** Print the "Index:" message that patches wants to see at the top of a diff. 26 */ 35 */ 27 void diff_print_index(const char *zFile, int diffFlags){ 36 void diff_print_index(const char *zFile, int diffFlags){ 28 if( (diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF))==0 ){ 37 if( (diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF))==0 ){ 29 char *z = mprintf("Index: %s\n%.66c\n", zFile, '='); 38 char *z = mprintf("Index: %s\n%.66c\n", zFile, '='); 30 fossil_print("%s", z); 39 fossil_print("%s", z); ................................................................................................................................................................................ 81 Blob out; /* Diff output text */ 90 Blob out; /* Diff output text */ 82 Blob file2; /* Content of zFile2 */ 91 Blob file2; /* Content of zFile2 */ 83 const char *zName2; /* Name of zFile2 for display */ 92 const char *zName2; /* Name of zFile2 for display */ 84 93 85 /* Read content of zFile2 into memory */ 94 /* Read content of zFile2 into memory */ 86 blob_zero(&file2); 95 blob_zero(&file2); 87 if( file_wd_size(zFile2)<0 ){ 96 if( file_wd_size(zFile2)<0 ){ 88 zName2 = "/dev/null"; | 97 zName2 = NULL_DEVICE; 89 }else{ 98 }else{ 90 if( file_wd_islink(zFile2) ){ 99 if( file_wd_islink(zFile2) ){ 91 blob_read_link(&file2, zFile2); 100 blob_read_link(&file2, zFile2); 92 }else{ 101 }else{ 93 blob_read_from_file(&file2, zFile2); 102 blob_read_from_file(&file2, zFile2); 94 } 103 } 95 zName2 = zName; 104 zName2 = zName; ................................................................................................................................................................................ 288 int srcid = db_column_int(&q, 4); 297 int srcid = db_column_int(&q, 4); 289 int isLink = db_column_int(&q, 5); 298 int isLink = db_column_int(&q, 5); 290 char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); 299 char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); 291 char *zToFree = zFullName; 300 char *zToFree = zFullName; 292 int showDiff = 1; 301 int showDiff = 1; 293 if( isDeleted ){ 302 if( isDeleted ){ 294 fossil_print("DELETED %s\n", zPathname); 303 fossil_print("DELETED %s\n", zPathname); 295 if( !asNewFile ){ showDiff = 0; zFullName = "/dev/null"; } | 304 if( !asNewFile ){ showDiff = 0; zFullName = NULL_DEVICE; } 296 }else if( file_access(zFullName, 0) ){ 305 }else if( file_access(zFullName, 0) ){ 297 fossil_print("MISSING %s\n", zPathname); 306 fossil_print("MISSING %s\n", zPathname); 298 if( !asNewFile ){ showDiff = 0; } 307 if( !asNewFile ){ showDiff = 0; } 299 }else if( isNew ){ 308 }else if( isNew ){ 300 fossil_print("ADDED %s\n", zPathname); 309 fossil_print("ADDED %s\n", zPathname); 301 srcid = 0; 310 srcid = 0; 302 if( !asNewFile ){ showDiff = 0; } 311 if( !asNewFile ){ showDiff = 0; }
Changes to src/doc.c
602 if( blob_size(&logo)==0 ){ 602 if( blob_size(&logo)==0 ){ 603 blob_init(&logo, (char*)aLogo, sizeof(aLogo)); 603 blob_init(&logo, (char*)aLogo, sizeof(aLogo)); 604 } 604 } 605 cgi_set_content_type(zMime); 605 cgi_set_content_type(zMime); 606 cgi_set_content(&logo); 606 cgi_set_content(&logo); 607 g.isConst = 1; 607 g.isConst = 1; 608 } 608 } > 609 > 610 /* > 611 ** The default background image: a 16x16 white GIF > 612 */ > 613 static const unsigned char aBackground[] = { > 614 71, 73, 70, 56, 57, 97, 16, 0, 16, 0, > 615 240, 0, 0, 255, 255, 255, 0, 0, 0, 33, > 616 254, 4, 119, 105, 115, 104, 0, 44, 0, 0, > 617 0, 0, 16, 0, 16, 0, 0, 2, 14, 132, > 618 143, 169, 203, 237, 15, 163, 156, 180, 218, 139, > 619 179, 62, 5, 0, 59, > 620 }; > 621 > 622 > 623 /* > 624 ** WEBPAGE: background > 625 ** > 626 ** Return the background image. > 627 */ > 628 void background_page(void){ > 629 Blob bgimg; > 630 char *zMime; > 631 > 632 zMime = db_get("background-mimetype", "image/gif"); > 633 blob_zero(&bgimg); > 634 db_blob(&bgimg, "SELECT value FROM config WHERE name='background-image'"); > 635 if( blob_size(&bgimg)==0 ){ > 636 blob_init(&bgimg, (char*)aBackground, sizeof(aBackground)); > 637 } > 638 cgi_set_content_type(zMime); > 639 cgi_set_content(&bgimg); > 640 g.isConst = 1; > 641 }
Changes to src/file.c
26 #include <sys/types.h> 26 #include <sys/types.h> 27 #include <sys/stat.h> 27 #include <sys/stat.h> 28 #include <unistd.h> 28 #include <unistd.h> 29 #include <string.h> 29 #include <string.h> 30 #include <errno.h> 30 #include <errno.h> 31 #include "file.h" 31 #include "file.h" 32 32 > 33 /* > 34 ** On Windows, include the Platform SDK header file. > 35 */ > 36 #ifdef _WIN32 > 37 # include <windows.h> > 38 #endif > 39 33 /* 40 /* 34 ** The file status information from the most recent stat() call. 41 ** The file status information from the most recent stat() call. 35 ** 42 ** 36 ** Use _stati64 rather than stat on windows, in order to handle files 43 ** Use _stati64 rather than stat on windows, in order to handle files 37 ** larger than 2GB. 44 ** larger than 2GB. 38 */ 45 */ 39 #if defined(_WIN32) && (defined(__MSVCRT__) || defined(_MSC_VER)) 46 #if defined(_WIN32) && (defined(__MSVCRT__) || defined(_MSC_VER)) ................................................................................................................................................................................ 166 nName = strlen(zLinkFile); 173 nName = strlen(zLinkFile); 167 if( nName>=sizeof(zBuf) ){ 174 if( nName>=sizeof(zBuf) ){ 168 zName = mprintf("%s", zLinkFile); 175 zName = mprintf("%s", zLinkFile); 169 }else{ 176 }else{ 170 zName = zBuf; 177 zName = zBuf; 171 memcpy(zName, zLinkFile, nName+1); 178 memcpy(zName, zLinkFile, nName+1); 172 } 179 } 173 nName = file_simplify_name(zName, nName); | 180 nName = file_simplify_name(zName, nName, 0); 174 for(i=1; i<nName; i++){ 181 for(i=1; i<nName; i++){ 175 if( zName[i]=='/' ){ 182 if( zName[i]=='/' ){ 176 zName[i] = 0; 183 zName[i] = 0; 177 if( file_mkdir(zName, 1) ){ 184 if( file_mkdir(zName, 1) ){ 178 fossil_fatal_recursive("unable to create directory %s", zName); 185 fossil_fatal_recursive("unable to create directory %s", zName); 179 return; 186 return; 180 } 187 } ................................................................................................................................................................................ 257 ** other than a directory. 264 ** other than a directory. 258 */ 265 */ 259 int file_isdir(const char *zFilename){ 266 int file_isdir(const char *zFilename){ 260 int rc; 267 int rc; 261 268 262 if( zFilename ){ 269 if( zFilename ){ 263 char *zFN = mprintf("%s", zFilename); 270 char *zFN = mprintf("%s", zFilename); 264 file_simplify_name(zFN, -1); | 271 file_simplify_name(zFN, -1, 0); 265 rc = getStat(zFN, 0); 272 rc = getStat(zFN, 0); 266 free(zFN); 273 free(zFN); 267 }else{ 274 }else{ 268 rc = getStat(0, 0); 275 rc = getStat(0, 0); 269 } 276 } 270 return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2); 277 return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2); 271 } 278 } ................................................................................................................................................................................ 274 ** Same as file_isdir(), but takes into account symlinks. 281 ** Same as file_isdir(), but takes into account symlinks. 275 */ 282 */ 276 int file_wd_isdir(const char *zFilename){ 283 int file_wd_isdir(const char *zFilename){ 277 int rc; 284 int rc; 278 285 279 if( zFilename ){ 286 if( zFilename ){ 280 char *zFN = mprintf("%s", zFilename); 287 char *zFN = mprintf("%s", zFilename); 281 file_simplify_name(zFN, -1); | 288 file_simplify_name(zFN, -1, 0); 282 rc = getStat(zFN, 1); 289 rc = getStat(zFN, 1); 283 free(zFN); 290 free(zFN); 284 }else{ 291 }else{ 285 rc = getStat(0, 1); 292 rc = getStat(0, 1); 286 } 293 } 287 return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2); 294 return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2); 288 } 295 } ................................................................................................................................................................................ 312 z = mprintf("%s-%s", zBase, zSuffix); 319 z = mprintf("%s-%s", zBase, zSuffix); 313 while( file_size(z)>=0 ){ 320 while( file_size(z)>=0 ){ 314 fossil_free(z); 321 fossil_free(z); 315 z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++); 322 z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++); 316 } 323 } 317 if( relFlag ){ 324 if( relFlag ){ 318 Blob x; 325 Blob x; 319 file_relative_name(z, &x); | 326 file_relative_name(z, &x, 0); 320 fossil_free(z); 327 fossil_free(z); 321 z = blob_str(&x); 328 z = blob_str(&x); 322 } 329 } 323 return z; 330 return z; 324 } 331 } 325 332 326 /* 333 /* ................................................................................................................................................................................ 471 ** 478 ** 472 ** * Convert all \ into / on windows 479 ** * Convert all \ into / on windows 473 ** * removing any trailing and duplicate / 480 ** * removing any trailing and duplicate / 474 ** * removing /./ 481 ** * removing /./ 475 ** * removing /A/../ 482 ** * removing /A/../ 476 ** 483 ** 477 ** Changes are made in-place. Return the new name length. 484 ** Changes are made in-place. Return the new name length. > 485 ** If the slash parameter is non-zero, the trailing slash, if any, > 486 ** is retained. 478 */ 487 */ 479 int file_simplify_name(char *z, int n){ | 488 int file_simplify_name(char *z, int n, int slash){ 480 int i, j; 489 int i, j; 481 if( n<0 ) n = strlen(z); 490 if( n<0 ) n = strlen(z); 482 491 483 /* On windows convert all \ characters to / */ 492 /* On windows convert all \ characters to / */ 484 #if defined(_WIN32) 493 #if defined(_WIN32) 485 for(i=0; i<n; i++){ 494 for(i=0; i<n; i++){ 486 if( z[i]=='\\' ) z[i] = '/'; 495 if( z[i]=='\\' ) z[i] = '/'; 487 } 496 } 488 #endif 497 #endif 489 498 490 /* Removing trailing "/" characters */ 499 /* Removing trailing "/" characters */ > 500 if ( !slash ){ 491 while( n>1 && z[n-1]=='/' ){ n--; } | 501 while( n>1 && z[n-1]=='/' ){ n--; } > 502 } 492 503 493 /* Remove duplicate '/' characters. Except, two // at the beginning 504 /* Remove duplicate '/' characters. Except, two // at the beginning 494 ** of a pathname is allowed since this is important on windows. */ 505 ** of a pathname is allowed since this is important on windows. */ 495 for(i=j=1; i<n; i++){ 506 for(i=j=1; i<n; i++){ 496 z[j++] = z[i]; 507 z[j++] = z[i]; 497 while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++; 508 while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++; 498 } 509 } ................................................................................................................................................................................ 538 */ 549 */ 539 void cmd_test_simplify_name(void){ 550 void cmd_test_simplify_name(void){ 540 int i; 551 int i; 541 char *z; 552 char *z; 542 for(i=2; i<g.argc; i++){ 553 for(i=2; i<g.argc; i++){ 543 z = mprintf("%s", g.argv[i]); 554 z = mprintf("%s", g.argv[i]); 544 fossil_print("[%s] -> ", z); 555 fossil_print("[%s] -> ", z); 545 file_simplify_name(z, -1); | 556 file_simplify_name(z, -1, 0); 546 fossil_print("[%s]\n", z); 557 fossil_print("[%s]\n", z); 547 fossil_free(z); 558 fossil_free(z); 548 } 559 } 549 } 560 } 550 561 551 /* 562 /* 552 ** Get the current working directory. 563 ** Get the current working directory. ................................................................................................................................................................................ 604 615 605 /* 616 /* 606 ** Compute a canonical pathname for a file or directory. 617 ** Compute a canonical pathname for a file or directory. 607 ** Make the name absolute if it is relative. 618 ** Make the name absolute if it is relative. 608 ** Remove redundant / characters 619 ** Remove redundant / characters 609 ** Remove all /./ path elements. 620 ** Remove all /./ path elements. 610 ** Convert /A/../ to just / 621 ** Convert /A/../ to just / > 622 ** If the slash parameter is non-zero, the trailing slash, if any, > 623 ** is retained. 611 */ 624 */ 612 void file_canonical_name(const char *zOrigName, Blob *pOut){ | 625 void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ 613 if( file_is_absolute_path(zOrigName) ){ 626 if( file_is_absolute_path(zOrigName) ){ > 627 #if defined(_WIN32) > 628 char *zOut; > 629 #endif 614 blob_set(pOut, zOrigName); 630 blob_set(pOut, zOrigName); 615 blob_materialize(pOut); 631 blob_materialize(pOut); > 632 #if defined(_WIN32) > 633 /* > 634 ** On Windows, normalize the drive letter to upper case. > 635 */ > 636 zOut = blob_str(pOut); > 637 if( fossil_isalpha(zOut[0]) && zOut[1]==':' ){ > 638 zOut[0] = fossil_toupper(zOut[0]); > 639 } > 640 #endif 616 }else{ 641 }else{ 617 char zPwd[2000]; 642 char zPwd[2000]; 618 file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName)); 643 file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName)); > 644 #if defined(_WIN32) > 645 /* > 646 ** On Windows, normalize the drive letter to upper case. > 647 */ > 648 if( fossil_isalpha(zPwd[0]) && zPwd[1]==':' ){ > 649 zPwd[0] = fossil_toupper(zPwd[0]); > 650 } > 651 #endif 619 blob_zero(pOut); 652 blob_zero(pOut); 620 blob_appendf(pOut, "%//%/", zPwd, zOrigName); 653 blob_appendf(pOut, "%//%/", zPwd, zOrigName); 621 } 654 } 622 blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut))); | 655 blob_resize(pOut, file_simplify_name(blob_buffer(pOut), > 656 blob_size(pOut), slash)); 623 } 657 } 624 658 625 /* 659 /* 626 ** COMMAND: test-canonical-name 660 ** COMMAND: test-canonical-name 627 ** Usage: %fossil test-canonical-name FILENAME... 661 ** Usage: %fossil test-canonical-name FILENAME... 628 ** 662 ** 629 ** Test the operation of the canonical name generator. 663 ** Test the operation of the canonical name generator. ................................................................................................................................................................................ 632 void cmd_test_canonical_name(void){ 666 void cmd_test_canonical_name(void){ 633 int i; 667 int i; 634 Blob x; 668 Blob x; 635 blob_zero(&x); 669 blob_zero(&x); 636 for(i=2; i<g.argc; i++){ 670 for(i=2; i<g.argc; i++){ 637 char zBuf[100]; 671 char zBuf[100]; 638 const char *zName = g.argv[i]; 672 const char *zName = g.argv[i]; 639 file_canonical_name(zName, &x); | 673 file_canonical_name(zName, &x, 0); 640 fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x)); 674 fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x)); 641 blob_reset(&x); 675 blob_reset(&x); 642 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName)); 676 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName)); 643 fossil_print(" file_size = %s\n", zBuf); 677 fossil_print(" file_size = %s\n", zBuf); 644 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName)); 678 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName)); 645 fossil_print(" file_mtime = %s\n", zBuf); 679 fossil_print(" file_mtime = %s\n", zBuf); 646 fossil_print(" file_isfile = %d\n", file_wd_isfile(zName)); 680 fossil_print(" file_isfile = %d\n", file_wd_isfile(zName)); ................................................................................................................................................................................ 686 if( fossil_isalpha(zIn[0]) && zIn[1]==':' ) zIn += 2; 720 if( fossil_isalpha(zIn[0]) && zIn[1]==':' ) zIn += 2; 687 #endif 721 #endif 688 return zIn; 722 return zIn; 689 } 723 } 690 724 691 /* 725 /* 692 ** Compute a pathname for a file or directory that is relative 726 ** Compute a pathname for a file or directory that is relative 693 ** to the current directory. | 727 ** to the current directory. If the slash parameter is non-zero, > 728 ** the trailing slash, if any, is retained. 694 */ 729 */ 695 void file_relative_name(const char *zOrigName, Blob *pOut){ | 730 void file_relative_name(const char *zOrigName, Blob *pOut, int slash){ 696 char *zPath; 731 char *zPath; 697 blob_set(pOut, zOrigName); 732 blob_set(pOut, zOrigName); 698 blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut))); | 733 blob_resize(pOut, file_simplify_name(blob_buffer(pOut), > 734 blob_size(pOut), slash)); 699 zPath = file_without_drive_letter(blob_buffer(pOut)); 735 zPath = file_without_drive_letter(blob_buffer(pOut)); 700 if( zPath[0]=='/' ){ 736 if( zPath[0]=='/' ){ 701 int i, j; 737 int i, j; 702 Blob tmp; 738 Blob tmp; 703 char *zPwd; 739 char *zPwd; 704 char zBuf[2000]; 740 char zBuf[2000]; 705 zPwd = zBuf; 741 zPwd = zBuf; ................................................................................................................................................................................ 751 ** Test the operation of the relative name generator. 787 ** Test the operation of the relative name generator. 752 */ 788 */ 753 void cmd_test_relative_name(void){ 789 void cmd_test_relative_name(void){ 754 int i; 790 int i; 755 Blob x; 791 Blob x; 756 blob_zero(&x); 792 blob_zero(&x); 757 for(i=2; i<g.argc; i++){ 793 for(i=2; i<g.argc; i++){ 758 file_relative_name(g.argv[i], &x); | 794 file_relative_name(g.argv[i], &x, 0); 759 fossil_print("%s\n", blob_buffer(&x)); 795 fossil_print("%s\n", blob_buffer(&x)); 760 blob_reset(&x); 796 blob_reset(&x); 761 } 797 } 762 } 798 } 763 799 764 /* 800 /* 765 ** Compute a pathname for a file relative to the root of the local 801 ** Compute a pathname for a file relative to the root of the local ................................................................................................................................................................................ 766 ** tree. Return TRUE on success. On failure, print and error 802 ** tree. Return TRUE on success. On failure, print and error 767 ** message and quit if the errFatal flag is true. If errFatal is 803 ** message and quit if the errFatal flag is true. If errFatal is 768 ** false, then simply return 0. 804 ** false, then simply return 0. 769 ** 805 ** 770 ** The root of the tree is defined by the g.zLocalRoot variable. 806 ** The root of the tree is defined by the g.zLocalRoot variable. 771 */ 807 */ 772 int file_tree_name(const char *zOrigName, Blob *pOut, int errFatal){ 808 int file_tree_name(const char *zOrigName, Blob *pOut, int errFatal){ > 809 Blob localRoot; 773 int n; | 810 int nLocalRoot; > 811 char *zLocalRoot; 774 Blob full; 812 Blob full; 775 int nFull; 813 int nFull; 776 char *zFull; 814 char *zFull; 777 815 778 blob_zero(pOut); 816 blob_zero(pOut); 779 db_must_be_within_tree(); 817 db_must_be_within_tree(); 780 file_canonical_name(zOrigName, &full); | 818 file_canonical_name(g.zLocalRoot, &localRoot, 1); 781 n = strlen(g.zLocalRoot); | 819 nLocalRoot = blob_size(&localRoot); 782 assert( n>0 && g.zLocalRoot[n-1]=='/' ); | 820 zLocalRoot = blob_buffer(&localRoot); > 821 assert( nLocalRoot>0 && zLocalRoot[nLocalRoot-1]=='/' ); > 822 file_canonical_name(zOrigName, &full, 0); 783 nFull = blob_size(&full); 823 nFull = blob_size(&full); 784 zFull = blob_buffer(&full); 824 zFull = blob_buffer(&full); 785 825 786 /* Special case. zOrigName refers to g.zLocalRoot directory. */ 826 /* Special case. zOrigName refers to g.zLocalRoot directory. */ 787 if( nFull==n-1 && memcmp(g.zLocalRoot, zFull, nFull)==0 ){ | 827 if( nFull==nLocalRoot-1 && memcmp(zLocalRoot, zFull, nFull)==0 ){ 788 blob_append(pOut, ".", 1); 828 blob_append(pOut, ".", 1); > 829 blob_reset(&localRoot); > 830 blob_reset(&full); 789 return 1; 831 return 1; 790 } 832 } 791 833 792 if( nFull<=n || memcmp(g.zLocalRoot, zFull, n) ){ | 834 if( nFull<=nLocalRoot || memcmp(zLocalRoot, zFull, nLocalRoot) ){ > 835 blob_reset(&localRoot); 793 blob_reset(&full); 836 blob_reset(&full); 794 if( errFatal ){ 837 if( errFatal ){ 795 fossil_fatal("file outside of checkout tree: %s", zOrigName); 838 fossil_fatal("file outside of checkout tree: %s", zOrigName); 796 } 839 } 797 return 0; 840 return 0; 798 } 841 } 799 blob_append(pOut, &zFull[n], nFull-n); | 842 blob_append(pOut, &zFull[nLocalRoot], nFull-nLocalRoot); > 843 blob_reset(&localRoot); > 844 blob_reset(&full); 800 return 1; 845 return 1; 801 } 846 } 802 847 803 /* 848 /* 804 ** COMMAND: test-tree-name 849 ** COMMAND: test-tree-name 805 ** 850 ** 806 ** Test the operation of the tree name generator. 851 ** Test the operation of the tree name generator. ................................................................................................................................................................................ 859 } 904 } 860 905 861 /* 906 /* 862 ** Construct a random temporary filename into zBuf[]. 907 ** Construct a random temporary filename into zBuf[]. 863 */ 908 */ 864 void file_tempname(int nBuf, char *zBuf){ 909 void file_tempname(int nBuf, char *zBuf){ 865 static const char *azDirs[] = { 910 static const char *azDirs[] = { > 911 #if defined(_WIN32) > 912 0, /* GetTempPath */ > 913 0, /* TEMP */ > 914 0, /* TMP */ > 915 #else 866 "/var/tmp", 916 "/var/tmp", 867 "/usr/tmp", 917 "/usr/tmp", 868 "/tmp", 918 "/tmp", 869 "/temp", 919 "/temp", > 920 #endif 870 ".", 921 ".", 871 }; 922 }; 872 static const unsigned char zChars[] = 923 static const unsigned char zChars[] = 873 "abcdefghijklmnopqrstuvwxyz" 924 "abcdefghijklmnopqrstuvwxyz" 874 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 925 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 875 "0123456789"; 926 "0123456789"; 876 unsigned int i, j; 927 unsigned int i, j; 877 const char *zDir = "."; 928 const char *zDir = "."; 878 int cnt = 0; 929 int cnt = 0; 879 | 930 > 931 #if defined(_WIN32) > 932 char zTmpPath[MAX_PATH]; > 933 > 934 if( GetTempPath(sizeof(zTmpPath), zTmpPath) ){ > 935 azDirs[0] = zTmpPath; > 936 } > 937 > 938 azDirs[1] = fossil_getenv("TEMP"); > 939 azDirs[2] = fossil_getenv("TMP"); > 940 #endif > 941 > 942 880 for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){ 943 for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){ > 944 if( azDirs[i]==0 ) continue; 881 if( !file_isdir(azDirs[i]) ) continue; 945 if( !file_isdir(azDirs[i]) ) continue; 882 zDir = azDirs[i]; 946 zDir = azDirs[i]; 883 break; 947 break; 884 } 948 } 885 949 886 /* Check that the output buffer is large enough for the temporary file 950 /* Check that the output buffer is large enough for the temporary file 887 ** name. If it is not, return SQLITE_ERROR. 951 ** name. If it is not, return SQLITE_ERROR. ................................................................................................................................................................................ 896 j = (int)strlen(zBuf); 960 j = (int)strlen(zBuf); 897 sqlite3_randomness(15, &zBuf[j]); 961 sqlite3_randomness(15, &zBuf[j]); 898 for(i=0; i<15; i++, j++){ 962 for(i=0; i<15; i++, j++){ 899 zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; 963 zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; 900 } 964 } 901 zBuf[j] = 0; 965 zBuf[j] = 0; 902 }while( file_size(zBuf)>=0 ); 966 }while( file_size(zBuf)>=0 ); > 967 > 968 #if defined(_WIN32) > 969 fossil_mbcs_free((char *)azDirs[1]); > 970 fossil_mbcs_free((char *)azDirs[2]); > 971 #endif 903 } 972 } 904 973 905 974 906 /* 975 /* 907 ** Return true if a file named zName exists and has identical content 976 ** Return true if a file named zName exists and has identical content 908 ** to the blob pContent. If zName does not exist or if the content is 977 ** to the blob pContent. If zName does not exist or if the content is 909 ** different in any way, then return false. 978 ** different in any way, then return false. ................................................................................................................................................................................ 928 997 929 998 930 /************************************************************************** 999 /************************************************************************** 931 ** The following routines translate between MBCS and UTF8 on windows. 1000 ** The following routines translate between MBCS and UTF8 on windows. 932 ** Since everything is always UTF8 on unix, these routines are no-ops 1001 ** Since everything is always UTF8 on unix, these routines are no-ops 933 ** there. 1002 ** there. 934 */ 1003 */ 935 #ifdef _WIN32 < 936 # include <windows.h> < 937 #endif < 938 1004 939 /* 1005 /* 940 ** Translate MBCS to UTF8. Return a pointer to the translated text. 1006 ** Translate MBCS to UTF8. Return a pointer to the translated text. 941 ** Call fossil_mbcs_free() to deallocate any memory used to store the 1007 ** Call fossil_mbcs_free() to deallocate any memory used to store the 942 ** returned pointer when done. 1008 ** returned pointer when done. 943 */ 1009 */ 944 char *fossil_mbcs_to_utf8(const char *zMbcs){ 1010 char *fossil_mbcs_to_utf8(const char *zMbcs){
Changes to src/json.c
488 } 488 } 489 489 490 490 491 /* 491 /* 492 ** Wrapper around json_getenv() which tries to evaluate a payload/env 492 ** Wrapper around json_getenv() which tries to evaluate a payload/env 493 ** value as a boolean. Uses mostly the same logic as 493 ** value as a boolean. Uses mostly the same logic as 494 ** json_getenv_int(), with the addition that string values which 494 ** json_getenv_int(), with the addition that string values which 495 ** either start with a digit 1..9 or the letters [tT] are considered | 495 ** either start with a digit 1..9 or the letters [tTyY] are considered 496 ** to be true. If this function cannot find a matching key/value then 496 ** to be true. If this function cannot find a matching key/value then 497 ** dflt is returned. e.g. if it finds the key but the value is-a 497 ** dflt is returned. e.g. if it finds the key but the value is-a 498 ** Object then dftl is returned. 498 ** Object then dftl is returned. 499 ** 499 ** 500 ** If an entry is found, this function guarantees that it will return 500 ** If an entry is found, this function guarantees that it will return 501 ** either 0 or 1, as opposed to "0 or non-zero", so that clients can 501 ** either 0 or 1, as opposed to "0 or non-zero", so that clients can 502 ** pass a different value as dflt. Thus they can use, e.g. -1 to know 502 ** pass a different value as dflt. Thus they can use, e.g. -1 to know ................................................................................................................................................................................ 510 }else if( cson_value_is_number(v) ){ 510 }else if( cson_value_is_number(v) ){ 511 return cson_value_get_integer(v) ? 1 : 0; 511 return cson_value_get_integer(v) ? 1 : 0; 512 }else if( cson_value_is_string(v) ){ 512 }else if( cson_value_is_string(v) ){ 513 char const * sv = cson_string_cstr(cson_value_get_string(v)); 513 char const * sv = cson_string_cstr(cson_value_get_string(v)); 514 if(!*sv || ('0'==*sv)){ 514 if(!*sv || ('0'==*sv)){ 515 return 0; 515 return 0; 516 }else{ 516 }else{ > 517 return ((('1'<=*sv) && ('9'>=*sv)) 517 return (('t'==*sv) || ('T'==*sv) | 518 || ('t'==*sv) || ('T'==*sv) 518 || (('1'<=*sv) && ('9'>=*sv))) < > 519 || ('y'==*sv) || ('Y'==*sv) > 520 ) 519 ? 1 : 0; 521 ? 1 : 0; 520 } 522 } 521 }else if( cson_value_is_bool(v) ){ 523 }else if( cson_value_is_bool(v) ){ 522 return cson_value_get_bool(v) ? 1 : 0; 524 return cson_value_get_bool(v) ? 1 : 0; 523 }else if( cson_value_is_null(v) ){ 525 }else if( cson_value_is_null(v) ){ 524 return 0; 526 return 0; 525 }else{ 527 }else{ ................................................................................................................................................................................ 539 } 541 } 540 542 541 /* 543 /* 542 ** An extended form of find_option() which tries to look up a combo 544 ** An extended form of find_option() which tries to look up a combo 543 ** GET/POST/CLI argument. 545 ** GET/POST/CLI argument. 544 ** 546 ** 545 ** zKey must be the GET/POST parameter key. zCLILong must be the "long 547 ** zKey must be the GET/POST parameter key. zCLILong must be the "long 546 ** form" CLI flag (NULL means to use zKey). zCLIShort may be NULL or | 548 s** form" CLI flag (NULL means to use zKey). zCLIShort may be NULL or 547 ** the "short form" CLI flag (if NULL, no short form is used). 549 ** the "short form" CLI flag (if NULL, no short form is used). 548 ** 550 ** 549 ** If argPos is >=0 and no other match is found, 551 ** If argPos is >=0 and no other match is found, 550 ** json_command_arg(argPos) is also checked. 552 ** json_command_arg(argPos) is also checked. 551 ** 553 ** 552 ** On error (no match found) NULL is returned. 554 ** On error (no match found) NULL is returned. 553 ** 555 ** ................................................................................................................................................................................ 562 assert(NULL != zKey); 564 assert(NULL != zKey); 563 if(!g.isHTTP){ 565 if(!g.isHTTP){ 564 rc = find_option(zCLILong ? zCLILong : zKey, 566 rc = find_option(zCLILong ? zCLILong : zKey, 565 zCLIShort, 1); 567 zCLIShort, 1); 566 } 568 } 567 if(!rc && fossil_has_json()){ 569 if(!rc && fossil_has_json()){ 568 rc = json_getenv_cstr(zKey); 570 rc = json_getenv_cstr(zKey); > 571 if(!rc && zCLIShort){ > 572 rc = cson_value_get_cstr( cson_object_get( g.json.param.o, zCLIShort) ); > 573 } 569 } 574 } 570 if(!rc && (argPos>=0)){ 575 if(!rc && (argPos>=0)){ 571 rc = json_command_arg((unsigned char)argPos); 576 rc = json_command_arg((unsigned char)argPos); 572 } 577 } 573 return rc; 578 return rc; 574 } 579 } 575 580 576 /* 581 /* 577 ** Short-hand form of json_find_option_cstr(zKey,zCLILong,zCLIShort,-1). | 582 ** Short-hand form of json_find_option_cstr2(zKey,zCLILong,zCLIShort,-1). 578 */ 583 */ 579 char const * json_find_option_cstr(char const * zKey, 584 char const * json_find_option_cstr(char const * zKey, 580 char const * zCLILong, 585 char const * zCLILong, 581 char const * zCLIShort){ 586 char const * zCLIShort){ 582 return json_find_option_cstr2(zKey, zCLILong, zCLIShort, -1); 587 return json_find_option_cstr2(zKey, zCLILong, zCLIShort, -1); 583 } 588 } 584 589 ................................................................................................................................................................................ 600 if((-1==rc) && fossil_has_json()){ 605 if((-1==rc) && fossil_has_json()){ 601 rc = json_getenv_bool(zKey,-1); 606 rc = json_getenv_bool(zKey,-1); 602 } 607 } 603 return (-1==rc) ? dflt : rc; 608 return (-1==rc) ? dflt : rc; 604 } 609 } 605 610 606 /* 611 /* 607 ** The integer equivalent of json_find_option_cstr(). | 612 ** The integer equivalent of json_find_option_cstr2(). 608 ** If the option is not found, dftl is returned. 613 ** If the option is not found, dftl is returned. 609 */ 614 */ 610 int json_find_option_int(char const * zKey, 615 int json_find_option_int(char const * zKey, 611 char const * zCLILong, 616 char const * zCLILong, 612 char const * zCLIShort, 617 char const * zCLIShort, 613 int dflt ){ 618 int dflt ){ 614 enum { Magic = -947 }; | 619 enum { Magic = -1947854832 }; 615 int rc = Magic; 620 int rc = Magic; 616 if(!g.isHTTP){ 621 if(!g.isHTTP){ 617 /* FIXME: use strtol() for better error/dflt handling. */ 622 /* FIXME: use strtol() for better error/dflt handling. */ 618 char const * opt = find_option(zCLILong ? zCLILong : zKey, 623 char const * opt = find_option(zCLILong ? zCLILong : zKey, 619 zCLIShort, 1); 624 zCLIShort, 1); 620 if(NULL!=opt){ 625 if(NULL!=opt){ 621 rc = atoi(opt); 626 rc = atoi(opt); ................................................................................................................................................................................ 776 } 781 } 777 } 782 } 778 } 783 } 779 return g.json.authToken; 784 return g.json.authToken; 780 } 785 } 781 786 782 /* 787 /* 783 ** IFF json.reqPayload.o is not NULL then this returns | 788 ** If g.json.reqPayload.o is NULL then NULL is returned, else the 784 ** cson_object_get(json.reqPayload.o,pKey), else it returns NULL. | 789 ** given property is searched for in the request payload. If found it 785 ** < 786 ** The returned value is owned by (or shared with) json.reqPayload.v. | 790 ** is returned. The returned value is owned by (or shares ownership > 791 ** with) g.json, and must NOT be cson_value_free()'d by the > 792 ** caller. 787 */ 793 */ 788 cson_value * json_req_payload_get(char const *pKey){ 794 cson_value * json_req_payload_get(char const *pKey){ 789 return g.json.reqPayload.o 795 return g.json.reqPayload.o 790 ? cson_object_get(g.json.reqPayload.o,pKey) 796 ? cson_object_get(g.json.reqPayload.o,pKey) 791 : NULL; 797 : NULL; 792 } 798 } 793 799 ................................................................................................................................................................................ 977 /* 983 /* 978 ** Wrapper around json_string_split(), taking the same first 3 984 ** Wrapper around json_string_split(), taking the same first 3 979 ** parameters as this function, but returns the results as a JSON 985 ** parameters as this function, but returns the results as a JSON 980 ** Array (if splitting produced tokens) or NULL (if splitting failed 986 ** Array (if splitting produced tokens) or NULL (if splitting failed 981 ** in any way or produced no tokens). 987 ** in any way or produced no tokens). 982 ** 988 ** 983 ** The returned value is owned by the caller. If not NULL then it 989 ** The returned value is owned by the caller. If not NULL then it 984 ** _will_ have a JSON type of Array or Null. | 990 ** _will_ have a JSON type of Array. 985 */ 991 */ 986 cson_value * json_string_split2( char const * zStr, 992 cson_value * json_string_split2( char const * zStr, 987 char separator, 993 char separator, 988 char doDeHttp ){ 994 char doDeHttp ){ 989 cson_value * v = cson_value_new_array(); 995 cson_value * v = cson_value_new_array(); 990 cson_array * a = cson_value_get_array(v); 996 cson_array * a = cson_value_get_array(v); 991 int rc = json_string_split( zStr, separator, doDeHttp, a ); 997 int rc = json_string_split( zStr, separator, doDeHttp, a ); ................................................................................................................................................................................ 1164 if( g.isHTTP ){ 1170 if( g.isHTTP ){ 1165 json_auth_token()/* will copy our auth token, if any, to fossil's 1171 json_auth_token()/* will copy our auth token, if any, to fossil's 1166 core, which we need before we call 1172 core, which we need before we call 1167 login_check_credentials(). */; 1173 login_check_credentials(). */; 1168 login_check_credentials()/* populates g.perm */; 1174 login_check_credentials()/* populates g.perm */; 1169 } 1175 } 1170 else{ 1176 else{ > 1177 /* FIXME: we need an option which allows us to skip this. At least > 1178 one known command (/json/version) does not need an opened > 1179 repo. The problem here is we cannot know which functions need > 1180 it from here (because command dispatching hasn't yet happened) > 1181 and all other commands rely on the repo being opened before > 1182 they are called. A textbook example of lack of foresight :/. > 1183 */ 1171 db_find_and_open_repository(OPEN_ANY_SCHEMA,0); 1184 db_find_and_open_repository(OPEN_ANY_SCHEMA,0); 1172 } 1185 } 1173 } 1186 } 1174 1187 1175 /* 1188 /* 1176 ** Returns the ndx'th item in the "command path", where index 0 is the 1189 ** Returns the ndx'th item in the "command path", where index 0 is the 1177 ** position of the "json" part of the path. Returns NULL if ndx is out 1190 ** position of the "json" part of the path. Returns NULL if ndx is out ................................................................................................................................................................................ 1216 if(g.json.cmd.offset < 0){ 1229 if(g.json.cmd.offset < 0){ 1217 return NULL; 1230 return NULL; 1218 }else{ 1231 }else{ 1219 ndx = g.json.cmd.offset + ndx; 1232 ndx = g.json.cmd.offset + ndx; 1220 return cson_string_cstr(cson_value_get_string(cson_array_get( ar, g.json.cmd 1233 return cson_string_cstr(cson_value_get_string(cson_array_get( ar, g.json.cmd 1221 } 1234 } 1222 } 1235 } 1223 < 1224 /* < 1225 ** If g.json.reqPayload.o is NULL then NULL is returned, else the < 1226 ** given property is searched for in the request payload. If found it < 1227 ** is returned. The returned value is owned by (or shares ownership < 1228 ** with) g.json, and must NOT be cson_value_free()'d by the < 1229 ** caller. < 1230 */ < 1231 cson_value * json_payload_property( char const * key ){ < 1232 return g.json.reqPayload.o ? < 1233 cson_object_get( g.json.reqPayload.o, key ) < 1234 : NULL; < 1235 } < 1236 < 1237 1236 1238 /* Returns the C-string form of json_auth_token(), or NULL 1237 /* Returns the C-string form of json_auth_token(), or NULL 1239 ** if json_auth_token() returns NULL. 1238 ** if json_auth_token() returns NULL. 1240 */ 1239 */ 1241 char const * json_auth_token_cstr(){ 1240 char const * json_auth_token_cstr(){ 1242 return cson_value_get_cstr( json_auth_token() ); 1241 return cson_value_get_cstr( json_auth_token() ); 1243 } 1242 } ................................................................................................................................................................................ 1332 cson_value * rc = NULL; 1331 cson_value * rc = NULL; 1333 Blob path = empty_blob; 1332 Blob path = empty_blob; 1334 unsigned int aLen = g.json.dispatchDepth+1; /*cson_array_length_get(g.json.c 1333 unsigned int aLen = g.json.dispatchDepth+1; /*cson_array_length_get(g.json.c 1335 unsigned int i = 1; 1334 unsigned int i = 1; 1336 for( ; i < aLen; ++i ){ 1335 for( ; i < aLen; ++i ){ 1337 char const * part = cson_string_cstr(cson_value_get_string(cson_array_get( 1336 char const * part = cson_string_cstr(cson_value_get_string(cson_array_get( 1338 if(!part){ 1337 if(!part){ > 1338 #if 1 1339 fossil_warning("Iterating further than expected in %s.", | 1339 fossil_warning("Iterating further than expected in %s.", 1340 __FILE__); 1340 __FILE__); > 1341 #endif 1341 break; | 1342 break; 1342 } 1343 } 1343 blob_appendf(&path,"%s%s", (i>1 ? "/": ""), part); 1344 blob_appendf(&path,"%s%s", (i>1 ? "/": ""), part); 1344 } 1345 } 1345 rc = json_new_string((blob_size(&path)>0) 1346 rc = json_new_string((blob_size(&path)>0) 1346 ? blob_buffer(&path) 1347 ? blob_buffer(&path) 1347 : "") 1348 : "") 1348 /* reminder; we need an empty string instead of NULL 1349 /* reminder; we need an empty string instead of NULL ................................................................................................................................................................................ 1473 ** pMsg is an optional message string property (resultText) of the 1474 ** pMsg is an optional message string property (resultText) of the 1474 ** response. If resultCode is non-0 and pMsg is NULL then 1475 ** response. If resultCode is non-0 and pMsg is NULL then 1475 ** json_err_cstr() is used to get the error string. The caller may 1476 ** json_err_cstr() is used to get the error string. The caller may 1476 ** provide his own or may use an empty string to suppress the 1477 ** provide his own or may use an empty string to suppress the 1477 ** resultText property. 1478 ** resultText property. 1478 ** 1479 ** 1479 */ 1480 */ 1480 cson_value * json_create_response( int resultCode, | 1481 static cson_value * json_create_response( int resultCode, 1481 char const * pMsg, | 1482 char const * pMsg, 1482 cson_value * payload){ | 1483 cson_value * payload){ 1483 cson_value * v = NULL; 1484 cson_value * v = NULL; 1484 cson_value * tmp = NULL; 1485 cson_value * tmp = NULL; 1485 cson_object * o = NULL; 1486 cson_object * o = NULL; 1486 int rc; 1487 int rc; 1487 resultCode = json_dumbdown_rc(resultCode ? resultCode : g.json.resultCode); 1488 resultCode = json_dumbdown_rc(resultCode ? resultCode : g.json.resultCode); 1488 o = cson_new_object(); 1489 o = cson_new_object(); 1489 v = cson_object_value(o); 1490 v = cson_object_value(o); ................................................................................................................................................................................ 1740 } 1741 } 1741 row = cson_sqlite3_row_to_array(pStmt->pStmt); 1742 row = cson_sqlite3_row_to_array(pStmt->pStmt); 1742 cson_array_append(a, row); 1743 cson_array_append(a, row); 1743 } 1744 } 1744 return cson_array_value(a); 1745 return cson_array_value(a); 1745 } 1746 } 1746 1747 > 1748 cson_value * json_stmt_to_array_of_values(Stmt *pStmt, > 1749 int resultColumn, > 1750 cson_array * pTgt){ > 1751 cson_array * a = pTgt; > 1752 while( (SQLITE_ROW==db_step(pStmt)) ){ > 1753 cson_value * row = cson_sqlite3_column_to_value(pStmt->pStmt, > 1754 resultColumn); > 1755 if(row){ > 1756 if(!a){ > 1757 a = cson_new_array(); > 1758 assert(NULL!=a); > 1759 } > 1760 cson_array_append(a, row); > 1761 } > 1762 } > 1763 return cson_array_value(a); > 1764 } 1747 1765 1748 /* 1766 /* 1749 ** Executes the given SQL and runs it through 1767 ** Executes the given SQL and runs it through 1750 ** json_stmt_to_array_of_obj(), returning the result of that 1768 ** json_stmt_to_array_of_obj(), returning the result of that 1751 ** function. If resetBlob is true then blob_reset(pSql) is called 1769 ** function. If resetBlob is true then blob_reset(pSql) is called 1752 ** after preparing the query. 1770 ** after preparing the query. 1753 ** 1771 ** 1754 ** pTgt has the same semantics as described for 1772 ** pTgt has the same semantics as described for 1755 ** json_stmt_to_array_of_obj(). 1773 ** json_stmt_to_array_of_obj(). > 1774 ** > 1775 ** FIXME: change this to take a (char const *) instead of a blob, > 1776 ** to simplify the trivial use-cases (which don't need a Blob). 1756 */ 1777 */ 1757 cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt, 1778 cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt, 1758 char resetBlob){ 1779 char resetBlob){ 1759 Stmt q = empty_Stmt; 1780 Stmt q = empty_Stmt; 1760 cson_value * pay = NULL; 1781 cson_value * pay = NULL; 1761 assert( blob_size(pSql) > 0 ); 1782 assert( blob_size(pSql) > 0 ); 1762 db_prepare(&q, "%s", blob_str(pSql)); 1783 db_prepare(&q, "%s", blob_str(pSql)); ................................................................................................................................................................................ 1772 /* 1793 /* 1773 ** If the given COMMIT rid has any tags associated with it, this 1794 ** If the given COMMIT rid has any tags associated with it, this 1774 ** function returns a JSON Array containing the tag names (owned by 1795 ** function returns a JSON Array containing the tag names (owned by 1775 ** the caller), else it returns NULL. 1796 ** the caller), else it returns NULL. 1776 ** 1797 ** 1777 ** See info_tags_of_checkin() for more details (this is simply a JSON 1798 ** See info_tags_of_checkin() for more details (this is simply a JSON 1778 ** wrapper for that function). 1799 ** wrapper for that function). > 1800 ** > 1801 ** If there are no tags then this function returns NULL, not an empty > 1802 ** Array. 1779 */ 1803 */ 1780 cson_value * json_tags_for_checkin_rid(int rid, char propagatingOnly){ 1804 cson_value * json_tags_for_checkin_rid(int rid, char propagatingOnly){ 1781 cson_value * v = NULL; 1805 cson_value * v = NULL; 1782 char * tags = info_tags_of_checkin(rid, propagatingOnly); 1806 char * tags = info_tags_of_checkin(rid, propagatingOnly); 1783 if(tags){ 1807 if(tags){ 1784 if(*tags){ 1808 if(*tags){ 1785 v = json_string_split2(tags,',',0); 1809 v = json_string_split2(tags,',',0); ................................................................................................................................................................................ 1811 cson_array * list = cson_value_get_array(listV); 1835 cson_array * list = cson_value_get_array(listV); 1812 cson_value * objV = NULL; 1836 cson_value * objV = NULL; 1813 cson_object * obj = NULL; 1837 cson_object * obj = NULL; 1814 cson_string * kRC; 1838 cson_string * kRC; 1815 cson_string * kSymbol; 1839 cson_string * kSymbol; 1816 cson_string * kNumber; 1840 cson_string * kNumber; 1817 cson_string * kDesc; 1841 cson_string * kDesc; 1818 int rc = cson_array_reserve( list, 35 ); | 1842 cson_array_reserve( list, 35 ); 1819 if(rc){ < 1820 assert( 0 && "Allocation error."); < 1821 exit(1); < 1822 } < 1823 kRC = cson_new_string("resultCode",10); 1843 kRC = cson_new_string("resultCode",10); 1824 kSymbol = cson_new_string("cSymbol",7); 1844 kSymbol = cson_new_string("cSymbol",7); 1825 kNumber = cson_new_string("number",6); 1845 kNumber = cson_new_string("number",6); 1826 kDesc = cson_new_string("description",11); 1846 kDesc = cson_new_string("description",11); 1827 #define C(K) objV = cson_value_new_object(); obj = cson_value_get_object(objV); 1847 #define C(K) objV = cson_value_new_object(); obj = cson_value_get_object(objV); 1828 cson_object_set_s(obj, kRC, json_new_string(json_rc_cstr(FSL_JSON_E_##K)) ); 1848 cson_object_set_s(obj, kRC, json_new_string(json_rc_cstr(FSL_JSON_E_##K)) ); 1829 cson_object_set_s(obj, kSymbol, json_new_string("FSL_JSON_E_"#K) ); 1849 cson_object_set_s(obj, kSymbol, json_new_string("FSL_JSON_E_"#K) ); ................................................................................................................................................................................ 1886 jobj = cson_value_get_object(jval); 1906 jobj = cson_value_get_object(jval); 1887 #define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X))) 1907 #define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X))) 1888 FSET(MANIFEST_UUID,"manifestUuid"); 1908 FSET(MANIFEST_UUID,"manifestUuid"); 1889 FSET(MANIFEST_VERSION,"manifestVersion"); 1909 FSET(MANIFEST_VERSION,"manifestVersion"); 1890 FSET(MANIFEST_DATE,"manifestDate"); 1910 FSET(MANIFEST_DATE,"manifestDate"); 1891 FSET(MANIFEST_YEAR,"manifestYear"); 1911 FSET(MANIFEST_YEAR,"manifestYear"); 1892 FSET(RELEASE_VERSION,"releaseVersion"); 1912 FSET(RELEASE_VERSION,"releaseVersion"); 1893 #undef FSET < 1894 cson_object_set( jobj, "releaseVersionNumber", 1913 cson_object_set( jobj, "releaseVersionNumber", 1895 cson_value_new_integer(RELEASE_VERSION_NUMBER) ); 1914 cson_value_new_integer(RELEASE_VERSION_NUMBER) ); 1896 cson_object_set( jobj, "resultCodeParanoiaLevel", 1915 cson_object_set( jobj, "resultCodeParanoiaLevel", 1897 cson_value_new_integer(g.json.errorDetailParanoia) ); 1916 cson_value_new_integer(g.json.errorDetailParanoia) ); > 1917 /*FSET(FOSSIL_JSON_API_VERSION, "jsonApiVersion" );*/ > 1918 #undef FSET 1898 return jval; 1919 return jval; 1899 } 1920 } 1900 1921 1901 1922 1902 /* 1923 /* 1903 ** Returns the current user's capabilities string as a String value. 1924 ** Returns the current user's capabilities string as a String value. 1904 ** Returned value is owned by the caller, and will only be NULL if 1925 ** Returned value is owned by the caller, and will only be NULL if ................................................................................................................................................................................ 2111 if((zPages+1)->name){ 2132 if((zPages+1)->name){ 2112 blob_append(pOut, ", ",2); 2133 blob_append(pOut, ", ",2); 2113 } 2134 } 2114 } 2135 } 2115 return i; 2136 return i; 2116 } 2137 } 2117 2138 > 2139 /* > 2140 ** Creates an error message using zErrPrefix and the given array of > 2141 ** JSON command definitions, and sets the g.json error state to > 2142 ** reflect FSL_JSON_E_MISSING_ARGS. If zErrPrefix is NULL then > 2143 ** some default is used (e.g. "Try one of: "). If it is "" then > 2144 ** no prefix is used. > 2145 ** > 2146 ** The intention is to provide the user (via the response.resultText) > 2147 ** a list of available commands/subcommands. > 2148 ** > 2149 */ > 2150 void json_dispatch_missing_args_err( JsonPageDef const * pCommands, > 2151 char const * zErrPrefix ){ > 2152 Blob cmdNames = empty_blob; > 2153 blob_init(&cmdNames,NULL,0); > 2154 if( !zErrPrefix ) { > 2155 zErrPrefix = "Try one of: "; > 2156 } > 2157 blob_append( &cmdNames, zErrPrefix, strlen(zErrPrefix) ); > 2158 json_pagedefs_to_string(pCommands, &cmdNames); > 2159 json_set_err(FSL_JSON_E_MISSING_ARGS, "%s", > 2160 blob_str(&cmdNames)); > 2161 blob_reset(&cmdNames); > 2162 } 2118 2163 2119 cson_value * json_page_dispatch_helper(JsonPageDef const * pages){ 2164 cson_value * json_page_dispatch_helper(JsonPageDef const * pages){ 2120 JsonPageDef const * def; 2165 JsonPageDef const * def; 2121 char const * cmd = json_command_arg(1+g.json.dispatchDepth); 2166 char const * cmd = json_command_arg(1+g.json.dispatchDepth); 2122 assert( NULL != pages ); 2167 assert( NULL != pages ); 2123 if( ! cmd ){ 2168 if( ! cmd ){ 2124 Blob cmdNames = empty_blob; | 2169 json_dispatch_missing_args_err(pages, 2125 json_pagedefs_to_string(pages, &cmdNames); < 2126 json_set_err(FSL_JSON_E_MISSING_ARGS, < 2127 "No subcommand specified. Try one of (%s).", | 2170 "No subcommand specified. " 2128 blob_str(&cmdNames)); < 2129 blob_reset(&cmdNames); < > 2171 "Try one of: "); 2130 return NULL; 2172 return NULL; 2131 } 2173 } 2132 def = json_handler_for_name( cmd, pages ); 2174 def = json_handler_for_name( cmd, pages ); 2133 if(!def){ 2175 if(!def){ 2134 json_set_err(FSL_JSON_E_UNKNOWN_COMMAND, 2176 json_set_err(FSL_JSON_E_UNKNOWN_COMMAND, 2135 "Unknown subcommand: %s", cmd); 2177 "Unknown subcommand: %s", cmd); 2136 return NULL; 2178 return NULL; ................................................................................................................................................................................ 2184 cson_value * json_page_anon_password(); 2226 cson_value * json_page_anon_password(); 2185 /* Impl in json_artifact.c. */ 2227 /* Impl in json_artifact.c. */ 2186 cson_value * json_page_artifact(); 2228 cson_value * json_page_artifact(); 2187 /* Impl in json_branch.c. */ 2229 /* Impl in json_branch.c. */ 2188 cson_value * json_page_branch(); 2230 cson_value * json_page_branch(); 2189 /* Impl in json_diff.c. */ 2231 /* Impl in json_diff.c. */ 2190 cson_value * json_page_diff(); 2232 cson_value * json_page_diff(); > 2233 /* Impl in json_dir.c. */ > 2234 cson_value * json_page_dir(); 2191 /* Impl in json_login.c. */ 2235 /* Impl in json_login.c. */ 2192 cson_value * json_page_login(); 2236 cson_value * json_page_login(); 2193 /* Impl in json_login.c. */ 2237 /* Impl in json_login.c. */ 2194 cson_value * json_page_logout(); 2238 cson_value * json_page_logout(); 2195 /* Impl in json_query.c. */ 2239 /* Impl in json_query.c. */ 2196 cson_value * json_page_query(); 2240 cson_value * json_page_query(); 2197 /* Impl in json_report.c. */ 2241 /* Impl in json_report.c. */ ................................................................................................................................................................................ 2198 cson_value * json_page_report(); 2242 cson_value * json_page_report(); 2199 /* Impl in json_tag.c. */ 2243 /* Impl in json_tag.c. */ 2200 cson_value * json_page_tag(); 2244 cson_value * json_page_tag(); 2201 /* Impl in json_user.c. */ 2245 /* Impl in json_user.c. */ 2202 cson_value * json_page_user(); 2246 cson_value * json_page_user(); 2203 /* Impl in json_config.c. */ 2247 /* Impl in json_config.c. */ 2204 cson_value * json_page_config(); 2248 cson_value * json_page_config(); > 2249 /* Impl in json_finfo.c. */ > 2250 cson_value * json_page_finfo(); > 2251 2205 /* 2252 /* 2206 ** Mapping of names to JSON pages/commands. Each name is a subpath of 2253 ** Mapping of names to JSON pages/commands. Each name is a subpath of 2207 ** /json (in CGI mode) or a subcommand of the json command in CLI mode 2254 ** /json (in CGI mode) or a subcommand of the json command in CLI mode 2208 */ 2255 */ 2209 static const JsonPageDef JsonPageDefs[] = { 2256 static const JsonPageDef JsonPageDefs[] = { 2210 /* please keep alphabetically sorted (case-insensitive) for maintenance reasons. 2257 /* please keep alphabetically sorted (case-insensitive) for maintenance reasons. 2211 {"anonymousPassword", json_page_anon_password, 0}, 2258 {"anonymousPassword", json_page_anon_password, 0}, 2212 {"artifact", json_page_artifact, 0}, 2259 {"artifact", json_page_artifact, 0}, 2213 {"branch", json_page_branch,0}, 2260 {"branch", json_page_branch,0}, 2214 {"cap", json_page_cap, 0}, 2261 {"cap", json_page_cap, 0}, 2215 {"config", json_page_config, 0 }, 2262 {"config", json_page_config, 0 }, 2216 {"diff", json_page_diff, 0}, 2263 {"diff", json_page_diff, 0}, 2217 {"dir", json_page_nyi, 0}, | 2264 {"dir", json_page_dir, 0}, > 2265 {"finfo", json_page_finfo, 0}, 2218 {"g", json_page_g, 0}, 2266 {"g", json_page_g, 0}, 2219 {"HAI",json_page_version,0}, 2267 {"HAI",json_page_version,0}, 2220 {"login",json_page_login,0}, 2268 {"login",json_page_login,0}, 2221 {"logout",json_page_logout,0}, 2269 {"logout",json_page_logout,0}, 2222 {"query",json_page_query,0}, 2270 {"query",json_page_query,0}, 2223 {"rebuild",json_page_rebuild,0}, 2271 {"rebuild",json_page_rebuild,0}, 2224 {"report", json_page_report, 0}, 2272 {"report", json_page_report, 0}, 2225 {"resultCodes", json_page_resultCodes,0}, 2273 {"resultCodes", json_page_resultCodes,0}, 2226 {"stat",json_page_stat,0}, 2274 {"stat",json_page_stat,0}, 2227 {"tag", json_page_tag,0}, 2275 {"tag", json_page_tag,0}, 2228 {"ticket", json_page_nyi,0}, | 2276 /*{"ticket", json_page_nyi,0},*/ 2229 {"timeline", json_page_timeline,0}, 2277 {"timeline", json_page_timeline,0}, 2230 {"user",json_page_user,0}, 2278 {"user",json_page_user,0}, 2231 {"version",json_page_version,0}, 2279 {"version",json_page_version,0}, 2232 {"whoami",json_page_whoami,0}, 2280 {"whoami",json_page_whoami,0}, 2233 {"wiki",json_page_wiki,0}, 2281 {"wiki",json_page_wiki,0}, 2234 /* Last entry MUST have a NULL name. */ < 2235 {NULL,NULL,0} < 2236 }; < 2237 < 2238 < 2239 /* < 2240 ** Mapping of /json/ticket/XXX commands/paths to callbacks. < 2241 */ < 2242 static const JsonPageDef JsonPageDefs_Ticket[] = { < 2243 {"get", json_page_nyi, 0}, < 2244 {"list", json_page_nyi, 0}, < 2245 {"save", json_page_nyi, 1}, < 2246 {"create", json_page_nyi, 1}, < 2247 /* Last entry MUST have a NULL name. */ < 2248 {NULL,NULL,0} < 2249 }; < 2250 < 2251 /* < 2252 ** Mapping of /json/artifact/XXX commands/paths to callbacks. < 2253 */ < 2254 static const JsonPageDef JsonPageDefs_Artifact[] = { < 2255 {"vinfo", json_page_nyi, 0}, < 2256 {"finfo", json_page_nyi, 0}, < 2257 /* Last entry MUST have a NULL name. */ < 2258 {NULL,NULL,0} < 2259 }; < 2260 < 2261 /* < 2262 ** Mapping of /json/tag/XXX commands/paths to callbacks. < 2263 */ < 2264 static const JsonPageDef JsonPageDefs_Tag[] = { < 2265 {"list", json_page_nyi, 0}, < 2266 {"create", json_page_nyi, 1}, < 2267 /* Last entry MUST have a NULL name. */ 2282 /* Last entry MUST have a NULL name. */ 2268 {NULL,NULL,0} 2283 {NULL,NULL,0} 2269 }; 2284 }; 2270 2285 2271 /* 2286 /* 2272 ** Internal helper for json_cmd_top() and json_page_top(). 2287 ** Internal helper for json_cmd_top() and json_page_top(). 2273 ** 2288 ** ................................................................................................................................................................................ 2293 rc = FSL_JSON_E_WRONG_MODE; 2308 rc = FSL_JSON_E_WRONG_MODE; 2294 } 2309 } 2295 else{ 2310 else{ 2296 rc = 0; 2311 rc = 0; 2297 g.json.dispatchDepth = 1; 2312 g.json.dispatchDepth = 1; 2298 payload = (*pageDef->func)(); 2313 payload = (*pageDef->func)(); 2299 } 2314 } 2300 payload = json_create_response(rc, rc ? g.zErrMsg : NULL, payload); | 2315 payload = json_create_response(rc, NULL, payload); 2301 json_send_response(payload); 2316 json_send_response(payload); 2302 cson_value_free(payload); 2317 cson_value_free(payload); 2303 return rc; 2318 return rc; 2304 } 2319 } 2305 2320 2306 #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */ 2321 #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */ 2307 /* 2322 /* ................................................................................................................................................................................ 2313 */ 2328 */ 2314 void json_page_top(void){ 2329 void json_page_top(void){ 2315 char const * zCommand; 2330 char const * zCommand; 2316 BEGIN_TIMER; 2331 BEGIN_TIMER; 2317 json_mode_bootstrap(); 2332 json_mode_bootstrap(); 2318 zCommand = json_command_arg(1); 2333 zCommand = json_command_arg(1); 2319 if(!zCommand || !*zCommand){ 2334 if(!zCommand || !*zCommand){ 2320 goto usage; | 2335 json_dispatch_missing_args_err( JsonPageDefs, 2321 } < > 2336 "No command (sub-path) specified." 2322 json_dispatch_root_command( zCommand ); | 2337 " Try one of: "); 2323 return; | 2338 return; 2324 usage: < 2325 { < 2326 Blob cmdNames = empty_blob; < 2327 blob_init(&cmdNames, < 2328 "No command (sub-path) specified. Try one of: ", < 2329 -1); < 2330 json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames); < 2331 json_err(FSL_JSON_E_MISSING_ARGS, < 2332 blob_str(&cmdNames), 0); < 2333 blob_reset(&cmdNames); < 2334 } 2339 } 2335 < > 2340 json_dispatch_root_command( zCommand ); 2336 } 2341 } 2337 #endif /* FOSSIL_ENABLE_JSON for mkindex */ 2342 #endif /* FOSSIL_ENABLE_JSON for mkindex */ 2338 2343 2339 #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */ 2344 #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */ 2340 /* 2345 /* 2341 ** This function dispatches json commands and is the CLI equivalent of 2346 ** This function dispatches json commands and is the CLI equivalent of 2342 ** json_page_top(). 2347 ** json_page_top(). 2343 ** 2348 ** 2344 ** COMMAND: json 2349 ** COMMAND: json 2345 ** 2350 ** 2346 ** Usage: %fossil json SUBCOMMAND | 2351 ** Usage: %fossil json SUBCOMMAND ?OPTIONS? > 2352 ** > 2353 ** In CLI mode, the -R REPO common option is supported. Due to limitations > 2354 ** in the argument dispatching code, any -FLAGS must come after the final > 2355 ** sub- (or subsub-) command. 2347 ** 2356 ** 2348 ** The commands include: 2357 ** The commands include: 2349 ** 2358 ** > 2359 ** anonymousPassord > 2360 ** artifact 2350 ** branch 2361 ** branch 2351 ** cap 2362 ** cap > 2363 ** diff > 2364 ** g > 2365 ** login > 2366 ** logout > 2367 ** query > 2368 ** rebuild > 2369 ** report > 2370 ** resultCodes 2352 ** stat 2371 ** stat > 2372 ** tag 2353 ** timeline 2373 ** timeline > 2374 ** user 2354 ** version (alias: HAI) 2375 ** version (alias: HAI) > 2376 ** whoami 2355 ** wiki 2377 ** wiki 2356 ** 2378 ** > 2379 ** Run '%fossil json' without any subcommand to see the full list (but be > 2380 ** aware that some listed might not yet be implemented). 2357 ** 2381 ** 2358 ** TODOs: < 2359 ** < 2360 ** tag < 2361 ** ticket < 2362 ** ... < > 2382 ** PS: the notable TODO-commands include: config, dir, finfo, ticket 2363 ** 2383 ** 2364 */ 2384 */ 2365 void json_cmd_top(void){ 2385 void json_cmd_top(void){ 2366 char const * cmd = NULL; 2386 char const * cmd = NULL; 2367 int rc = 0; 2387 int rc = 0; 2368 BEGIN_TIMER; 2388 BEGIN_TIMER; 2369 memset( &g.perm, 0xff, sizeof(g.perm) ) 2389 memset( &g.perm, 0xff, sizeof(g.perm) ) ................................................................................................................................................................................ 2395 e.g. add g.errCode. 2415 e.g. add g.errCode. 2396 */ 2416 */ 2397 fossil_exit(1); 2417 fossil_exit(1); 2398 } 2418 } 2399 return; 2419 return; 2400 usage: 2420 usage: 2401 { 2421 { 2402 Blob cmdNames = empty_blob; | 2422 cson_value * payload; 2403 blob_init(&cmdNames, | 2423 json_dispatch_missing_args_err( JsonPageDefs, 2404 "No subcommand specified. Try one of: ", -1); | 2424 "No subcommand specified." 2405 json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames); | 2425 " Try one of: "); 2406 json_err(FSL_JSON_E_MISSING_ARGS, | 2426 payload = json_create_response(0, NULL, NULL); 2407 blob_str(&cmdNames), 1); | 2427 json_send_response(payload); 2408 blob_reset(&cmdNames); | 2428 cson_value_free(payload); 2409 fossil_exit(1); 2429 fossil_exit(1); 2410 } 2430 } 2411 } 2431 } 2412 #endif /* FOSSIL_ENABLE_JSON for mkindex */ 2432 #endif /* FOSSIL_ENABLE_JSON for mkindex */ 2413 2433 2414 #endif /* FOSSIL_ENABLE_JSON */ 2434 #endif /* FOSSIL_ENABLE_JSON */
Changes to src/json_artifact.c
79 ** Generates an artifact Object for the given rid, 79 ** Generates an artifact Object for the given rid, 80 ** which must refer to a Checkin. 80 ** which must refer to a Checkin. 81 ** 81 ** 82 ** Returned value is NULL or an Object owned by the caller. 82 ** Returned value is NULL or an Object owned by the caller. 83 */ 83 */ 84 cson_value * json_artifact_for_ci( int rid, char showFiles ){ 84 cson_value * json_artifact_for_ci( int rid, char showFiles ){ 85 cson_value * v = NULL; 85 cson_value * v = NULL; 86 Stmt q; | 86 Stmt q = empty_Stmt; 87 static cson_value * eventTypeLabel = NULL; 87 static cson_value * eventTypeLabel = NULL; 88 if(!eventTypeLabel){ 88 if(!eventTypeLabel){ 89 eventTypeLabel = json_new_string("checkin"); 89 eventTypeLabel = json_new_string("checkin"); 90 json_gc_add("$EVENT_TYPE_LABEL(commit)", eventTypeLabel); 90 json_gc_add("$EVENT_TYPE_LABEL(commit)", eventTypeLabel); 91 } 91 } 92 | 92 93 db_prepare(&q, | 93 db_prepare(&q, 94 "SELECT uuid, " | 94 "SELECT b.uuid, " 95 " cast(strftime('%%s',mtime) as int), " | 95 " cast(strftime('%%s',e.mtime) as int), " > 96 " strftime('%%s',e.omtime)," 96 " user, " | 97 " e.user, " 97 " comment," | 98 " e.comment" 98 " strftime('%%s',omtime)" < 99 " FROM blob, event" | 99 " FROM blob b, event e" 100 " WHERE blob.rid=%d" | 100 " WHERE b.rid=%d" 101 " AND event.objid=%d", | 101 " AND e.objid=%d", 102 rid, rid 102 rid, rid 103 ); 103 ); 104 if( db_step(&q)==SQLITE_ROW ){ 104 if( db_step(&q)==SQLITE_ROW ){ 105 cson_object * o; 105 cson_object * o; 106 cson_value * tmpV = NULL; 106 cson_value * tmpV = NULL; 107 const char *zUuid = db_column_text(&q, 0); 107 const char *zUuid = db_column_text(&q, 0); 108 const char *zUser; 108 const char *zUser; 109 const char *zComment; 109 const char *zComment; 110 char * zEUser, * zEComment; 110 char * zEUser, * zEComment; 111 #define ADD_PRIMARY_PARENT_UUID 0 /* temporary local macro */ < 112 #if ADD_PRIMARY_PARENT_UUID < 113 char * zParent = NULL; < 114 #endif < 115 int mtime, omtime; 111 int mtime, omtime; 116 v = cson_value_new_object(); 112 v = cson_value_new_object(); 117 o = cson_value_get_object(v); 113 o = cson_value_get_object(v); 118 #define SET(K,V) cson_object_set(o,(K), (V)) 114 #define SET(K,V) cson_object_set(o,(K), (V)) 119 SET("type", eventTypeLabel ); 115 SET("type", eventTypeLabel ); 120 SET("uuid",json_new_string(zUuid)); 116 SET("uuid",json_new_string(zUuid)); 121 SET("isLeaf", cson_value_new_bool(is_a_leaf(rid))); 117 SET("isLeaf", cson_value_new_bool(is_a_leaf(rid))); > 118 > 119 mtime = db_column_int(&q,1); > 120 SET("timestamp",json_new_int(mtime)); > 121 omtime = db_column_int(&q,2); > 122 if(omtime && (omtime!=mtime)){ > 123 SET("originTime",json_new_int(omtime)); > 124 } > 125 122 zUser = db_column_text(&q,2); | 126 zUser = db_column_text(&q,3); 123 zEUser = db_text(0, 127 zEUser = db_text(0, 124 "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", 128 "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", 125 TAG_USER, rid); 129 TAG_USER, rid); 126 if(zEUser){ 130 if(zEUser){ 127 SET("user", json_new_string(zEUser)); 131 SET("user", json_new_string(zEUser)); 128 if(0!=strcmp(zEUser,zUser)){ 132 if(0!=strcmp(zEUser,zUser)){ 129 SET("originUser",json_new_string(zUser)); 133 SET("originUser",json_new_string(zUser)); 130 } 134 } 131 free(zEUser); 135 free(zEUser); 132 }else{ 136 }else{ 133 SET("user",json_new_string(zUser)); 137 SET("user",json_new_string(zUser)); 134 } 138 } 135 139 136 zComment = db_column_text(&q,3); | 140 zComment = db_column_text(&q,4); 137 zEComment = db_text(0, 141 zEComment = db_text(0, 138 "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", 142 "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", 139 TAG_COMMENT, rid); 143 TAG_COMMENT, rid); 140 if(zEComment){ 144 if(zEComment){ 141 SET("comment",json_new_string(zEComment)); 145 SET("comment",json_new_string(zEComment)); 142 if(0 != strcmp(zEComment,zComment)){ 146 if(0 != strcmp(zEComment,zComment)){ 143 SET("originComment", json_new_string(zComment)); 147 SET("originComment", json_new_string(zComment)); 144 } 148 } 145 free(zEComment); 149 free(zEComment); 146 }else{ 150 }else{ 147 SET("comment",json_new_string(zComment)); 151 SET("comment",json_new_string(zComment)); 148 } 152 } 149 153 150 mtime = db_column_int(&q,1); < 151 SET("mtime",json_new_int(mtime)); < 152 omtime = db_column_int(&q,4); < 153 if(omtime && (omtime!=mtime)){ < 154 SET("originTime",json_new_int(omtime)); < 155 } < 156 < 157 #if ADD_PRIMARY_PARENT_UUID < 158 zParent = db_text(0, < 159 "SELECT uuid FROM plink, blob" < 160 " WHERE plink.cid=%d AND blob.rid=plink.pid" < 161 " AND plink.isprim", < 162 rid < 163 ); < 164 tmpV = zParent ? json_new_string(zParent) : cson_value_null(); < 165 free(zParent); < 166 zParent = NULL; < 167 SET("parentUuid", tmpV ); < 168 #endif < 169 #undef ADD_PRIMARY_PARENT_UUID < 170 < 171 tmpV = json_parent_uuids_for_ci(rid); 154 tmpV = json_parent_uuids_for_ci(rid); 172 if(tmpV){ 155 if(tmpV){ 173 SET("parents", tmpV); 156 SET("parents", tmpV); 174 } 157 } 175 158 176 tmpV = json_tags_for_checkin_rid(rid,0); 159 tmpV = json_tags_for_checkin_rid(rid,0); 177 if(tmpV){ 160 if(tmpV){ ................................................................................................................................................................................ 221 return cson_object_value(pay); 204 return cson_object_value(pay); 222 } 205 } 223 206 224 /* 207 /* 225 ** Sub-impl of /json/artifact for checkins. 208 ** Sub-impl of /json/artifact for checkins. 226 */ 209 */ 227 static cson_value * json_artifact_ci( int rid ){ 210 static cson_value * json_artifact_ci( int rid ){ 228 if(! g.perm.Read ){ | 211 if(!g.perm.Read){ 229 g.json.resultCode = FSL_JSON_E_DENIED; | 212 json_set_err( FSL_JSON_E_DENIED, "Viewing checkins requires 'o' privileges." 230 return NULL; 213 return NULL; 231 }else{ 214 }else{ 232 return json_artifact_for_ci(rid, 1); 215 return json_artifact_for_ci(rid, 1); 233 } 216 } 234 } 217 } 235 218 236 /* 219 /* ................................................................................................................................................................................ 256 239 257 cson_value * json_artifact_wiki(int rid){ 240 cson_value * json_artifact_wiki(int rid){ 258 if( ! g.perm.RdWiki ){ 241 if( ! g.perm.RdWiki ){ 259 json_set_err(FSL_JSON_E_DENIED, 242 json_set_err(FSL_JSON_E_DENIED, 260 "Requires 'j' privileges."); 243 "Requires 'j' privileges."); 261 return NULL; 244 return NULL; 262 }else{ 245 }else{ > 246 char contentFormat = json_wiki_get_content_format_flag(-9); > 247 if(-9 == contentFormat){ > 248 contentFormat = json_artifact_include_content_flag() ? -1 : 0; > 249 } 263 return json_get_wiki_page_by_rid(rid, 0); | 250 return json_get_wiki_page_by_rid(rid, contentFormat); 264 } 251 } 265 } 252 } > 253 > 254 /* > 255 ** Internal helper for routines which add a "status" flag to file > 256 ** artifact data. isNew and isDel should be the "is this object new?" > 257 ** and "is this object removed?" flags of the underlying query. This > 258 ** function returns a static string from the set (added, removed, > 259 ** modified), depending on the combination of the two args. > 260 ** > 261 ** Reminder to self: (mlink.pid==0) AS isNew, (mlink.fid==0) AS isDel > 262 */ > 263 char const * json_artifact_status_to_string( char isNew, char isDel ){ > 264 return isNew > 265 ? "added" > 266 : (isDel > 267 ? "removed" > 268 : "modified"); > 269 } 266 270 267 cson_value * json_artifact_file(int rid){ 271 cson_value * json_artifact_file(int rid){ 268 cson_object * pay = NULL; 272 cson_object * pay = NULL; 269 const char *zMime; < 270 Blob content = empty_blob; < 271 Stmt q = empty_Stmt; 273 Stmt q = empty_Stmt; 272 cson_array * checkin_arr = NULL; 274 cson_array * checkin_arr = NULL; 273 275 274 if( ! g.perm.Read ){ 276 if( ! g.perm.Read ){ 275 json_set_err(FSL_JSON_E_DENIED, 277 json_set_err(FSL_JSON_E_DENIED, 276 "Requires 'o' privileges."); 278 "Requires 'o' privileges."); 277 return NULL; 279 return NULL; 278 } 280 } 279 281 280 pay = cson_new_object(); 282 pay = cson_new_object(); 281 283 > 284 if( json_artifact_include_content_flag() ){ > 285 Blob content = empty_blob; > 286 const char *zMime; 282 content_get(rid, &content); | 287 content_get(rid, &content); 283 cson_object_set(pay, "contentLength", < 284 json_new_int( blob_size(&content) ) < 285 /* achtung: overflow potential on 32-bit builds! */); < 286 zMime = mimetype_from_content(&content); | 288 zMime = mimetype_from_content(&content); 287 < 288 cson_object_set(pay, "contentType", | 289 cson_object_set(pay, "contentType", 289 json_new_string(zMime ? zMime : "text/plain")); | 290 json_new_string(zMime ? zMime : "text/plain")); > 291 cson_object_set(pay, "size", json_new_int( blob_size(&content)) ); 290 if( json_artifact_include_content_flag() && !zMime ){ | 292 if(!zMime){ 291 cson_object_set(pay, "content", 293 cson_object_set(pay, "content", 292 cson_value_new_string(blob_str(&content), 294 cson_value_new_string(blob_str(&content), 293 (unsigned int)blob_size(&content))); 295 (unsigned int)blob_size(&content))); 294 } | 296 } 295 blob_reset(&content); | 297 blob_reset(&content); > 298 } 296 299 297 db_prepare(&q, 300 db_prepare(&q, 298 "SELECT filename.name AS name, " 301 "SELECT filename.name AS name, " > 302 " (mlink.pid==0) AS isNew," > 303 " (mlink.fid==0) AS isDel," 299 " cast(strftime('%%s',event.mtime) as int) AS mtime," | 304 " cast(strftime('%%s',event.mtime) as int) AS timestamp," 300 " coalesce(event.ecomment,event.comment) as comment," | 305 " coalesce(event.ecomment,event.comment) as comment," 301 " coalesce(event.euser,event.user) as user," | 306 " coalesce(event.euser,event.user) as user," > 307 " a.size AS size," > 308 " b.uuid as uuid, " > 309 #if 0 302 " b.uuid as uuid, mlink.mperm as mperm," | 310 " mlink.mperm as mperm," > 311 #endif 303 " coalesce((SELECT value FROM tagxref" | 312 " coalesce((SELECT value FROM tagxref" 304 " WHERE tagid=%d AND tagtype>0 AND " 313 " WHERE tagid=%d AND tagtype>0 AND " 305 " rid=mlink.mid),'trunk') as branch" 314 " rid=mlink.mid),'trunk') as branch" 306 " FROM mlink, filename, event, blob a, blob b" 315 " FROM mlink, filename, event, blob a, blob b" 307 " WHERE filename.fnid=mlink.fnid" 316 " WHERE filename.fnid=mlink.fnid" 308 " AND event.objid=mlink.mid" 317 " AND event.objid=mlink.mid" 309 " AND a.rid=mlink.fid" 318 " AND a.rid=mlink.fid" 310 " AND b.rid=mlink.mid" 319 " AND b.rid=mlink.mid" 311 " AND mlink.fid=%d" 320 " AND mlink.fid=%d" 312 " ORDER BY filename.name, event.mtime", 321 " ORDER BY filename.name, event.mtime", 313 TAG_BRANCH, rid 322 TAG_BRANCH, rid 314 ); 323 ); > 324 /* TODO: add a "state" flag for the file in each checkin, > 325 e.g. "modified", "new", "deleted". > 326 */ 315 checkin_arr = cson_new_array(); 327 checkin_arr = cson_new_array(); 316 cson_object_set(pay, "checkins", cson_array_value(checkin_arr)); 328 cson_object_set(pay, "checkins", cson_array_value(checkin_arr)); 317 json_stmt_to_array_of_obj( &q, checkin_arr ); < > 329 while( (SQLITE_ROW==db_step(&q) ) ){ > 330 cson_object * row = cson_value_get_object(cson_sqlite3_row_to_object(q.pStmt > 331 char const isNew = cson_value_get_bool(cson_object_get(row,"isNew")); > 332 char const isDel = cson_value_get_bool(cson_object_get(row,"isDel")); > 333 cson_object_set(row, "isNew", NULL); > 334 cson_object_set(row, "isDel", NULL); > 335 cson_object_set(row, "state", > 336 json_new_string(json_artifact_status_to_string(isNew, isDel) > 337 cson_array_append( checkin_arr, cson_object_value(row) ); > 338 } 318 db_finalize(&q); 339 db_finalize(&q); 319 return cson_object_value(pay); 340 return cson_object_value(pay); 320 } 341 } 321 342 322 /* 343 /* 323 ** Impl of /json/artifact. This basically just determines the type of 344 ** Impl of /json/artifact. This basically just determines the type of 324 ** an artifact and forwards the real work to another function. 345 ** an artifact and forwards the real work to another function. ................................................................................................................................................................................ 329 char const * zType = NULL; 350 char const * zType = NULL; 330 char const * zUuid = NULL; 351 char const * zUuid = NULL; 331 cson_value * entry = NULL; 352 cson_value * entry = NULL; 332 Blob uuid = empty_blob; 353 Blob uuid = empty_blob; 333 int rc; 354 int rc; 334 int rid = 0; 355 int rid = 0; 335 ArtifactDispatchEntry const * dispatcher = &ArtifactDispatchList[0]; 356 ArtifactDispatchEntry const * dispatcher = &ArtifactDispatchList[0]; 336 zName = json_find_option_cstr2("uuid", NULL, NULL, 2); | 357 zName = json_find_option_cstr2("name", NULL, NULL, g.json.dispatchDepth+1); 337 if(!zName || !*zName) { 358 if(!zName || !*zName) { 338 json_set_err(FSL_JSON_E_MISSING_ARGS, 359 json_set_err(FSL_JSON_E_MISSING_ARGS, 339 "Missing 'uuid' argument."); | 360 "Missing 'name' argument."); 340 return NULL; 361 return NULL; 341 } 362 } 342 363 343 if( validate16(zName, strlen(zName)) ){ 364 if( validate16(zName, strlen(zName)) ){ 344 if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ 365 if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ 345 zType = "ticket"; 366 zType = "ticket"; 346 goto handle_entry; 367 goto handle_entry; ................................................................................................................................................................................ 404 if(!g.json.resultCode){ 425 if(!g.json.resultCode){ 405 assert( NULL != entry ); 426 assert( NULL != entry ); 406 assert( NULL != zType ); 427 assert( NULL != zType ); 407 pay = cson_new_object(); 428 pay = cson_new_object(); 408 cson_object_set( pay, "type", json_new_string(zType) ); 429 cson_object_set( pay, "type", json_new_string(zType) ); 409 /*cson_object_set( pay, "uuid", json_new_string(zUuid) );*/ 430 /*cson_object_set( pay, "uuid", json_new_string(zUuid) );*/ 410 cson_object_set( pay, "name", json_new_string(zName ? zName : zUuid) ); 431 cson_object_set( pay, "name", json_new_string(zName ? zName : zUuid) ); 411 cson_object_set( pay, "rid", cson_value_new_integer(rid) ); | 432 /*cson_object_set( pay, "rid", cson_value_new_integer(rid) );*/ 412 if(entry){ 433 if(entry){ 413 cson_object_set(pay, "artifact", entry); 434 cson_object_set(pay, "artifact", entry); 414 } 435 } 415 } 436 } 416 veryend: 437 veryend: 417 blob_reset(&uuid); 438 blob_reset(&uuid); 418 return cson_object_value(pay); 439 return cson_object_value(pay); 419 } 440 } 420 441 421 #endif /* FOSSIL_ENABLE_JSON */ 442 #endif /* FOSSIL_ENABLE_JSON */
Changes to src/json_detail.h
15 ** Author contact information: 15 ** Author contact information: 16 ** drh@hwaci.com 16 ** drh@hwaci.com 17 ** http://www.hwaci.com/drh/ 17 ** http://www.hwaci.com/drh/ 18 ** 18 ** 19 */ 19 */ 20 20 21 #include "cson_amalgamation.h" 21 #include "cson_amalgamation.h" > 22 > 23 /** > 24 FOSSIL_JSON_API_VERSION holds the date (YYYYMMDD) of the latest > 25 "significant" change to the JSON API (a change in an interface > 26 or new functionality). It is sent as part of the /json/version > 27 request. We could arguably add it to each response. > 28 */ > 29 #define FOSSIL_JSON_API_VERSION "20120409" > 30 22 /* 31 /* 23 ** Impl details for the JSON API which need to be shared 32 ** Impl details for the JSON API which need to be shared 24 ** across multiple C files. 33 ** across multiple C files. 25 */ 34 */ 26 35 27 /* 36 /* 28 ** The "official" list of Fossil/JSON error codes. Their values might 37 ** The "official" list of Fossil/JSON error codes. Their values might ................................................................................................................................................................................ 31 ** 40 ** 32 ** Values must be in the range 1000..9999 for error codes and 1..999 41 ** Values must be in the range 1000..9999 for error codes and 1..999 33 ** for warning codes. 42 ** for warning codes. 34 ** 43 ** 35 ** Numbers evenly dividable by 100 are "categories", and error codes 44 ** Numbers evenly dividable by 100 are "categories", and error codes 36 ** for a given category have their high bits set to the category 45 ** for a given category have their high bits set to the category 37 ** value. 46 ** value. > 47 ** > 48 ** Maintenance reminder: when entries are added to this list, update > 49 ** the code in json_page_resultCodes() and json_err_cstr() (both in > 50 ** json.c)! 38 ** 51 ** 39 */ 52 */ 40 enum FossilJsonCodes { 53 enum FossilJsonCodes { 41 FSL_JSON_W_START = 0, 54 FSL_JSON_W_START = 0, 42 FSL_JSON_W_UNKNOWN /*+1*/, 55 FSL_JSON_W_UNKNOWN /*+1*/, 43 FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/, 56 FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/, 44 FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/, 57 FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/,
Changes to src/json_diff.c
27 27 28 /* 28 /* 29 ** Generates a diff between two versions (zFrom and zTo), using nContext 29 ** Generates a diff between two versions (zFrom and zTo), using nContext 30 ** content lines in the output. On success, returns a new JSON String 30 ** content lines in the output. On success, returns a new JSON String 31 ** object. On error it sets g.json's error state and returns NULL. 31 ** object. On error it sets g.json's error state and returns NULL. 32 ** 32 ** 33 ** If fSbs is true (non-0) them side-by-side diffs are used. 33 ** If fSbs is true (non-0) them side-by-side diffs are used. > 34 ** > 35 ** If fHtml is true then HTML markup is added to the diff. 34 */ 36 */ 35 cson_value * json_generate_diff(const char *zFrom, const char *zTo, 37 cson_value * json_generate_diff(const char *zFrom, const char *zTo, 36 int nContext, char fSbs){ | 38 int nContext, char fSbs, > 39 char fHtml){ 37 int fromid; 40 int fromid; 38 int toid; 41 int toid; 39 int outLen; 42 int outLen; 40 Blob from = empty_blob, to = empty_blob, out = empty_blob; 43 Blob from = empty_blob, to = empty_blob, out = empty_blob; 41 cson_value * rc = NULL; 44 cson_value * rc = NULL; 42 int flags = (DIFF_CONTEXT_MASK & nContext) 45 int flags = (DIFF_CONTEXT_MASK & nContext) 43 | (fSbs ? DIFF_SIDEBYSIDE : 0); | 46 | (fSbs ? DIFF_SIDEBYSIDE : 0) > 47 | (fHtml ? DIFF_HTML : 0); 44 fromid = name_to_typed_rid(zFrom, "*"); 48 fromid = name_to_typed_rid(zFrom, "*"); 45 if(fromid<=0){ 49 if(fromid<=0){ 46 json_set_err(FSL_JSON_E_UNRESOLVED_UUID, 50 json_set_err(FSL_JSON_E_UNRESOLVED_UUID, 47 "Could not resolve 'from' ID."); 51 "Could not resolve 'from' ID."); 48 return NULL; 52 return NULL; 49 } 53 } 50 toid = name_to_typed_rid(zTo, "*"); 54 toid = name_to_typed_rid(zTo, "*"); ................................................................................................................................................................................ 56 content_get(fromid, &from); 60 content_get(fromid, &from); 57 content_get(toid, &to); 61 content_get(toid, &to); 58 blob_zero(&out); 62 blob_zero(&out); 59 text_diff(&from, &to, &out, flags); 63 text_diff(&from, &to, &out, flags); 60 blob_reset(&from); 64 blob_reset(&from); 61 blob_reset(&to); 65 blob_reset(&to); 62 outLen = blob_size(&out); 66 outLen = blob_size(&out); 63 if(outLen>0){ | 67 if(outLen>=0){ 64 rc = cson_value_new_string(blob_buffer(&out), blob_size(&out)); | 68 rc = cson_value_new_string(blob_buffer(&out), > 69 (unsigned int)blob_size(&out)); 65 } 70 } 66 blob_reset(&out); 71 blob_reset(&out); 67 return rc; 72 return rc; 68 } 73 } 69 74 70 /* 75 /* 71 ** Implementation of the /json/diff page. 76 ** Implementation of the /json/diff page. ................................................................................................................................................................................ 82 cson_value * json_page_diff(){ 87 cson_value * json_page_diff(){ 83 cson_object * pay = NULL; 88 cson_object * pay = NULL; 84 cson_value * v = NULL; 89 cson_value * v = NULL; 85 char const * zFrom; 90 char const * zFrom; 86 char const * zTo; 91 char const * zTo; 87 int nContext = 0; 92 int nContext = 0; 88 char doSBS; 93 char doSBS; > 94 char doHtml; 89 if(!g.perm.Read){ 95 if(!g.perm.Read){ 90 json_set_err(FSL_JSON_E_DENIED, 96 json_set_err(FSL_JSON_E_DENIED, 91 "Requires 'o' permissions."); 97 "Requires 'o' permissions."); 92 return NULL; 98 return NULL; 93 } 99 } 94 zFrom = json_find_option_cstr("v1",NULL,NULL); 100 zFrom = json_find_option_cstr("v1",NULL,NULL); 95 if(!zFrom){ 101 if(!zFrom){ ................................................................................................................................................................................ 107 if(!zTo){ 113 if(!zTo){ 108 json_set_err(FSL_JSON_E_MISSING_ARGS, 114 json_set_err(FSL_JSON_E_MISSING_ARGS, 109 "Required 'v2' parameter is missing."); 115 "Required 'v2' parameter is missing."); 110 return NULL; 116 return NULL; 111 } 117 } 112 nContext = json_find_option_int("context",NULL,"c",5); 118 nContext = json_find_option_int("context",NULL,"c",5); 113 doSBS = json_find_option_bool("sbs",NULL,"y",0); 119 doSBS = json_find_option_bool("sbs",NULL,"y",0); > 120 doHtml = json_find_option_bool("html",NULL,"h",0); 114 v = json_generate_diff(zFrom, zTo, nContext, doSBS); | 121 v = json_generate_diff(zFrom, zTo, nContext, doSBS, doHtml); 115 if(!v){ 122 if(!v){ 116 if(!g.json.resultCode){ 123 if(!g.json.resultCode){ 117 json_set_err(FSL_JSON_E_UNKNOWN, 124 json_set_err(FSL_JSON_E_UNKNOWN, 118 "Generating diff failed for unknown reason."); 125 "Generating diff failed for unknown reason."); 119 } 126 } 120 return NULL; 127 return NULL; 121 } 128 }
Added src/json_dir.c
> 1 #ifdef FOSSIL_ENABLE_JSON > 2 /* > 3 ** Copyright (c) 2011 D. Richard Hipp > 4 ** > 5 ** This program is free software; you can redistribute it and/or > 6 ** modify it under the terms of the Simplified BSD License (also > 7 ** known as the "2-Clause License" or "FreeBSD License".) > 8 ** > 9 ** This program is distributed in the hope that it will be useful, > 10 ** but without any warranty; without even the implied warranty of > 11 ** merchantability or fitness for a particular purpose. > 12 ** > 13 ** Author contact information: > 14 ** drh@hwaci.com > 15 ** http://www.hwaci.com/drh/ > 16 ** > 17 */ > 18 #include "VERSION.h" > 19 #include "config.h" > 20 #include "json_dir.h" > 21 > 22 #if INTERFACE > 23 #include "json_detail.h" > 24 #endif > 25 > 26 static cson_value * json_page_dir_list(); > 27 /* > 28 ** Mapping of /json/wiki/XXX commands/paths to callbacks. > 29 */ > 30 static const JsonPageDef JsonPageDefs_Dir[] = { > 31 /* Last entry MUST have a NULL name. */ > 32 {NULL,NULL,0} > 33 }; > 34 > 35 #if 0 /* TODO: Not used? */ > 36 static char const * json_dir_path_extra(){ > 37 static char const * zP = NULL; > 38 if( !zP ){ > 39 zP = g.zExtra; > 40 while(zP && *zP && ('/'==*zP)){ > 41 ++zP; > 42 } > 43 } > 44 return zP; > 45 } > 46 #endif > 47 > 48 /* > 49 ** Impl of /json/dir. 98% of it was taken directly > 50 ** from browse.c::page_dir() > 51 */ > 52 static cson_value * json_page_dir_list(){ > 53 cson_object * zPayload = NULL; /* return value */ > 54 cson_array * zEntries = NULL; /* accumulated list of entries. */ > 55 cson_object * zEntry = NULL; /* a single dir/file entry. */ > 56 cson_array * keyStore = NULL; /* garbage collector for shared strings. */ > 57 cson_string * zKeyName = NULL; > 58 cson_string * zKeySize = NULL; > 59 cson_string * zKeyIsDir = NULL; > 60 cson_string * zKeyUuid = NULL; > 61 cson_string * zKeyTime = NULL; > 62 cson_string * zKeyRaw = NULL; > 63 char * zD = NULL; > 64 char const * zDX = NULL; > 65 int nD; > 66 char * zUuid = NULL; > 67 char const * zCI = NULL; > 68 Manifest * pM = NULL; > 69 Stmt q = empty_Stmt; > 70 int rid = 0; > 71 if( !g.perm.History ){ > 72 json_set_err(FSL_JSON_E_DENIED, "Requires 'h' permissions."); > 73 return NULL; > 74 } > 75 zCI = json_find_option_cstr("checkin",NULL,"ci" ); > 76 > 77 /* If a specific check-in is requested, fetch and parse it. If the > 78 ** specific check-in does not exist, clear zCI. zCI==0 will cause all > 79 ** files from all check-ins to be displayed. > 80 */ > 81 if( zCI && *zCI ){ > 82 pM = manifest_get_by_name(zCI, &rid); > 83 if( pM ){ > 84 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); > 85 }else{ > 86 json_set_err(FSL_JSON_E_UNRESOLVED_UUID, > 87 "Checkin name [%s] is unresolved.", > 88 zCI); > 89 return NULL; > 90 } > 91 } > 92 > 93 /* Jump through some hoops to find the directory name... */ > 94 zDX = json_find_option_cstr("name",NULL,NULL); > 95 if(!zDX && !g.isHTTP){ > 96 zDX = json_command_arg(g.json.dispatchDepth+1); > 97 } > 98 if(zDX && (!*zDX || (0==strcmp(zDX,"/")))){ > 99 zDX = NULL; > 100 } > 101 zD = zDX ? fossil_strdup(zDX) : NULL; > 102 nD = zD ? strlen(zD)+1 : 0; > 103 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } > 104 > 105 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, > 106 pathelementFunc, 0, 0); > 107 > 108 /* Compute the temporary table "localfiles" containing the names > 109 ** of all files and subdirectories in the zD[] directory. > 110 ** > 111 ** Subdirectory names begin with "/". This causes them to sort > 112 ** first and it also gives us an easy way to distinguish files > 113 ** from directories in the loop that follows. > 114 */ > 115 > 116 if( zCI ){ > 117 Stmt ins; > 118 ManifestFile *pFile; > 119 ManifestFile *pPrev = 0; > 120 int nPrev = 0; > 121 int c; > 122 > 123 db_multi_exec( > 124 "CREATE TEMP TABLE json_dir_files(" > 125 " n UNIQUE NOT NULL %s," /* file name */ > 126 " fn UNIQUE NOT NULL %s," /* full file name */ > 127 " u DEFAULT NULL," /* file uuid */ > 128 " sz DEFAULT -1," /* file size */ > 129 " mtime DEFAULT NULL" /* file mtime in unix epoch format */ > 130 ");", > 131 filename_collation(), filename_collation() > 132 ); > 133 > 134 db_prepare(&ins, > 135 "INSERT OR IGNORE INTO json_dir_files (n,fn,u,sz,mtime) " > 136 "SELECT" > 137 " pathelement(:path,0)," > 138 " CASE WHEN %Q IS NULL THEN '' ELSE %Q||'/' END ||:abspath," > 139 " a.uuid," > 140 " a.size," > 141 " CAST(strftime('%%s',e.mtime) AS INTEGER) " > 142 "FROM" > 143 " mlink m, " > 144 " event e," > 145 " blob a," > 146 " blob b " > 147 "WHERE" > 148 " e.objid=m.mid" > 149 " AND a.rid=m.fid"/*FILE artifact*/ > 150 " AND b.rid=m.mid"/*CHECKIN artifact*/ > 151 " AND a.uuid=:uuid", > 152 zD, zD > 153 ); > 154 manifest_file_rewind(pM); > 155 while( (pFile = manifest_file_next(pM,0))!=0 ){ > 156 if( nD>0 > 157 && ((pFile->zName[nD-1]!='/') || (0!=memcmp(pFile->zName, zD, nD-1))) > 158 ){ > 159 continue; > 160 } > 161 /*printf("zD=%s, nD=%d, pFile->zName=%s\n", zD, nD, pFile->zName);*/ > 162 if( pPrev > 163 && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 > 164 && (pFile->zName[nD+nPrev]==0 || pFile->zName[nD+nPrev]=='/') > 165 ){ > 166 continue; > 167 } > 168 db_bind_text( &ins, ":path", &pFile->zName[nD] ); > 169 db_bind_text( &ins, ":abspath", &pFile->zName[nD] ); > 170 db_bind_text( &ins, ":uuid", pFile->zUuid ); > 171 db_step(&ins); > 172 db_reset(&ins); > 173 pPrev = pFile; > 174 for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){} > 175 if( c=='/' ) nPrev++; > 176 } > 177 db_finalize(&ins); > 178 }else if( zD && *zD ){ > 179 if( filenames_are_case_sensitive() ){ > 180 db_multi_exec( > 181 "CREATE TEMP VIEW json_dir_files AS" > 182 " SELECT DISTINCT(pathelement(name,%d)) AS n," > 183 " %Q||'/'||name AS fn," > 184 " NULL AS u, NULL AS sz, NULL AS mtime" > 185 " FROM filename" > 186 " WHERE name GLOB '%q/*'" > 187 " GROUP BY n", > 188 nD, zD, zD > 189 ); > 190 }else{ > 191 db_multi_exec( > 192 "CREATE TEMP VIEW json_dir_files AS" > 193 " SELECT DISTINCT(pathelement(name,%d)) AS n, " > 194 " %Q||'/'||name AS fn," > 195 " NULL AS u, NULL AS sz, NULL AS mtime" > 196 " FROM filename" > 197 " WHERE name LIKE '%q/%%'" > 198 " GROUP BY n", > 199 nD, zD, zD > 200 ); > 201 } > 202 }else{ > 203 db_multi_exec( > 204 "CREATE TEMP VIEW json_dir_files" > 205 " AS SELECT DISTINCT(pathelement(name,0)) AS n, NULL AS fn" > 206 " FROM filename" > 207 ); > 208 } > 209 > 210 if(zCI){ > 211 db_prepare( &q, "SELECT" > 212 " n as name," > 213 " fn as fullname," > 214 " u as uuid," > 215 " sz as size," > 216 " mtime as mtime " > 217 "FROM json_dir_files ORDER BY n"); > 218 }else{/* UUIDs are all NULL. */ > 219 db_prepare( &q, "SELECT n, fn FROM json_dir_files ORDER BY n"); > 220 } > 221 > 222 zKeyName = cson_new_string("name",4); > 223 zKeyUuid = cson_new_string("uuid",4); > 224 zKeyIsDir = cson_new_string("isDir",5); > 225 keyStore = cson_new_array(); > 226 cson_array_append( keyStore, cson_string_value(zKeyName) ); > 227 cson_array_append( keyStore, cson_string_value(zKeyUuid) ); > 228 cson_array_append( keyStore, cson_string_value(zKeyIsDir) ); > 229 > 230 if( zCI ){ > 231 zKeySize = cson_new_string("size",4); > 232 cson_array_append( keyStore, cson_string_value(zKeySize) ); > 233 zKeyTime = cson_new_string("timestamp",9); > 234 cson_array_append( keyStore, cson_string_value(zKeyTime) ); > 235 zKeyRaw = cson_new_string("downloadPath",12); > 236 cson_array_append( keyStore, cson_string_value(zKeyRaw) ); > 237 } > 238 zPayload = cson_new_object(); > 239 cson_object_set_s( zPayload, zKeyName, > 240 json_new_string((zD&&*zD) ? zD : "/") ); > 241 if( zUuid ){ > 242 cson_object_set( zPayload, "checkin", json_new_string(zUuid) ); > 243 } > 244 > 245 while( (SQLITE_ROW==db_step(&q)) ){ > 246 cson_value * name = NULL; > 247 char const * n = db_column_text(&q,0); > 248 char const isDir = ('/'==*n); > 249 zEntry = cson_new_object(); > 250 if(!zEntries){ > 251 zEntries = cson_new_array(); > 252 cson_object_set( zPayload, "entries", cson_array_value(zEntries) ); > 253 } > 254 cson_array_append(zEntries, cson_object_value(zEntry) ); > 255 if(isDir){ > 256 name = json_new_string( n+1 ); > 257 cson_object_set_s(zEntry, zKeyIsDir, cson_value_true() ); > 258 } else{ > 259 name = json_new_string( n ); > 260 } > 261 cson_object_set_s(zEntry, zKeyName, name ); > 262 if( zCI && !isDir){ > 263 /* Don't add the uuid/size for dir entries - that data refers to > 264 one of the files in that directory :/. Entries with no > 265 --checkin may refer to N versions, and therefore we cannot > 266 associate a single size and uuid with them (and fetching all > 267 would be overkill for most use cases). > 268 */ > 269 char const * fullName = db_column_text(&q,1); > 270 char const * u = db_column_text(&q,2); > 271 sqlite_int64 const sz = db_column_int64(&q,3); > 272 sqlite_int64 const ts = db_column_int64(&q,4); > 273 cson_object_set_s(zEntry, zKeyUuid, json_new_string( u ) ); > 274 cson_object_set_s(zEntry, zKeySize, > 275 cson_value_new_integer( (cson_int_t)sz )); > 276 cson_object_set_s(zEntry, zKeyTime, > 277 cson_value_new_integer( (cson_int_t)ts )); > 278 cson_object_set_s(zEntry, zKeyRaw, > 279 json_new_string_f("/raw/%T?name=%t", > 280 fullName, u)); > 281 } > 282 } > 283 db_finalize(&q); > 284 if(pM){ > 285 manifest_destroy(pM); > 286 } > 287 cson_free_array( keyStore ); > 288 > 289 free( zUuid ); > 290 free( zD ); > 291 return cson_object_value(zPayload); > 292 } > 293 > 294 /* > 295 ** Implements the /json/dir family of pages/commands. > 296 ** > 297 */ > 298 cson_value * json_page_dir(){ > 299 #if 1 > 300 return json_page_dir_list(); > 301 #else > 302 return json_page_dispatch_helper(&JsonPageDefs_Dir[0]); > 303 #endif > 304 } > 305 > 306 #endif /* FOSSIL_ENABLE_JSON */
Added src/json_finfo.c
> 1 #ifdef FOSSIL_ENABLE_JSON > 2 /* > 3 ** Copyright (c) 2011 D. Richard Hipp > 4 ** > 5 ** This program is free software; you can redistribute it and/or > 6 ** modify it under the terms of the Simplified BSD License (also > 7 ** known as the "2-Clause License" or "FreeBSD License".) > 8 ** > 9 ** This program is distributed in the hope that it will be useful, > 10 ** but without any warranty; without even the implied warranty of > 11 ** merchantability or fitness for a particular purpose. > 12 ** > 13 ** Author contact information: > 14 ** drh@hwaci.com > 15 ** http://www.hwaci.com/drh/ > 16 ** > 17 */ > 18 #include "VERSION.h" > 19 #include "config.h" > 20 #include "json_finfo.h" > 21 > 22 #if INTERFACE > 23 #include "json_detail.h" > 24 #endif > 25 > 26 /* > 27 ** Implements the /json/finfo page/command. > 28 ** > 29 */ > 30 cson_value * json_page_finfo(){ > 31 cson_object * pay = NULL; > 32 cson_array * checkins = NULL; > 33 char const * zFilename = NULL; > 34 Blob sql = empty_blob; > 35 Stmt q = empty_Stmt; > 36 char const * zAfter = NULL; > 37 char const * zBefore = NULL; > 38 int limit = -1; > 39 int currentRow = 0; > 40 char const * zCheckin = NULL; > 41 char sort = -1; > 42 if(!g.perm.Read){ > 43 json_set_err(FSL_JSON_E_DENIED,"Requires 'o' privileges."); > 44 return NULL; > 45 } > 46 json_warn( FSL_JSON_W_UNKNOWN, "Achtung: the output of the finfo command is up > 47 > 48 /* For the "name" argument we have to jump through some hoops to make sure tha > 49 get the fossil-internally-assigned "name" option. > 50 */ > 51 zFilename = json_find_option_cstr2("name",NULL,NULL, g.json.dispatchDepth+1); > 52 if(!zFilename || !*zFilename){ > 53 json_set_err(FSL_JSON_E_MISSING_ARGS, "Missing 'name' parameter."); > 54 return NULL; > 55 } > 56 > 57 if(0==db_int(0,"SELECT 1 FROM filename WHERE name=%Q",zFilename)){ > 58 json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "File entry not found."); > 59 return NULL; > 60 } > 61 > 62 zBefore = json_find_option_cstr("before",NULL,"b"); > 63 zAfter = json_find_option_cstr("after",NULL,"a"); > 64 limit = json_find_option_int("limit",NULL,"n", -1); > 65 zCheckin = json_find_option_cstr("checkin",NULL,"ci"); > 66 > 67 blob_appendf(&sql, > 68 /*0*/ "SELECT b.uuid," > 69 /*1*/ " ci.uuid," > 70 /*2*/ " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* Current file uuid > 71 /*3*/ " cast(strftime('%%s',event.mtime) AS INTEGER)," > 72 /*4*/ " coalesce(event.euser, event.user)," > 73 /*5*/ " coalesce(event.ecomment, event.comment)," > 74 /*6*/ " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */ > 75 /*7*/ " event.bgcolor," > 76 /*8*/ " b.size," > 77 /*9*/ " (mlink.pid==0) AS isNew," > 78 /*10*/ " (mlink.fid==0) AS isDel" > 79 " FROM mlink, blob b, event, blob ci, filename" > 80 " WHERE filename.name=%Q %s" > 81 " AND mlink.fnid=filename.fnid" > 82 " AND b.rid=mlink.fid" > 83 " AND event.objid=mlink.mid" > 84 " AND event.objid=ci.rid", > 85 zFilename, filename_collation() > 86 ); > 87 > 88 if( zCheckin && *zCheckin ){ > 89 char * zU = NULL; > 90 int rc = name_to_uuid2( zCheckin, "ci", &zU ); > 91 /*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/ > 92 if(rc<=0){ > 93 json_set_err((rc<0) ? FSL_JSON_E_AMBIGUOUS_UUID : FSL_JSON_E_RESOURCE_NOT_ > 94 "Checkin UUID %s.", (rc<0) ? "is ambiguous" : "not found"); > 95 blob_reset(&sql); > 96 return NULL; > 97 } > 98 blob_appendf(&sql, " AND ci.uuid='%q'", zU); > 99 free(zU); > 100 }else{ > 101 if( zAfter && *zAfter ){ > 102 blob_appendf(&sql, " AND event.mtime>=julianday('%q')", zAfter); > 103 sort = 1; > 104 }else if( zBefore && *zBefore ){ > 105 blob_appendf(&sql, " AND event.mtime<=julianday('%q')", zBefore); > 106 } > 107 } > 108 > 109 blob_appendf(&sql," ORDER BY event.mtime %s /*sort*/", (sort>0?"ASC":"DESC")); > 110 /*printf("SQL=\n%s\n",blob_str(&sql));*/ > 111 db_prepare(&q, "%s", blob_str(&sql)/*extra %s to avoid double-expanding > 112 SQL escapes*/); > 113 blob_reset(&sql); > 114 > 115 pay = cson_new_object(); > 116 cson_object_set(pay, "name", json_new_string(zFilename)); > 117 if( limit > 0 ){ > 118 cson_object_set(pay, "limit", json_new_int(limit)); > 119 } > 120 checkins = cson_new_array(); > 121 cson_object_set(pay, "checkins", cson_array_value(checkins)); > 122 while( db_step(&q)==SQLITE_ROW ){ > 123 cson_object * row = cson_new_object(); > 124 int const isNew = db_column_int(&q,9); > 125 int const isDel = db_column_int(&q,10); > 126 cson_array_append( checkins, cson_object_value(row) ); > 127 cson_object_set(row, "checkin", json_new_string( db_column_text(&q,1) )); > 128 cson_object_set(row, "uuid", json_new_string( db_column_text(&q,2) )); > 129 /*cson_object_set(row, "parentArtifact", json_new_string( db_column_text(&q, > 130 cson_object_set(row, "timestamp", json_new_int( db_column_int(&q,3) )); > 131 cson_object_set(row, "user", json_new_string( db_column_text(&q,4) )); > 132 cson_object_set(row, "comment", json_new_string( db_column_text(&q,5) )); > 133 /*cson_object_set(row, "bgColor", json_new_string( db_column_text(&q,7) ));* > 134 cson_object_set(row, "size", cson_value_new_integer( (cson_int_t)db_column_i > 135 cson_object_set(row, "state", > 136 json_new_string(json_artifact_status_to_string(isNew,isDel)) > 137 if( (0 < limit) && (++currentRow >= limit) ){ > 138 break; > 139 } > 140 } > 141 db_finalize(&q); > 142 > 143 return pay ? cson_object_value(pay) : NULL; > 144 } > 145 > 146 > 147 > 148 #endif /* FOSSIL_ENABLE_JSON */
Changes to src/json_login.c
50 50 51 Summary: If we check for P("name") first, then P("n"), 51 Summary: If we check for P("name") first, then P("n"), 52 then ONLY a GET param of "name" will match ("n" 52 then ONLY a GET param of "name" will match ("n" 53 is not recognized). If we reverse the order of the 53 is not recognized). If we reverse the order of the 54 checks then both forms work. Strangely enough, the 54 checks then both forms work. Strangely enough, the 55 "p"/"password" check is not affected by this. 55 "p"/"password" check is not affected by this. 56 */ 56 */ 57 char const * name = cson_value_get_cstr(json_payload_property("name")); | 57 char const * name = cson_value_get_cstr(json_req_payload_get("name")); 58 char const * pw = NULL; 58 char const * pw = NULL; 59 char const * anonSeed = NULL; 59 char const * anonSeed = NULL; 60 cson_value * payload = NULL; 60 cson_value * payload = NULL; 61 int uid = 0; 61 int uid = 0; 62 /* reminder to self: Fossil internally (for the sake of /wiki) 62 /* reminder to self: Fossil internally (for the sake of /wiki) 63 interprets paths in the form /foo/bar/baz such that P("name") == 63 interprets paths in the form /foo/bar/baz such that P("name") == 64 "bar/baz". This collides with our name/password checking, and 64 "bar/baz". This collides with our name/password checking, and 65 thus we do some rather elaborate name=... checking. 65 thus we do some rather elaborate name=... checking. 66 */ 66 */ 67 pw = cson_value_get_cstr(json_payload_property("password")); | 67 pw = cson_value_get_cstr(json_req_payload_get("password")); 68 if( !pw ){ 68 if( !pw ){ 69 pw = PD("p",NULL); 69 pw = PD("p",NULL); 70 if( !pw ){ 70 if( !pw ){ 71 pw = PD("password",NULL); 71 pw = PD("password",NULL); 72 } 72 } 73 } 73 } 74 if(!pw){ 74 if(!pw){ ................................................................................................................................................................................ 97 80-digit number. 97 80-digit number. 98 */ 98 */ 99 }; 99 }; 100 static char seedBuffer[SeedBufLen]; 100 static char seedBuffer[SeedBufLen]; 101 cson_value const * jseed = json_getenv(FossilJsonKeys.anonymousSeed); 101 cson_value const * jseed = json_getenv(FossilJsonKeys.anonymousSeed); 102 seedBuffer[0] = 0; 102 seedBuffer[0] = 0; 103 if( !jseed ){ 103 if( !jseed ){ 104 jseed = json_payload_property(FossilJsonKeys.anonymousSeed); | 104 jseed = json_req_payload_get(FossilJsonKeys.anonymousSeed); 105 if( !jseed ){ 105 if( !jseed ){ 106 jseed = json_getenv("cs") /* name used by HTML interface */; 106 jseed = json_getenv("cs") /* name used by HTML interface */; 107 } 107 } 108 } 108 } 109 if(jseed){ 109 if(jseed){ 110 if( cson_value_is_number(jseed) ){ 110 if( cson_value_is_number(jseed) ){ 111 sprintf(seedBuffer, "%"CSON_INT_T_PFMT, cson_value_get_integer(jseed)); 111 sprintf(seedBuffer, "%"CSON_INT_T_PFMT, cson_value_get_integer(jseed));
Changes to src/json_report.c
99 "Missing or invalid 'report' (-r) parameter."); 99 "Missing or invalid 'report' (-r) parameter."); 100 return NULL; 100 return NULL; 101 } 101 } 102 102 103 db_prepare(&q,"SELECT rn AS report," 103 db_prepare(&q,"SELECT rn AS report," 104 " owner AS owner," 104 " owner AS owner," 105 " title AS title," 105 " title AS title," 106 " cast(strftime('%%s',mtime) as int) as mtime," | 106 " cast(strftime('%%s',mtime) as int) as timestamp," 107 " cols as columns," 107 " cols as columns," 108 " sqlcode as sqlCode" 108 " sqlcode as sqlCode" 109 " FROM reportfmt" 109 " FROM reportfmt" 110 " WHERE rn=%d", 110 " WHERE rn=%d", 111 nReport); 111 nReport); 112 if( SQLITE_ROW != db_step(&q) ){ 112 if( SQLITE_ROW != db_step(&q) ){ 113 db_finalize(&q); 113 db_finalize(&q);
Changes to src/json_tag.c
274 }else{ 274 }else{ 275 char const * zSqlBase = /*modified from timeline_query_for_tty()*/ 275 char const * zSqlBase = /*modified from timeline_query_for_tty()*/ 276 " SELECT" 276 " SELECT" 277 #if 0 277 #if 0 278 " blob.rid AS rid," 278 " blob.rid AS rid," 279 #endif 279 #endif 280 " uuid AS uuid," 280 " uuid AS uuid," 281 " cast(strftime('%s',event.mtime) as int) AS mtime," | 281 " cast(strftime('%s',event.mtime) as int) AS timestamp," 282 " coalesce(ecomment,comment) AS comment," 282 " coalesce(ecomment,comment) AS comment," 283 " coalesce(euser,user) AS user," 283 " coalesce(euser,user) AS user," 284 " CASE event.type" 284 " CASE event.type" 285 " WHEN 'ci' THEN 'checkin'" 285 " WHEN 'ci' THEN 'checkin'" 286 " WHEN 'w' THEN 'wiki'" 286 " WHEN 'w' THEN 'wiki'" 287 " WHEN 'e' THEN 'event'" 287 " WHEN 'e' THEN 'event'" 288 " WHEN 't' THEN 'ticket'" 288 " WHEN 't' THEN 'ticket'"
Changes to src/json_timeline.c
33 static const JsonPageDef JsonPageDefs_Timeline[] = { 33 static const JsonPageDef JsonPageDefs_Timeline[] = { 34 /* the short forms are only enabled in CLI mode, to avoid 34 /* the short forms are only enabled in CLI mode, to avoid 35 that we end up with HTTP clients using 3 different names 35 that we end up with HTTP clients using 3 different names 36 for the same requests. 36 for the same requests. 37 */ 37 */ 38 {"branch", json_timeline_branch, 0}, 38 {"branch", json_timeline_branch, 0}, 39 {"checkin", json_timeline_ci, 0}, 39 {"checkin", json_timeline_ci, 0}, 40 {"ci", json_timeline_ci, -1}, < 41 {"t", json_timeline_ticket, -1}, < 42 {"ticket", json_timeline_ticket, 0}, 40 {"ticket", json_timeline_ticket, 0}, 43 {"w", json_timeline_wiki, -1}, < 44 {"wiki", json_timeline_wiki, 0}, 41 {"wiki", json_timeline_wiki, 0}, 45 /* Last entry MUST have a NULL name. */ 42 /* Last entry MUST have a NULL name. */ 46 {NULL,NULL,0} 43 {NULL,NULL,0} 47 }; 44 }; 48 45 49 46 50 /* 47 /* ................................................................................................................................................................................ 98 const char const * json_timeline_query(void){ 95 const char const * json_timeline_query(void){ 99 /* Field order MUST match that from json_timeline_temp_table()!!! */ 96 /* Field order MUST match that from json_timeline_temp_table()!!! */ 100 static const char zBaseSql[] = 97 static const char zBaseSql[] = 101 @ SELECT 98 @ SELECT 102 @ NULL, 99 @ NULL, 103 @ blob.rid, 100 @ blob.rid, 104 @ uuid, 101 @ uuid, 105 @ strftime('%%s',event.mtime), | 102 @ CAST(strftime('%%s',event.mtime) AS INTEGER), 106 @ datetime(event.mtime,'utc'), 103 @ datetime(event.mtime,'utc'), 107 @ coalesce(ecomment, comment), 104 @ coalesce(ecomment, comment), 108 @ coalesce(euser, user), 105 @ coalesce(euser, user), 109 @ blob.rid IN leaf, 106 @ blob.rid IN leaf, 110 @ bgcolor, 107 @ bgcolor, 111 @ event.type, 108 @ event.type, 112 @ (SELECT group_concat(substr(tagname,5), ',') FROM tag, tagxref 109 @ (SELECT group_concat(substr(tagname,5), ',') FROM tag, tagxref ................................................................................................................................................................................ 133 ** 130 ** 134 ** pSql is the target blob to append the query [subset] 131 ** pSql is the target blob to append the query [subset] 135 ** to. 132 ** to. 136 ** 133 ** 137 ** Returns a positive value if it modifies pSql, 0 if it 134 ** Returns a positive value if it modifies pSql, 0 if it 138 ** does not. It returns a negative value if the tag 135 ** does not. It returns a negative value if the tag 139 ** provided to the request was not found (pSql is not modified 136 ** provided to the request was not found (pSql is not modified 140 ** in that case. | 137 ** in that case). 141 ** 138 ** 142 ** If payload is not NULL then on success its "tag" or "branch" 139 ** If payload is not NULL then on success its "tag" or "branch" 143 ** property is set to the tag/branch name found in the request. 140 ** property is set to the tag/branch name found in the request. 144 ** 141 ** 145 ** Only one of "tag" or "branch" modes will work at a time, and if 142 ** Only one of "tag" or "branch" modes will work at a time, and if 146 ** both are specified, which one takes precedence is unspecified. 143 ** both are specified, which one takes precedence is unspecified. 147 */ 144 */ ................................................................................................................................................................................ 231 /* 228 /* 232 ** Tries to figure out a timeline query length limit base on 229 ** Tries to figure out a timeline query length limit base on 233 ** environment parameters. If it can it returns that value, 230 ** environment parameters. If it can it returns that value, 234 ** else it returns some statically defined default value. 231 ** else it returns some statically defined default value. 235 ** 232 ** 236 ** Never returns a negative value. 0 means no limit. 233 ** Never returns a negative value. 0 means no limit. 237 */ 234 */ 238 static int json_timeline_limit(){ | 235 static int json_timeline_limit(int defaultLimit){ 239 static const int defaultLimit = 20; < 240 int limit = -1; 236 int limit = -1; 241 if(!g.isHTTP){/* CLI mode */ 237 if(!g.isHTTP){/* CLI mode */ 242 char const * arg = find_option("limit","n",1); 238 char const * arg = find_option("limit","n",1); 243 if(arg && *arg){ 239 if(arg && *arg){ 244 limit = atoi(arg); 240 limit = atoi(arg); 245 } 241 } 246 } 242 } ................................................................................................................................................................................ 273 blob_append(pSql, "INSERT OR IGNORE INTO json_timeline ", -1); 269 blob_append(pSql, "INSERT OR IGNORE INTO json_timeline ", -1); 274 blob_append(pSql, json_timeline_query(), -1 ); 270 blob_append(pSql, json_timeline_query(), -1 ); 275 blob_appendf(pSql, " AND event.type IN(%Q) ", zEventType); 271 blob_appendf(pSql, " AND event.type IN(%Q) ", zEventType); 276 if( json_timeline_add_tag_branch_clause(pSql, pPayload) < 0 ){ 272 if( json_timeline_add_tag_branch_clause(pSql, pPayload) < 0 ){ 277 return FSL_JSON_E_INVALID_ARGS; 273 return FSL_JSON_E_INVALID_ARGS; 278 } 274 } 279 json_timeline_add_time_clause(pSql); 275 json_timeline_add_time_clause(pSql); 280 limit = json_timeline_limit(); | 276 limit = json_timeline_limit(20); 281 if(limit>=0){ | 277 if(limit>0){ 282 blob_appendf(pSql,"LIMIT %d ",limit); 278 blob_appendf(pSql,"LIMIT %d ",limit); 283 } 279 } 284 if(pPayload){ 280 if(pPayload){ 285 cson_object_set(pPayload, "limit", json_new_int(limit)); 281 cson_object_set(pPayload, "limit", json_new_int(limit)); 286 } 282 } 287 return 0; 283 return 0; 288 } 284 } ................................................................................................................................................................................ 293 ** caller). If no files are associated with it then NULL is returned. 289 ** caller). If no files are associated with it then NULL is returned. 294 */ 290 */ 295 cson_value * json_get_changed_files(int rid){ 291 cson_value * json_get_changed_files(int rid){ 296 cson_value * rowsV = NULL; 292 cson_value * rowsV = NULL; 297 cson_array * rows = NULL; 293 cson_array * rows = NULL; 298 Stmt q = empty_Stmt; 294 Stmt q = empty_Stmt; 299 db_prepare(&q, 295 db_prepare(&q, 300 #if 0 < 301 "SELECT (mlink.pid==0) AS isNew," < 302 " (mlink.fid==0) AS isDel," < 303 " filename.name AS name" < 304 " FROM mlink, filename" < 305 " WHERE mid=%d" < 306 " AND pid!=fid" < 307 " AND filename.fnid=mlink.fnid" < 308 " ORDER BY 3 /*sort*/", < 309 #else < 310 "SELECT (pid==0) AS isnew," 296 "SELECT (pid==0) AS isnew," 311 " (fid==0) AS isdel," 297 " (fid==0) AS isdel," 312 " (SELECT name FROM filename WHERE fnid=mlink.fnid) AS name," 298 " (SELECT name FROM filename WHERE fnid=mlink.fnid) AS name," 313 " (SELECT uuid FROM blob WHERE rid=fid) as uuid," | 299 " blob.uuid as uuid," 314 " (SELECT uuid FROM blob WHERE rid=pid) as prevUuid" | 300 " (SELECT uuid FROM blob WHERE rid=pid) as parent," > 301 " blob.size as size" 315 " FROM mlink" | 302 " FROM mlink, blob" 316 " WHERE mid=%d AND pid!=fid" 303 " WHERE mid=%d AND pid!=fid" > 304 " AND blob.rid=fid " 317 " ORDER BY name /*sort*/", 305 " ORDER BY name /*sort*/", 318 #endif < 319 rid 306 rid 320 ); 307 ); 321 while( (SQLITE_ROW == db_step(&q)) ){ 308 while( (SQLITE_ROW == db_step(&q)) ){ 322 cson_value * rowV = cson_value_new_object(); 309 cson_value * rowV = cson_value_new_object(); 323 cson_object * row = cson_value_get_object(rowV); 310 cson_object * row = cson_value_get_object(rowV); 324 int const isNew = db_column_int(&q,0); 311 int const isNew = db_column_int(&q,0); 325 int const isDel = db_column_int(&q,1); 312 int const isDel = db_column_int(&q,1); ................................................................................................................................................................................ 328 rowsV = cson_value_new_array(); 315 rowsV = cson_value_new_array(); 329 rows = cson_value_get_array(rowsV); 316 rows = cson_value_get_array(rowsV); 330 } 317 } 331 cson_array_append( rows, rowV ); 318 cson_array_append( rows, rowV ); 332 cson_object_set(row, "name", json_new_string(db_column_text(&q,2))); 319 cson_object_set(row, "name", json_new_string(db_column_text(&q,2))); 333 cson_object_set(row, "uuid", json_new_string(db_column_text(&q,3))); 320 cson_object_set(row, "uuid", json_new_string(db_column_text(&q,3))); 334 if(!isNew){ 321 if(!isNew){ 335 cson_object_set(row, "prevUuid", json_new_string(db_column_text(&q,4))); | 322 cson_object_set(row, "parent", json_new_string(db_column_text(&q,4))); 336 } 323 } > 324 cson_object_set(row, "size", json_new_int(db_column_int(&q,5))); > 325 337 cson_object_set(row, "state", 326 cson_object_set(row, "state", 338 json_new_string(isNew | 327 json_new_string(json_artifact_status_to_string(isNew,isDel)) 339 ? "added" < 340 : (isDel < 341 ? "removed" < 342 : "modified"))); < 343 zDownload = mprintf("/raw/%s?name=%s", 328 zDownload = mprintf("/raw/%s?name=%s", 344 /* reminder: g.zBaseURL is of course not set for CLI mod 329 /* reminder: g.zBaseURL is of course not set for CLI mod 345 db_column_text(&q,2), 330 db_column_text(&q,2), 346 db_column_text(&q,3)); 331 db_column_text(&q,3)); 347 cson_object_set(row, "downloadPath", json_new_string(zDownload)); 332 cson_object_set(row, "downloadPath", json_new_string(zDownload)); 348 free(zDownload); 333 free(zDownload); 349 } 334 } ................................................................................................................................................................................ 351 return rowsV; 336 return rowsV; 352 } 337 } 353 338 354 static cson_value * json_timeline_branch(){ 339 static cson_value * json_timeline_branch(){ 355 cson_value * pay = NULL; 340 cson_value * pay = NULL; 356 Blob sql = empty_blob; 341 Blob sql = empty_blob; 357 Stmt q = empty_Stmt; 342 Stmt q = empty_Stmt; > 343 int limit = 0; 358 if(!g.perm.Read){ 344 if(!g.perm.Read){ 359 json_set_err(FSL_JSON_E_DENIED, 345 json_set_err(FSL_JSON_E_DENIED, 360 "Requires 'o' permissions."); 346 "Requires 'o' permissions."); 361 return NULL; 347 return NULL; 362 } 348 } 363 json_timeline_temp_table(); 349 json_timeline_temp_table(); 364 blob_append(&sql, 350 blob_append(&sql, 365 "SELECT" 351 "SELECT" 366 " blob.rid AS rid," 352 " blob.rid AS rid," 367 " uuid AS uuid," 353 " uuid AS uuid," 368 " datetime(event.mtime,'utc') as mtime," | 354 " CAST(strftime('%s',event.mtime) AS INTEGER) as timestamp," 369 " coalesce(ecomment, comment) as comment," 355 " coalesce(ecomment, comment) as comment," 370 " coalesce(euser, user) as user," 356 " coalesce(euser, user) as user," 371 " blob.rid IN leaf as isLeaf," 357 " blob.rid IN leaf as isLeaf," 372 " bgcolor as bgColor" 358 " bgcolor as bgColor" 373 " FROM event JOIN blob" 359 " FROM event JOIN blob" 374 " WHERE blob.rid=event.objid", 360 " WHERE blob.rid=event.objid", 375 -1); 361 -1); ................................................................................................................................................................................ 376 362 377 blob_appendf(&sql, 363 blob_appendf(&sql, 378 " AND event.type='ci'" 364 " AND event.type='ci'" 379 " AND blob.rid IN (SELECT rid FROM tagxref" 365 " AND blob.rid IN (SELECT rid FROM tagxref" 380 " WHERE tagtype>0 AND tagid=%d AND srcid!=0)" 366 " WHERE tagtype>0 AND tagid=%d AND srcid!=0)" 381 " ORDER BY event.mtime DESC", 367 " ORDER BY event.mtime DESC", 382 TAG_BRANCH); 368 TAG_BRANCH); > 369 limit = json_timeline_limit(20); > 370 if(limit>0){ > 371 blob_appendf(&sql," LIMIT %d ",limit); > 372 } 383 db_prepare(&q,"%s", blob_str(&sql)); 373 db_prepare(&q,"%s", blob_str(&sql)); 384 blob_reset(&sql); 374 blob_reset(&sql); 385 pay = json_stmt_to_array_of_obj(&q, NULL); 375 pay = json_stmt_to_array_of_obj(&q, NULL); 386 db_finalize(&q); 376 db_finalize(&q); 387 assert(NULL != pay); 377 assert(NULL != pay); 388 if(pay){ 378 if(pay){ 389 /* get the array-form tags of each record. */ 379 /* get the array-form tags of each record. */ 390 cson_string * tags = cson_new_string("tags",4); 380 cson_string * tags = cson_new_string("tags",4); 391 cson_string * isLeaf = cson_new_string("isLeaf",6); 381 cson_string * isLeaf = cson_new_string("isLeaf",6); 392 cson_value_add_reference( cson_string_value(tags) ); < 393 cson_value_add_reference( cson_string_value(isLeaf) ); < 394 cson_array * ar = cson_value_get_array(pay); 382 cson_array * ar = cson_value_get_array(pay); > 383 cson_object * outer = NULL; 395 unsigned int i = 0; 384 unsigned int i = 0; 396 unsigned int len = cson_array_length_get(ar); 385 unsigned int len = cson_array_length_get(ar); > 386 cson_value_add_reference( cson_string_value(tags) ); > 387 cson_value_add_reference( cson_string_value(isLeaf) ); 397 for( ; i < len; ++i ){ 388 for( ; i < len; ++i ){ 398 cson_object * row = cson_value_get_object(cson_array_get(ar,i)); 389 cson_object * row = cson_value_get_object(cson_array_get(ar,i)); 399 int rid = cson_value_get_integer(cson_object_get(row,"rid")); 390 int rid = cson_value_get_integer(cson_object_get(row,"rid")); 400 if(row>0) { | 391 assert( rid > 0 ); 401 cson_object_set_s(row, tags, json_tags_for_checkin_rid(rid,0)); | 392 cson_object_set_s(row, tags, json_tags_for_checkin_rid(rid,0)); > 393 cson_object_set_s(row, isLeaf, 402 cson_object_set_s(row, isLeaf, json_value_to_bool(cson_object_get(row,"i | 394 json_value_to_bool(cson_object_get(row,"isLeaf"))); 403 } < > 395 cson_object_set(row, "rid", NULL) > 396 /* remove rid - we don't really want it to be public */; 404 } 397 } 405 cson_value_free( cson_string_value(tags) ); 398 cson_value_free( cson_string_value(tags) ); 406 cson_value_free( cson_string_value(isLeaf) ); 399 cson_value_free( cson_string_value(isLeaf) ); 407 } | 400 408 < > 401 /* now we wrap the payload in an outer shell, for consistency with 409 goto end; | 402 other /json/timeline/xyz APIs... > 403 */ > 404 outer = cson_new_object(); > 405 if(limit>0){ > 406 cson_object_set( outer, "limit", json_new_int(limit) ); 410 | 407 } 411 assert( 0 != g.json.resultCode ); < 412 cson_value_free(pay); < > 408 cson_object_set( outer, "timeline", pay ); > 409 pay = cson_object_value(outer); 413 | 410 } 414 end: < 415 return pay; 411 return pay; 416 } 412 } 417 413 418 /* 414 /* 419 ** Implementation of /json/timeline/ci. 415 ** Implementation of /json/timeline/ci. 420 ** 416 ** 421 ** Still a few TODOs (like figuring out how to structure 417 ** Still a few TODOs (like figuring out how to structure ................................................................................................................................................................................ 428 cson_value * listV = NULL; 424 cson_value * listV = NULL; 429 cson_array * list = NULL; 425 cson_array * list = NULL; 430 int check = 0; 426 int check = 0; 431 char showFiles = -1/*magic number*/; 427 char showFiles = -1/*magic number*/; 432 Stmt q = empty_Stmt; 428 Stmt q = empty_Stmt; 433 char warnRowToJsonFailed = 0; 429 char warnRowToJsonFailed = 0; 434 Blob sql = empty_blob; 430 Blob sql = empty_blob; 435 if( !g.perm.Read ){ | 431 if( !g.perm.History ){ 436 /* IMO this falls more under the category of g.perm.History, but | 432 /* Reminder to self: HTML impl requires 'o' (Read) 437 i'm following the original timeline impl here. | 433 rights. 438 */ 434 */ 439 json_set_err( FSL_JSON_E_DENIED, "Checkin timeline requires 'o' access." ); | 435 json_set_err( FSL_JSON_E_DENIED, "Checkin timeline requires 'h' access." ); 440 return NULL; 436 return NULL; 441 } 437 } 442 showFiles = json_find_option_bool("files",NULL,"f",0); 438 showFiles = json_find_option_bool("files",NULL,"f",0); 443 payV = cson_value_new_object(); 439 payV = cson_value_new_object(); 444 pay = cson_value_get_object(payV); 440 pay = cson_value_get_object(payV); 445 check = json_timeline_setup_sql( "ci", &sql, pay ); 441 check = json_timeline_setup_sql( "ci", &sql, pay ); 446 if(check){ 442 if(check){ ................................................................................................................................................................................ 459 tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); 455 tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); 460 SET("timelineSql"); 456 SET("timelineSql"); 461 #endif 457 #endif 462 db_multi_exec(blob_buffer(&sql)); 458 db_multi_exec(blob_buffer(&sql)); 463 blob_reset(&sql); 459 blob_reset(&sql); 464 db_prepare(&q, "SELECT " 460 db_prepare(&q, "SELECT " 465 " rid AS rid" 461 " rid AS rid" 466 #if 0 < 467 " uuid AS uuid," < 468 " mtime AS timestamp," < 469 # if 0 < 470 " timestampString AS timestampString," < 471 # endif < 472 " comment AS comment, " < 473 " user AS user," < 474 " isLeaf AS isLeaf," /*FIXME: convert to JSON bool */ < 475 " bgColor AS bgColor," /* why always null? */ < 476 " eventType AS eventType" < 477 # if 0 < 478 " tags AS tags" < 479 /*tagId is always null?*/ < 480 " tagId AS tagId" < 481 # endif < 482 #endif < 483 " FROM json_timeline" 462 " FROM json_timeline" 484 " ORDER BY rowid"); 463 " ORDER BY rowid"); 485 listV = cson_value_new_array(); 464 listV = cson_value_new_array(); 486 list = cson_value_get_array(listV); 465 list = cson_value_get_array(listV); 487 tmp = listV; 466 tmp = listV; 488 SET("timeline"); 467 SET("timeline"); 489 while( (SQLITE_ROW == db_step(&q) )){ 468 while( (SQLITE_ROW == db_step(&q) )){ ................................................................................................................................................................................ 516 ** Implementation of /json/timeline/wiki. 495 ** Implementation of /json/timeline/wiki. 517 ** 496 ** 518 */ 497 */ 519 cson_value * json_timeline_wiki(){ 498 cson_value * json_timeline_wiki(){ 520 /* This code is 95% the same as json_timeline_ci(), by the way. */ 499 /* This code is 95% the same as json_timeline_ci(), by the way. */ 521 cson_value * payV = NULL; 500 cson_value * payV = NULL; 522 cson_object * pay = NULL; 501 cson_object * pay = NULL; 523 cson_value * tmp = NULL; < 524 cson_array * list = NULL; 502 cson_array * list = NULL; 525 int check = 0; 503 int check = 0; 526 Stmt q = empty_Stmt; 504 Stmt q = empty_Stmt; 527 Blob sql = empty_blob; 505 Blob sql = empty_blob; 528 if( !g.perm.RdWiki && !g.perm.Read ){ 506 if( !g.perm.RdWiki && !g.perm.Read ){ 529 json_set_err( FSL_JSON_E_DENIED, "Wiki timeline requires 'o' or 'j' access." 507 json_set_err( FSL_JSON_E_DENIED, "Wiki timeline requires 'o' or 'j' access." 530 return NULL; 508 return NULL; ................................................................................................................................................................................ 533 pay = cson_value_get_object(payV); 511 pay = cson_value_get_object(payV); 534 check = json_timeline_setup_sql( "w", &sql, pay ); 512 check = json_timeline_setup_sql( "w", &sql, pay ); 535 if(check){ 513 if(check){ 536 json_set_err(check, "Query initialization failed."); 514 json_set_err(check, "Query initialization failed."); 537 goto error; 515 goto error; 538 } 516 } 539 517 540 #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ < 541 json_set_err((cson_rc.AllocError==check) \ < 542 ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN, \ < 543 "Object property insertion failed."); \ < 544 goto error;\ < 545 } (void)0 < 546 #if 0 518 #if 0 547 /* only for testing! */ 519 /* only for testing! */ 548 tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); 520 tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); 549 SET("timelineSql"); 521 SET("timelineSql"); 550 #endif 522 #endif 551 db_multi_exec(blob_buffer(&sql)); 523 db_multi_exec(blob_buffer(&sql)); 552 blob_reset(&sql); 524 blob_reset(&sql); 553 db_prepare(&q, "SELECT rid AS rid," | 525 db_prepare(&q, "SELECT" 554 " uuid AS uuid," 526 " uuid AS uuid," 555 " mtime AS timestamp," 527 " mtime AS timestamp," 556 #if 0 528 #if 0 557 " timestampString AS timestampString," 529 " timestampString AS timestampString," 558 #endif 530 #endif 559 " comment AS comment, " 531 " comment AS comment, " 560 " user AS user," 532 " user AS user," ................................................................................................................................................................................ 565 a JSON array*/ 537 a JSON array*/ 566 " tagId AS tagId," 538 " tagId AS tagId," 567 #endif 539 #endif 568 " FROM json_timeline" 540 " FROM json_timeline" 569 " ORDER BY rowid", 541 " ORDER BY rowid", 570 -1); 542 -1); 571 list = cson_new_array(); 543 list = cson_new_array(); 572 tmp = cson_array_value(list); < 573 SET("timeline"); < 574 json_stmt_to_array_of_obj(&q, list); 544 json_stmt_to_array_of_obj(&q, list); 575 #undef SET < > 545 cson_object_set(pay, "timeline", cson_array_value(list)); 576 goto ok; 546 goto ok; 577 error: 547 error: 578 assert( 0 != g.json.resultCode ); 548 assert( 0 != g.json.resultCode ); 579 cson_value_free(payV); 549 cson_value_free(payV); 580 payV = NULL; 550 payV = NULL; 581 ok: 551 ok: 582 db_finalize(&q); 552 db_finalize(&q); ................................................................................................................................................................................ 648 tmp = listV; 618 tmp = listV; 649 SET("timeline"); 619 SET("timeline"); 650 while( (SQLITE_ROW == db_step(&q) )){ 620 while( (SQLITE_ROW == db_step(&q) )){ 651 /* convert each row into a JSON object...*/ 621 /* convert each row into a JSON object...*/ 652 int rc; 622 int rc; 653 int const rid = db_column_int(&q,0); 623 int const rid = db_column_int(&q,0); 654 Manifest * pMan = NULL; 624 Manifest * pMan = NULL; > 625 cson_value * rowV; > 626 cson_object * row; > 627 /*printf("rid=%d\n",rid);*/ > 628 pMan = manifest_get(rid, CFTYPE_TICKET); > 629 if(!pMan){ > 630 /* this might be an attachment? i'm seeing this with > 631 rid 15380, uuid [1292fef05f2472108]. > 632 > 633 /json/artifact/1292fef05f2472108 returns not-found, > 634 probably because we haven't added artifact/ticket > 635 yet(?). > 636 */ > 637 continue; > 638 } > 639 655 cson_value * rowV = cson_sqlite3_row_to_object(q.pStmt); | 640 rowV = cson_sqlite3_row_to_object(q.pStmt); 656 cson_object * row = cson_value_get_object(rowV); | 641 row = cson_value_get_object(rowV); 657 if(!row){ 642 if(!row){ > 643 manifest_destroy(pMan); 658 json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, 644 json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, 659 "Could not convert at least one timeline result row to JSON." ) 645 "Could not convert at least one timeline result row to JSON." ) 660 continue; 646 continue; 661 } 647 } 662 pMan = manifest_get(rid, CFTYPE_TICKET); < 663 assert( pMan && "Manifest is NULL!?!" ); < 664 if( pMan ){ < 665 /* FIXME: certainly there's a more efficient way for use to get | 648 /* FIXME: certainly there's a more efficient way for use to get 666 the ticket UUIDs? | 649 the ticket UUIDs? 667 */ | 650 */ 668 cson_object_set(row,"ticketUuid",json_new_string(pMan->zTicketUuid)); | 651 cson_object_set(row,"ticketUuid",json_new_string(pMan->zTicketUuid)); 669 manifest_destroy(pMan); | 652 manifest_destroy(pMan); 670 } < 671 rc = cson_array_append( list, rowV ); 653 rc = cson_array_append( list, rowV ); 672 if( 0 != rc ){ 654 if( 0 != rc ){ 673 cson_value_free(rowV); 655 cson_value_free(rowV); 674 g.json.resultCode = (cson_rc.AllocError==rc) 656 g.json.resultCode = (cson_rc.AllocError==rc) 675 ? FSL_JSON_E_ALLOC 657 ? FSL_JSON_E_ALLOC 676 : FSL_JSON_E_UNKNOWN; 658 : FSL_JSON_E_UNKNOWN; 677 goto error; 659 goto error;
Changes to src/json_user.c
22 #if INTERFACE 22 #if INTERFACE 23 #include "json_detail.h" 23 #include "json_detail.h" 24 #endif 24 #endif 25 25 26 static cson_value * json_user_get(); 26 static cson_value * json_user_get(); 27 static cson_value * json_user_list(); 27 static cson_value * json_user_list(); 28 static cson_value * json_user_save(); 28 static cson_value * json_user_save(); 29 #if 0 < 30 static cson_value * json_user_create(); < 31 < 32 #endif < 33 29 34 /* 30 /* 35 ** Mapping of /json/user/XXX commands/paths to callbacks. 31 ** Mapping of /json/user/XXX commands/paths to callbacks. 36 */ 32 */ 37 static const JsonPageDef JsonPageDefs_User[] = { 33 static const JsonPageDef JsonPageDefs_User[] = { 38 {"create", json_page_nyi, 0}, < 39 {"save", json_user_save, 0}, 34 {"save", json_user_save, 0}, 40 {"get", json_user_get, 0}, 35 {"get", json_user_get, 0}, 41 {"list", json_user_list, 0}, 36 {"list", json_user_list, 0}, 42 /* Last entry MUST have a NULL name. */ 37 /* Last entry MUST have a NULL name. */ 43 {NULL,NULL,0} 38 {NULL,NULL,0} 44 }; 39 }; 45 40 ................................................................................................................................................................................ 50 */ 45 */ 51 cson_value * json_page_user(){ 46 cson_value * json_page_user(){ 52 return json_page_dispatch_helper(&JsonPageDefs_User[0]); 47 return json_page_dispatch_helper(&JsonPageDefs_User[0]); 53 } 48 } 54 49 55 50 56 /* 51 /* 57 ** Impl of /json/user/list. Requires admin rights. | 52 ** Impl of /json/user/list. Requires admin/setup rights. 58 */ 53 */ 59 static cson_value * json_user_list(){ 54 static cson_value * json_user_list(){ 60 cson_value * payV = NULL; 55 cson_value * payV = NULL; 61 Stmt q; 56 Stmt q; 62 if(!g.perm.Admin){ | 57 if(!g.perm.Admin && !g.perm.Setup){ 63 g.json.resultCode = FSL_JSON_E_DENIED; | 58 json_set_err(FSL_JSON_E_DENIED, > 59 "Requires 'a' or 's' privileges."); 64 return NULL; 60 return NULL; 65 } 61 } 66 db_prepare(&q,"SELECT uid AS uid," 62 db_prepare(&q,"SELECT uid AS uid," 67 " login AS name," 63 " login AS name," 68 " cap AS capabilities," 64 " cap AS capabilities," 69 " info AS info," 65 " info AS info," 70 " mtime AS mtime" | 66 " mtime AS timestamp" 71 " FROM user ORDER BY login"); 67 " FROM user ORDER BY login"); 72 payV = json_stmt_to_array_of_obj(&q, NULL); 68 payV = json_stmt_to_array_of_obj(&q, NULL); 73 db_finalize(&q); 69 db_finalize(&q); 74 if(NULL == payV){ 70 if(NULL == payV){ 75 json_set_err(FSL_JSON_E_UNKNOWN, 71 json_set_err(FSL_JSON_E_UNKNOWN, 76 "Could not convert user list to JSON."); 72 "Could not convert user list to JSON."); 77 } 73 } ................................................................................................................................................................................ 87 static cson_value * json_load_user_by_name(char const * zName){ 83 static cson_value * json_load_user_by_name(char const * zName){ 88 cson_value * u = NULL; 84 cson_value * u = NULL; 89 Stmt q; 85 Stmt q; 90 db_prepare(&q,"SELECT uid AS uid," 86 db_prepare(&q,"SELECT uid AS uid," 91 " login AS name," 87 " login AS name," 92 " cap AS capabilities," 88 " cap AS capabilities," 93 " info AS info," 89 " info AS info," 94 " mtime AS mtime" | 90 " mtime AS timestamp" 95 " FROM user" 91 " FROM user" 96 " WHERE login=%Q", 92 " WHERE login=%Q", 97 zName); 93 zName); 98 if( (SQLITE_ROW == db_step(&q)) ){ 94 if( (SQLITE_ROW == db_step(&q)) ){ 99 u = cson_sqlite3_row_to_object(q.pStmt); 95 u = cson_sqlite3_row_to_object(q.pStmt); 100 } 96 } 101 db_finalize(&q); 97 db_finalize(&q); 102 return u; 98 return u; 103 } 99 } 104 100 105 /* 101 /* 106 ** Identical to _load_user_by_name(), but expects a user ID. Returns | 102 ** Identical to json_load_user_by_name(), but expects a user ID. Returns 107 ** NULL if no user found with that ID. 103 ** NULL if no user found with that ID. 108 */ 104 */ 109 static cson_value * json_load_user_by_id(int uid){ 105 static cson_value * json_load_user_by_id(int uid){ 110 cson_value * u = NULL; 106 cson_value * u = NULL; 111 Stmt q; 107 Stmt q; 112 db_prepare(&q,"SELECT uid AS uid," 108 db_prepare(&q,"SELECT uid AS uid," 113 " login AS name," 109 " login AS name," 114 " cap AS capabilities," 110 " cap AS capabilities," 115 " info AS info," 111 " info AS info," 116 " mtime AS mtime" | 112 " mtime AS timestamp" 117 " FROM user" 113 " FROM user" 118 " WHERE uid=%d", 114 " WHERE uid=%d", 119 uid); 115 uid); 120 if( (SQLITE_ROW == db_step(&q)) ){ 116 if( (SQLITE_ROW == db_step(&q)) ){ 121 u = cson_sqlite3_row_to_object(q.pStmt); 117 u = cson_sqlite3_row_to_object(q.pStmt); 122 } 118 } 123 db_finalize(&q); 119 db_finalize(&q); 124 return u; 120 return u; 125 } 121 } 126 122 127 123 128 /* 124 /* 129 ** Impl of /json/user/get. Requires admin rights. | 125 ** Impl of /json/user/get. Requires admin or setup rights. 130 */ 126 */ 131 static cson_value * json_user_get(){ 127 static cson_value * json_user_get(){ 132 cson_value * payV = NULL; 128 cson_value * payV = NULL; 133 char const * pUser = NULL; 129 char const * pUser = NULL; 134 if(!g.perm.Admin){ | 130 if(!g.perm.Admin && !g.perm.Setup){ 135 json_set_err(FSL_JSON_E_DENIED, 131 json_set_err(FSL_JSON_E_DENIED, 136 "Requires 'a' privileges."); | 132 "Requires 'a' or 's' privileges."); 137 return NULL; 133 return NULL; 138 } 134 } 139 pUser = json_command_arg(g.json.dispatchDepth+1); | 135 pUser = json_find_option_cstr2("name", NULL, NULL, g.json.dispatchDepth+1); 140 if( g.isHTTP && (!pUser || !*pUser) ){ < 141 pUser = json_getenv_cstr("name") < 142 /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ < 143 if we pass the name as part of the path, which is why we check < 144 with json_command_path() before trying to get("name"). < 145 */; < 146 } < 147 if(!pUser || !*pUser){ 136 if(!pUser || !*pUser){ 148 json_set_err(FSL_JSON_E_MISSING_ARGS,"Missing 'name' property."); 137 json_set_err(FSL_JSON_E_MISSING_ARGS,"Missing 'name' property."); 149 return NULL; 138 return NULL; 150 } 139 } 151 payV = json_load_user_by_name(pUser); 140 payV = json_load_user_by_name(pUser); 152 if(!payV){ 141 if(!payV){ 153 json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,"User not found."); 142 json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,"User not found."); ................................................................................................................................................................................ 162 ** At least one of (name, uid) must be included. All others are 151 ** At least one of (name, uid) must be included. All others are 163 ** optional and their db fields will not be updated if those fields 152 ** optional and their db fields will not be updated if those fields 164 ** are not included in pUser. 153 ** are not included in pUser. 165 ** 154 ** 166 ** If uid is specified then name may refer to a _new_ name 155 ** If uid is specified then name may refer to a _new_ name 167 ** for a user, otherwise the name must refer to an existing user. 156 ** for a user, otherwise the name must refer to an existing user. 168 ** If uid=-1 then the name must be specified and a new user is 157 ** If uid=-1 then the name must be specified and a new user is 169 ** created (failes if one already exists). | 158 ** created (fails if one already exists). 170 ** 159 ** 171 ** If uid is not set, this function might modify pUser to contain the 160 ** If uid is not set, this function might modify pUser to contain the 172 ** db-found (or inserted) user ID. 161 ** db-found (or inserted) user ID. 173 ** 162 ** 174 ** On error g.json's error state is set one of the FSL_JSON_E_xxx | 163 ** On error g.json's error state is set and one of the FSL_JSON_E_xxx 175 ** values from FossilJsonCodes is returned. 164 ** values from FossilJsonCodes is returned. 176 ** 165 ** 177 ** On success the db record for the given user is updated. 166 ** On success the db record for the given user is updated. 178 ** 167 ** 179 ** Requires either Admin, Setup, or Password access. Non-admin/setup 168 ** Requires either Admin, Setup, or Password access. Non-admin/setup 180 ** users can only change their own information. Non-setup users may 169 ** users can only change their own information. Non-setup users may 181 ** not modify the 's' permission. Admin users without setup 170 ** not modify the 's' permission. Admin users without setup ................................................................................................................................................................................ 195 #undef CSTR 184 #undef CSTR 196 cson_int_t uid = cson_value_get_integer( cson_object_get(pUser, "uid") ); 185 cson_int_t uid = cson_value_get_integer( cson_object_get(pUser, "uid") ); 197 char const tgtHasSetup = zCap && (NULL!=strchr(zCap, 's')); 186 char const tgtHasSetup = zCap && (NULL!=strchr(zCap, 's')); 198 char tgtHadSetup = 0; 187 char tgtHadSetup = 0; 199 Blob sql = empty_blob; 188 Blob sql = empty_blob; 200 Stmt q = empty_Stmt; 189 Stmt q = empty_Stmt; 201 190 > 191 #if 0 202 if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){ 192 if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){ 203 return json_set_err( FSL_JSON_E_DENIED, 193 return json_set_err( FSL_JSON_E_DENIED, 204 "Password change requires 'a', 's', " 194 "Password change requires 'a', 's', " 205 "or 'p' permissions."); 195 "or 'p' permissions."); 206 } 196 } 207 | 197 #endif 208 if(uid<=0 && (!zName||!*zName)){ 198 if(uid<=0 && (!zName||!*zName)){ 209 return json_set_err(FSL_JSON_E_MISSING_ARGS, 199 return json_set_err(FSL_JSON_E_MISSING_ARGS, 210 "One of 'uid' or 'name' is required."); 200 "One of 'uid' or 'name' is required."); 211 }else if(uid>0){ 201 }else if(uid>0){ 212 zNameFree = db_text(NULL, "SELECT login FROM user WHERE uid=%d",uid); 202 zNameFree = db_text(NULL, "SELECT login FROM user WHERE uid=%d",uid); 213 if(!zNameFree){ 203 if(!zNameFree){ 214 return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, 204 return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, 215 "No login found for uid %d.", uid); 205 "No login found for uid %d.", uid); 216 } 206 } 217 zName = zNameFree; 207 zName = zNameFree; 218 }else if(-1==uid){ 208 }else if(-1==uid){ 219 /* try to create a new user */ 209 /* try to create a new user */ 220 if(!g.perm.Admin && !g.perm.Setup){ 210 if(!g.perm.Admin && !g.perm.Setup){ 221 return json_set_err(FSL_JSON_E_DENIED, | 211 json_set_err(FSL_JSON_E_DENIED, 222 "Requires 'a' or 's' privileges."); | 212 "Requires 'a' or 's' privileges."); > 213 goto error; 223 }else if(!zName || !*zName){ 214 }else if(!zName || !*zName){ 224 return json_set_err(FSL_JSON_E_MISSING_ARGS, | 215 json_set_err(FSL_JSON_E_MISSING_ARGS, 225 "No name specified for new user."); | 216 "No name specified for new user."); > 217 goto error; 226 }else if( db_exists("SELECT 1 FROM user WHERE login=%Q", zName) ){ 218 }else if( db_exists("SELECT 1 FROM user WHERE login=%Q", zName) ){ 227 return json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS, | 219 json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS, 228 "User %s already exists.", zName); | 220 "User %s already exists.", zName); > 221 goto error; 229 }else{ 222 }else{ 230 Stmt ins = empty_Stmt; 223 Stmt ins = empty_Stmt; 231 db_prepare(&ins, "INSERT INTO user (login) VALUES(%Q)",zName); 224 db_prepare(&ins, "INSERT INTO user (login) VALUES(%Q)",zName); 232 db_step( &ins ); 225 db_step( &ins ); 233 db_finalize(&ins); 226 db_finalize(&ins); 234 uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); 227 uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); 235 assert(uid>0); 228 assert(uid>0); 236 zNameNew = zName; 229 zNameNew = zName; 237 cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); 230 cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); 238 } 231 } 239 }else{ 232 }else{ 240 uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); 233 uid = db_int(0,"SELECT uid FROM user WHERE login=%Q", zName); 241 if(uid<=0){ 234 if(uid<=0){ 242 return json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, | 235 json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, 243 "No login found for user [%s].", zName); | 236 "No login found for user [%s].", zName); > 237 goto error; 244 } 238 } 245 cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); 239 cson_object_set( pUser, "uid", cson_value_new_integer(uid) ); 246 } 240 } 247 241 248 /* Maintenance note: all error-returns from here on out should go 242 /* Maintenance note: all error-returns from here on out should go 249 via 'goto error' in order to clean up. 243 via 'goto error' in order to clean up. 250 */ 244 */ 251 245 252 if(uid != g.userUid){ 246 if(uid != g.userUid){ 253 if(!g.perm.Admin && !g.perm.Setup){ 247 if(!g.perm.Admin && !g.perm.Setup){ 254 json_set_err(FSL_JSON_E_DENIED, 248 json_set_err(FSL_JSON_E_DENIED, 255 "Changing another user's data requires " 249 "Changing another user's data requires " 256 "'a' or 's' privileges."); 250 "'a' or 's' privileges."); > 251 goto error; 257 } 252 } 258 } 253 } 259 /* check if the target uid currently has setup rights. */ 254 /* check if the target uid currently has setup rights. */ 260 tgtHadSetup = db_int(0,"SELECT 1 FROM user where uid=%d" 255 tgtHadSetup = db_int(0,"SELECT 1 FROM user where uid=%d" 261 " AND cap GLOB '*s*'", uid); 256 " AND cap GLOB '*s*'", uid); 262 257 263 if((tgtHasSetup || tgtHadSetup) && !g.perm.Setup){ 258 if((tgtHasSetup || tgtHadSetup) && !g.perm.Setup){ 264 /* 259 /* 265 Do not allow a non-setup user to set or remove setup 260 Do not allow a non-setup user to set or remove setup 266 privileges. setup.c uses similar logic. 261 privileges. setup.c uses similar logic. 267 */ 262 */ 268 return json_set_err(FSL_JSON_E_DENIED, | 263 json_set_err(FSL_JSON_E_DENIED, 269 "Modifying 's' users/privileges requires " | 264 "Modifying 's' users/privileges requires " 270 "'s' privileges."); | 265 "'s' privileges."); > 266 goto error; 271 } 267 } 272 /* 268 /* 273 Potential todo: do not allow a setup user to remove 's' from 269 Potential todo: do not allow a setup user to remove 's' from 274 himself, to avoid locking himself out? 270 himself, to avoid locking himself out? 275 */ 271 */ 276 272 277 blob_append(&sql, "UPDATE USER SET",-1 ); | 273 blob_append(&sql, "UPDATE user SET",-1 ); 278 blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1); 274 blob_append(&sql, " mtime=cast(strftime('%s') AS INTEGER)", -1); 279 275 280 if((uid>0) && zNameNew){ 276 if((uid>0) && zNameNew){ 281 /* Check for name change... */ 277 /* Check for name change... */ > 278 if(0!=strcmp(zName,zNameNew)){ 282 if( (!g.perm.Admin && !g.perm.Setup) | 279 if( (!g.perm.Admin && !g.perm.Setup) 283 && zNameNew && (zName != zNameNew) | 280 && (zName != zNameNew)){ 284 && (0!=strcmp(zNameNew,zName))){ < 285 json_set_err( FSL_JSON_E_DENIED, | 281 json_set_err( FSL_JSON_E_DENIED, 286 "Modifying user names requires 'a' or 's' privileges."); | 282 "Modifying user names requires 'a' or 's' privileges."); 287 goto error; | 283 goto error; 288 } | 284 } 289 forceLogout = cson_value_true() | 285 forceLogout = cson_value_true() 290 /* reminders: 1) does not allocate. 286 /* reminders: 1) does not allocate. 291 2) we do this because changing a name | 287 2) we do this because changing a name 292 invalidates any login token because the old name | 288 invalidates any login token because the old name 293 is part of the token hash. | 289 is part of the token hash. 294 */; 290 */; 295 blob_appendf(&sql, ", login=%Q", zNameNew); | 291 blob_appendf(&sql, ", login=%Q", zNameNew); 296 ++gotFields; | 292 ++gotFields; 297 } | 293 } 298 | 294 } > 295 299 if( zCap ){ | 296 if( zCap && *zCap ){ > 297 if(!g.perm.Admin || !g.perm.Setup){ > 298 /* we "could" arguably silently ignore cap in this case. */ > 299 json_set_err(FSL_JSON_E_DENIED, > 300 "Changing capabilities requires 'a' or 's' privileges."); > 301 goto error; > 302 } 300 blob_appendf(&sql, ", cap=%Q", zCap); 303 blob_appendf(&sql, ", cap=%Q", zCap); 301 ++gotFields; 304 ++gotFields; 302 } 305 } 303 306 304 if( zPW ){ | 307 if( zPW && *zPW ){ > 308 if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){ > 309 json_set_err( FSL_JSON_E_DENIED, > 310 "Password change requires 'a', 's', " > 311 "or 'p' permissions."); > 312 goto error; > 313 }else{ > 314 #define TRY_LOGIN_GROUP 0 /* login group support is not yet implemented. */ > 315 #if !TRY_LOGIN_GROUP 305 char * zPWHash = NULL; | 316 char * zPWHash = NULL; 306 ++gotFields; | 317 ++gotFields; 307 zPWHash = sha1_shared_secret(zPW, zNameNew ? zNameNew : zName, NULL); | 318 zPWHash = sha1_shared_secret(zPW, zNameNew ? zNameNew : zName, NULL); 308 blob_appendf(&sql, ", pw=%Q", zPWHash); | 319 blob_appendf(&sql, ", pw=%Q", zPWHash); 309 free(zPWHash); | 320 free(zPWHash); > 321 #else > 322 ++gotFields; > 323 blob_appendf(&sql, ", pw=coalesce(shared_secret(%Q,%Q," > 324 "(SELECT value FROM config WHERE name='project-code')))", > 325 zPW, zNameNew ? zNameNew : zName); > 326 /* shared_secret() func is undefined? */ > 327 #endif > 328 } 310 } 329 } 311 330 312 if( zInfo ){ 331 if( zInfo ){ 313 blob_appendf(&sql, ", info=%Q", zInfo); 332 blob_appendf(&sql, ", info=%Q", zInfo); 314 ++gotFields; 333 ++gotFields; 315 } 334 } 316 335 ................................................................................................................................................................................ 322 341 323 if(!gotFields){ 342 if(!gotFields){ 324 json_set_err( FSL_JSON_E_MISSING_ARGS, 343 json_set_err( FSL_JSON_E_MISSING_ARGS, 325 "Required user data are missing."); 344 "Required user data are missing."); 326 goto error; 345 goto error; 327 } 346 } 328 assert(uid>0); 347 assert(uid>0); > 348 #if !TRY_LOGIN_GROUP 329 blob_appendf(&sql, " WHERE uid=%d", uid); 349 blob_appendf(&sql, " WHERE uid=%d", uid); 330 free( zNameFree ); < > 350 #else /* need name for login group support :/ */ > 351 blob_appendf(&sql, " WHERE login=%Q", zName); > 352 #endif 331 #if 0 353 #if 0 332 puts(blob_str(&sql)); 354 puts(blob_str(&sql)); 333 cson_output_FILE( cson_object_value(pUser), stdout, NULL ); 355 cson_output_FILE( cson_object_value(pUser), stdout, NULL ); 334 #endif 356 #endif 335 db_prepare(&q, "%s", blob_str(&sql)); 357 db_prepare(&q, "%s", blob_str(&sql)); 336 blob_reset(&sql); < 337 db_exec(&q); 358 db_exec(&q); 338 db_finalize(&q); 359 db_finalize(&q); > 360 #if TRY_LOGIN_GROUP > 361 if( zPW || forceLogout ){ > 362 Blob groupSql = empty_blob; > 363 char * zErr = NULL; > 364 blob_appendf(&groupSql, > 365 "INSERT INTO user(login)" > 366 " SELECT %Q WHERE NOT EXISTS(SELECT 1 FROM user WHERE login=%Q);", > 367 zName, zName > 368 ); > 369 blob_append(&groupSql, blob_str(&sql), blob_size(&sql)); > 370 login_group_sql(blob_str(&groupSql), NULL, NULL, &zErr); > 371 blob_reset(&groupSql); > 372 if( zErr ){ > 373 json_set_err( FSL_JSON_E_UNKNOWN, > 374 "Repo-group update at least partially failed: %s", > 375 zErr); > 376 free(zErr); > 377 goto error; > 378 } > 379 } > 380 #endif /* TRY_LOGIN_GROUP */ > 381 > 382 #undef TRY_LOGIN_GROUP > 383 > 384 free( zNameFree ); > 385 blob_reset(&sql); 339 return 0; 386 return 0; 340 387 341 error: 388 error: 342 assert(0 != g.json.resultCode); 389 assert(0 != g.json.resultCode); 343 free(zNameFree); 390 free(zNameFree); 344 blob_reset(&sql); 391 blob_reset(&sql); 345 return g.json.resultCode; 392 return g.json.resultCode; ................................................................................................................................................................................ 354 a JSON form of it... */ 401 a JSON form of it... */ 355 cson_object * u = cson_new_object(); 402 cson_object * u = cson_new_object(); 356 char const * str = NULL; 403 char const * str = NULL; 357 char b = -1; 404 char b = -1; 358 int i = -1; 405 int i = -1; 359 int uid = -1; 406 int uid = -1; 360 cson_value * payload = NULL; 407 cson_value * payload = NULL; 361 #define PROP(LK) str = json_find_option_cstr(LK,NULL,NULL); \ | 408 #define PROP(LK,SK) str = json_find_option_cstr(LK,NULL,SK); \ 362 if(str){ cson_object_set(u, LK, json_new_string(str)); } (void)0 409 if(str){ cson_object_set(u, LK, json_new_string(str)); } (void)0 363 PROP("name"); | 410 PROP("name","n"); 364 PROP("password"); | 411 PROP("password","p"); 365 PROP("info"); | 412 PROP("info","i"); 366 PROP("capabilities"); | 413 PROP("capabilities","c"); 367 #undef PROP 414 #undef PROP 368 < 369 #define PROP(LK,DFLT) b = json_find_option_bool(LK,NULL,NULL,DFLT); \ 415 #define PROP(LK,DFLT) b = json_find_option_bool(LK,NULL,NULL,DFLT); \ 370 if(DFLT!=b){ cson_object_set(u, LK, cson_value_new_bool(b)); } (void)0 416 if(DFLT!=b){ cson_object_set(u, LK, cson_value_new_bool(b)); } (void)0 371 PROP("forceLogout",-1); 417 PROP("forceLogout",-1); 372 #undef PROP 418 #undef PROP 373 419 374 #define PROP(LK,DFLT) i = json_find_option_int(LK,NULL,NULL,DFLT); \ | 420 #define PROP(LK,DFLT) i = json_find_option_int(LK,NULL,NULL,DFLT); \ 375 if(DFLT != i){ cson_object_set(u, LK, cson_value_new_integer(i)); } (void)0 421 if(DFLT != i){ cson_object_set(u, LK, cson_value_new_integer(i)); } (void)0 376 PROP("uid",-99); 422 PROP("uid",-99); 377 #undef PROP 423 #undef PROP 378 if( g.json.reqPayload.o ){ 424 if( g.json.reqPayload.o ){ 379 cson_object_merge( u, g.json.reqPayload.o, CSON_MERGE_NO_RECURSE ); 425 cson_object_merge( u, g.json.reqPayload.o, CSON_MERGE_NO_RECURSE ); 380 } 426 } 381 json_user_update_from_json( u ); 427 json_user_update_from_json( u );
Changes to src/json_wiki.c
22 #if INTERFACE 22 #if INTERFACE 23 #include "json_detail.h" 23 #include "json_detail.h" 24 #endif 24 #endif 25 25 26 static cson_value * json_wiki_create(); 26 static cson_value * json_wiki_create(); 27 static cson_value * json_wiki_get(); 27 static cson_value * json_wiki_get(); 28 static cson_value * json_wiki_list(); 28 static cson_value * json_wiki_list(); > 29 static cson_value * json_wiki_preview(); 29 static cson_value * json_wiki_save(); 30 static cson_value * json_wiki_save(); 30 < > 31 static cson_value * json_wiki_diff(); 31 /* 32 /* 32 ** Mapping of /json/wiki/XXX commands/paths to callbacks. 33 ** Mapping of /json/wiki/XXX commands/paths to callbacks. 33 */ 34 */ 34 static const JsonPageDef JsonPageDefs_Wiki[] = { 35 static const JsonPageDef JsonPageDefs_Wiki[] = { 35 {"create", json_wiki_create, 1}, | 36 {"create", json_wiki_create, 0}, > 37 {"diff", json_wiki_diff, 0}, 36 {"get", json_wiki_get, 0}, 38 {"get", json_wiki_get, 0}, 37 {"list", json_wiki_list, 0}, 39 {"list", json_wiki_list, 0}, > 40 {"preview", json_wiki_preview, 0}, 38 {"save", json_wiki_save, 1}, | 41 {"save", json_wiki_save, 0}, 39 {"timeline", json_timeline_wiki,0}, 42 {"timeline", json_timeline_wiki,0}, 40 /* Last entry MUST have a NULL name. */ 43 /* Last entry MUST have a NULL name. */ 41 {NULL,NULL,0} 44 {NULL,NULL,0} 42 }; 45 }; 43 46 44 47 45 /* 48 /* ................................................................................................................................................................................ 46 ** Implements the /json/wiki family of pages/commands. 49 ** Implements the /json/wiki family of pages/commands. 47 ** 50 ** 48 */ 51 */ 49 cson_value * json_page_wiki(){ 52 cson_value * json_page_wiki(){ 50 return json_page_dispatch_helper(&JsonPageDefs_Wiki[0]); 53 return json_page_dispatch_helper(&JsonPageDefs_Wiki[0]); 51 } 54 } 52 55 53 | 56 char * json_wiki_get_uuid_for_rid( int rid ) 54 /* | 57 { 55 ** Loads the given wiki page and creates a JSON object representation | 58 return db_text(NULL, 56 ** of it. If the page is not found then NULL is returned. If | 59 "SELECT b.uuid FROM tag t, tagxref x, blob b" 57 ** contentFormat is positive true then the page content is HTML-ized | 60 " WHERE x.tagid=t.tagid AND t.tagname GLOB 'wiki-*' " 58 ** using fossil's conventional wiki format, if it is negative then no | 61 " AND b.rid=x.rid AND b.rid=%d" 59 ** parsing is performed, if it is 0 then the content is not returned | 62 " ORDER BY x.mtime DESC LIMIT 1", 60 ** in the response. If contentFormat is 0 then the contentSize reflects | 63 rid 61 ** the number of bytes, not characters, stored in the page. | 64 ); 62 ** | 65 } 63 ** The returned value, if not NULL, is-a JSON Object owned by the | 66 64 ** caller. If it returns NULL then it may set g.json's error state. | 67 /* 65 */ | 68 ** Tries to load a wiki page from the given rid creates a JSON object 66 cson_value * json_get_wiki_page_by_name(char const * zPageName, char contentForm | 69 ** representation of it. If the page is not found then NULL is 67 int rid; | 70 ** returned. If contentFormat is positive then the page content 68 Manifest *pWiki = 0; | 71 ** is HTML-ized using fossil's conventional wiki format, if it is 69 char * zUuid = NULL; | 72 ** negative then no parsing is performed, if it is 0 then the content 70 Stmt q; | 73 ** is not returned in the response. If contentFormat is 0 then the 71 db_prepare(&q, | 74 ** contentSize reflects the number of bytes, not characters, stored in 72 "SELECT x.rid, b.uuid FROM tag t, tagxref x, blob b" | 75 ** the page. 73 " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' " | 76 ** 74 " AND b.rid=x.rid" | 77 ** The returned value, if not NULL, is-a JSON Object owned by the 75 " ORDER BY x.mtime DESC LIMIT 1", | 78 ** caller. If it returns NULL then it may set g.json's error state. 76 zPageName | 79 */ 77 ); | 80 cson_value * json_get_wiki_page_by_rid(int rid, char contentFormat){ 78 if( (SQLITE_ROW != db_step(&q)) ){ | 81 Manifest * pWiki = NULL; 79 db_finalize(&q); | 82 if( NULL == (pWiki = manifest_get(rid, CFTYPE_WIKI)) ){ 80 json_set_err( FSL_JSON_E_RESOURCE_NOT_FOUND, "Wiki page not found: %s", | 83 json_set_err( FSL_JSON_E_UNKNOWN, 81 zPageName ); | 84 "Error reading wiki page from manifest (rid=%d).", 82 return NULL; | 85 rid ); 83 } | 86 return NULL; 84 rid = db_column_int(&q,0); | 87 }else{ 85 zUuid = db_column_malloc(&q,1); | 88 /*char const * zFormat = NULL;*/ 86 db_finalize(&q); < 87 if( NULL == (pWiki = manifest_get(rid, CFTYPE_WIKI)) ){ < 88 free(zUuid); < 89 json_set_err( FSL_JSON_E_UNKNOWN, < 90 "Error reading wiki page from manifest (rid=%d, uuid=%s).", < 91 rid, zUuid ); < 92 return NULL; < 93 }else{ < 94 char const * zFormat = NULL; < 95 unsigned int len = 0; 89 unsigned int len = 0; 96 cson_object * pay = cson_new_object(); 90 cson_object * pay = cson_new_object(); 97 char const * zBody = pWiki->zWiki; 91 char const * zBody = pWiki->zWiki; > 92 char const * zFormat = NULL; > 93 char * zUuid = json_wiki_get_uuid_for_rid(rid); 98 cson_object_set(pay,"name",json_new_string(zPageName)); | 94 cson_object_set(pay,"name",json_new_string(pWiki->zWikiTitle)); 99 cson_object_set(pay,"uuid",json_new_string(zUuid)); 95 cson_object_set(pay,"uuid",json_new_string(zUuid)); 100 free(zUuid); 96 free(zUuid); 101 zUuid = NULL; 97 zUuid = NULL; > 98 if( pWiki->nParent > 0 ){ > 99 cson_object_set( pay, "parent", json_new_string(pWiki->azParent[0]) ) > 100 /* Reminder: wiki pages do not branch and have only one parent > 101 (except for the initial version, which has no parents). */; > 102 } 102 /*cson_object_set(pay,"rid",json_new_int((cson_int_t)rid));*/ 103 /*cson_object_set(pay,"rid",json_new_int((cson_int_t)rid));*/ 103 cson_object_set(pay,"lastSavedBy",json_new_string(pWiki->zUser)); | 104 cson_object_set(pay,"user",json_new_string(pWiki->zUser)); 104 cson_object_set(pay,FossilJsonKeys.timestamp, 105 cson_object_set(pay,FossilJsonKeys.timestamp, 105 json_julian_to_timestamp(pWiki->rDate)); 106 json_julian_to_timestamp(pWiki->rDate)); 106 < 107 < 108 if(0 == contentFormat){ 107 if(0 == contentFormat){ 109 cson_object_set(pay,"contentLength", | 108 cson_object_set(pay,"size", 110 json_new_int((cson_int_t)(zBody?strlen(zBody):0))); 109 json_new_int((cson_int_t)(zBody?strlen(zBody):0))); 111 }else{ 110 }else{ 112 cson_object_set(pay,"contentFormat",json_new_string(zFormat)); < 113 if( contentFormat>0 ){/*HTML-ize it*/ 111 if( contentFormat>0 ){/*HTML-ize it*/ > 112 zFormat = "html"; 114 Blob content = empty_blob; 113 Blob content = empty_blob; 115 Blob raw = empty_blob; 114 Blob raw = empty_blob; 116 if(zBody && *zBody){ 115 if(zBody && *zBody){ 117 blob_append(&raw,zBody,-1); 116 blob_append(&raw,zBody,-1); 118 wiki_convert(&raw,&content,0); 117 wiki_convert(&raw,&content,0); 119 len = (unsigned int)blob_size(&content); 118 len = (unsigned int)blob_size(&content); 120 } 119 } 121 cson_object_set(pay,"contentLength",json_new_int((cson_int_t)len)); | 120 cson_object_set(pay,"size",json_new_int((cson_int_t)len)); 122 cson_object_set(pay,"content", 121 cson_object_set(pay,"content", 123 cson_value_new_string(blob_buffer(&content),len)); 122 cson_value_new_string(blob_buffer(&content),len)); 124 blob_reset(&content); 123 blob_reset(&content); 125 blob_reset(&raw); 124 blob_reset(&raw); 126 }else{/*raw format*/ 125 }else{/*raw format*/ > 126 zFormat = "raw"; 127 len = zBody ? strlen(zBody) : 0; 127 len = zBody ? strlen(zBody) : 0; 128 cson_object_set(pay,"contentLength",json_new_int((cson_int_t)len)); | 128 cson_object_set(pay,"size",json_new_int((cson_int_t)len)); 129 cson_object_set(pay,"content",cson_value_new_string(zBody,len)); 129 cson_object_set(pay,"content",cson_value_new_string(zBody,len)); 130 } 130 } > 131 cson_object_set(pay,"contentFormat",json_new_string(zFormat)); > 132 131 } 133 } 132 /*TODO: add 'T' (tag) fields*/ 134 /*TODO: add 'T' (tag) fields*/ 133 /*TODO: add the 'A' card (file attachment) entries?*/ 135 /*TODO: add the 'A' card (file attachment) entries?*/ 134 manifest_destroy(pWiki); 136 manifest_destroy(pWiki); 135 return cson_object_value(pay); 137 return cson_object_value(pay); 136 } 138 } 137 | 139 } 138 } | 140 139 < 140 < 141 /* 141 /* 142 ** Searches for a wiki page with the given rid. If found it behaves | 142 ** Searches for the latest version of a wiki page with the given > 143 ** name. If found it behaves like json_get_wiki_page_by_rid(theRid, 143 ** like json_get_wiki_page_by_name(pageName, contentFormat), else it returns | 144 ** contentFormat), else it returns NULL. 144 ** NULL. < 145 */ 145 */ 146 cson_value * json_get_wiki_page_by_rid(int rid, char contentFormat){ | 146 cson_value * json_get_wiki_page_by_name(char const * zPageName, char contentForm 147 char * zPageName = NULL; < 148 cson_value * rc = NULL; < 149 zPageName = db_text(NULL, < 150 "SELECT substr(t.tagname,6) AS name " < > 147 int rid; > 148 rid = db_int(0, 151 " FROM tag t, tagxref x, blob b " | 149 "SELECT x.rid FROM tag t, tagxref x, blob b" 152 " WHERE b.rid=%d " < 153 " AND t.tagname GLOB 'wiki-*'" < > 150 " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' " 154 " AND x.tagid=t.tagid AND b.rid=x.rid ", | 151 " AND b.rid=x.rid" > 152 " ORDER BY x.mtime DESC LIMIT 1", > 153 zPageName 155 rid); | 154 ); > 155 if( 0==rid ){ > 156 json_set_err( FSL_JSON_E_RESOURCE_NOT_FOUND, "Wiki page not found: %s", 156 if( zPageName ){ | 157 zPageName ); > 158 return NULL; > 159 } > 160 return json_get_wiki_page_by_rid(rid, contentFormat); > 161 } > 162 > 163 > 164 /* > 165 ** Searches json_find_option_ctr("format",NULL,"f") for a flag. > 166 ** If not found it returns defaultValue else it returns a value > 167 ** depending on the first character of the format option: > 168 ** > 169 ** [h]tml = 1 > 170 ** [n]one = 0 > 171 ** [r]aw = -1 > 172 ** > 173 ** The return value is intended for use with 157 rc = json_get_wiki_page_by_name(zPageName, contentFormat); | 174 ** json_get_wiki_page_by_rid() and friends. 158 free(zPageName); < > 175 */ > 176 char json_wiki_get_content_format_flag( char defaultValue ){ > 177 char contentFormat = defaultValue; > 178 char const * zFormat = json_find_option_cstr("format",NULL,"f"); > 179 if( !zFormat || !*zFormat ){ > 180 return contentFormat; 159 } 181 } > 182 else if('r'==*zFormat){ > 183 contentFormat = -1; > 184 } > 185 else if('h'==*zFormat){ > 186 contentFormat = 1; > 187 } > 188 else if('n'==*zFormat){ > 189 contentFormat = 0; > 190 } > 191 return contentFormat; > 192 } > 193 > 194 /* > 195 ** Helper for /json/wiki/get and /json/wiki/preview. At least one of > 196 ** zPageName (wiki page name) or zSymname must be set to a > 197 ** non-empty/non-NULL value. zSymname takes precedence. On success > 198 ** the result of one of json_get_wiki_page_by_rid() or > 199 ** json_get_wiki_page_by_name() will be returned (owned by the > 200 ** caller). On error g.json's error state is set and NULL is returned. > 201 */ > 202 static cson_value * json_wiki_get_by_name_or_symname(char const * zPageName, > 203 char const * zSymname, > 204 char contentFormat ){ > 205 if(!zSymname || !*zSymname){ > 206 return json_get_wiki_page_by_name(zPageName, contentFormat); > 207 }else{ > 208 int rid = symbolic_name_to_rid( zSymname ? zSymname : zPageName, "w" ); > 209 if(rid<0){ > 210 json_set_err(FSL_JSON_E_AMBIGUOUS_UUID, > 211 "UUID [%s] is ambiguious.", zSymname); 160 return rc; | 212 return NULL; > 213 }else if(rid==0){ > 214 json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, > 215 "UUID [%s] does not resolve to a wiki page.", zSymname); > 216 return NULL; > 217 }else{ > 218 return json_get_wiki_page_by_rid(rid, contentFormat); 161 } | 219 } 162 | 220 } > 221 } > 222 163 /* 223 /* 164 ** Implementation of /json/wiki/get. 224 ** Implementation of /json/wiki/get. 165 ** 225 ** 166 */ 226 */ 167 static cson_value * json_wiki_get(){ 227 static cson_value * json_wiki_get(){ 168 char const * zPageName; 228 char const * zPageName; 169 char const * zFormat = NULL; | 229 char const * zSymName = NULL; 170 char contentFormat = -1; 230 char contentFormat = -1; 171 if( !g.perm.RdWiki && !g.perm.Read ){ 231 if( !g.perm.RdWiki && !g.perm.Read ){ 172 json_set_err(FSL_JSON_E_DENIED, 232 json_set_err(FSL_JSON_E_DENIED, 173 "Requires 'o' or 'j' access."); 233 "Requires 'o' or 'j' access."); 174 return NULL; 234 return NULL; 175 } 235 } > 236 zPageName = json_find_option_cstr2("name",NULL,"n",g.json.dispatchDepth+1); > 237 176 zPageName = json_find_option_cstr("name",NULL,"n") | 238 zSymName = json_find_option_cstr("uuid",NULL,"u"); 177 /* Damn... fossil automatically sets name to the PATH < 178 part after /json, so we need a workaround down here.... < 179 */ < 180 ; | 239 181 if( zPageName && (NULL != strstr(zPageName, "/"))){ < 182 /* Assume that we picked up a path remnant. */ < > 240 if((!zPageName||!*zPageName) && (!zSymName || !*zSymName)){ > 241 json_set_err(FSL_JSON_E_MISSING_ARGS, > 242 "At least one of the 'name' or 'uuid' arguments must be provide 183 zPageName = NULL; | 243 return NULL; 184 } 244 } 185 if( !zPageName && cson_value_is_string(g.json.reqPayload.v) ){ < 186 zPageName = cson_string_cstr(cson_value_get_string(g.json.reqPayload.v)); < 187 } | 245 > 246 /* TODO: see if we have a page named zPageName. If not, try to resolve 188 if(!zPageName){ | 247 zPageName as a UUID. 189 zPageName = json_command_arg(g.json.dispatchDepth+1); < > 248 */ 190 } | 249 191 if(!zPageName||!*zPageName){ < 192 json_set_err(FSL_JSON_E_MISSING_ARGS, < 193 "'name' argument is missing."); < 194 return NULL; < > 250 contentFormat = json_wiki_get_content_format_flag(contentFormat); > 251 return json_wiki_get_by_name_or_symname( zPageName, zSymName, contentFormat ); 195 } | 252 } 196 253 197 zFormat = json_find_option_cstr("format",NULL,"f"); < 198 if(!zFormat || !*zFormat || ('r'==*zFormat)){ < 199 contentFormat = -1; < > 254 /* > 255 ** Implementation of /json/wiki/preview. > 256 ** > 257 */ > 258 static cson_value * json_wiki_preview(){ > 259 char const * zContent = NULL; > 260 cson_value * pay = NULL; > 261 Blob contentOrig = empty_blob; > 262 Blob contentHtml = empty_blob; > 263 if( !g.perm.WrWiki ){ > 264 json_set_err(FSL_JSON_E_DENIED, > 265 "Requires 'k' access."); > 266 return NULL; 200 } 267 } 201 else if('h'==*zFormat){ < > 268 zContent = cson_string_cstr(cson_value_get_string(g.json.reqPayload.v)); 202 contentFormat = 1; | 269 if(!zContent) { > 270 json_set_err(FSL_JSON_E_MISSING_ARGS, > 271 "The 'payload' property must be a string containing the wiki co > 272 return NULL; 203 } 273 } 204 else if('n'==*zFormat){ < 205 contentFormat = 0; < > 274 blob_append( &contentOrig, zContent, (int)cson_string_length_bytes(cson_value_ > 275 wiki_convert( &contentOrig, &contentHtml, 0 ); > 276 blob_reset( &contentOrig ); > 277 pay = cson_value_new_string( blob_str(&contentHtml), (unsigned int)blob_size(& > 278 blob_reset( &contentHtml ); > 279 return pay; 206 } | 280 } 207 return json_get_wiki_page_by_name(zPageName, contentFormat); < 208 } | 281 209 282 210 /* 283 /* 211 ** Internal impl of /wiki/save and /wiki/create. If createMode is 0 284 ** Internal impl of /wiki/save and /wiki/create. If createMode is 0 212 ** and the page already exists then a 285 ** and the page already exists then a 213 ** FSL_JSON_E_RESOURCE_ALREADY_EXISTS error is triggered. If 286 ** FSL_JSON_E_RESOURCE_ALREADY_EXISTS error is triggered. If 214 ** createMode is false then the FSL_JSON_E_RESOURCE_NOT_FOUND is 287 ** createMode is false then the FSL_JSON_E_RESOURCE_NOT_FOUND is 215 ** triggered if the page does not already exists. 288 ** triggered if the page does not already exists. ................................................................................................................................................................................ 217 ** Note that the error triggered when createMode==0 and no such page 290 ** Note that the error triggered when createMode==0 and no such page 218 ** exists is rather arbitrary - we could just as well create the entry 291 ** exists is rather arbitrary - we could just as well create the entry 219 ** here if it doesn't already exist. With that, save/create would 292 ** here if it doesn't already exist. With that, save/create would 220 ** become one operation. That said, i expect there are people who 293 ** become one operation. That said, i expect there are people who 221 ** would categorize such behaviour as "being too clever" or "doing too 294 ** would categorize such behaviour as "being too clever" or "doing too 222 ** much automatically" (and i would likely agree with them). 295 ** much automatically" (and i would likely agree with them). 223 ** 296 ** 224 ** If allowCreateIfExists is true then this function will allow a new | 297 ** If allowCreateIfNotExists is true then this function will allow a new 225 ** page to be created even if createMode is false. 298 ** page to be created even if createMode is false. 226 */ 299 */ 227 static cson_value * json_wiki_create_or_save(char createMode, 300 static cson_value * json_wiki_create_or_save(char createMode, 228 char allowCreateIfExists){ | 301 char allowCreateIfNotExists){ 229 Blob content = empty_blob; /* wiki page content */ 302 Blob content = empty_blob; /* wiki page content */ 230 cson_value * nameV; /* wiki page name */ 303 cson_value * nameV; /* wiki page name */ 231 char const * zPageName; /* cstr form of page name */ 304 char const * zPageName; /* cstr form of page name */ 232 cson_value * contentV; /* passed-in content */ 305 cson_value * contentV; /* passed-in content */ 233 cson_value * emptyContent = NULL; /* placeholder for empty content. */ 306 cson_value * emptyContent = NULL; /* placeholder for empty content. */ 234 cson_value * payV = NULL; /* payload/return value */ 307 cson_value * payV = NULL; /* payload/return value */ 235 cson_string const * jstr = NULL; /* temp for cson_value-to-cson_string conver 308 cson_string const * jstr = NULL; /* temp for cson_value-to-cson_string conver ................................................................................................................................................................................ 245 nameV = json_req_payload_get("name"); 318 nameV = json_req_payload_get("name"); 246 if(!nameV){ 319 if(!nameV){ 247 json_set_err( FSL_JSON_E_MISSING_ARGS, 320 json_set_err( FSL_JSON_E_MISSING_ARGS, 248 "'name' parameter is missing."); 321 "'name' parameter is missing."); 249 return NULL; 322 return NULL; 250 } 323 } 251 zPageName = cson_string_cstr(cson_value_get_string(nameV)); 324 zPageName = cson_string_cstr(cson_value_get_string(nameV)); > 325 if(!zPageName || !*zPageName){ > 326 json_set_err(FSL_JSON_E_INVALID_ARGS, > 327 "'name' parameter must be a non-empty string."); > 328 return NULL; > 329 } 252 rid = db_int(0, 330 rid = db_int(0, 253 "SELECT x.rid FROM tag t, tagxref x" 331 "SELECT x.rid FROM tag t, tagxref x" 254 " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" 332 " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" 255 " ORDER BY x.mtime DESC LIMIT 1", 333 " ORDER BY x.mtime DESC LIMIT 1", 256 zPageName 334 zPageName 257 ); 335 ); 258 336 ................................................................................................................................................................................ 259 if(rid){ 337 if(rid){ 260 if(createMode){ 338 if(createMode){ 261 json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS, 339 json_set_err(FSL_JSON_E_RESOURCE_ALREADY_EXISTS, 262 "Wiki page '%s' already exists.", 340 "Wiki page '%s' already exists.", 263 zPageName); 341 zPageName); 264 goto error; 342 goto error; 265 } 343 } 266 }else if(!allowCreateIfExists){ | 344 }else if(!createMode && !allowCreateIfNotExists){ 267 json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, 345 json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, 268 "Wiki page '%s' not found.", 346 "Wiki page '%s' not found.", 269 zPageName); 347 zPageName); 270 goto error; 348 goto error; 271 } 349 } 272 350 273 contentV = json_req_payload_get("content"); 351 contentV = json_req_payload_get("content"); 274 if( !contentV ){ 352 if( !contentV ){ 275 if( createMode || (!rid && allowCreateIfExists) ){ | 353 if( createMode || (!rid && allowCreateIfNotExists) ){ 276 contentV = emptyContent = cson_value_new_string("",0); 354 contentV = emptyContent = cson_value_new_string("",0); 277 }else{ 355 }else{ 278 json_set_err(FSL_JSON_E_MISSING_ARGS, 356 json_set_err(FSL_JSON_E_MISSING_ARGS, 279 "'content' parameter is missing."); 357 "'content' parameter is missing."); 280 goto error; 358 goto error; 281 } 359 } 282 } 360 } 283 if( !cson_value_is_string(nameV) 361 if( !cson_value_is_string(nameV) 284 || !cson_value_is_string(contentV)){ 362 || !cson_value_is_string(contentV)){ 285 json_set_err(FSL_JSON_E_INVALID_ARGS, 363 json_set_err(FSL_JSON_E_INVALID_ARGS, 286 "'name' and 'content' parameters must be strings."); | 364 "'content' parameter must be a string."); 287 goto error; 365 goto error; 288 } 366 } 289 jstr = cson_value_get_string(contentV); 367 jstr = cson_value_get_string(contentV); 290 contentLen = (int)cson_string_length_bytes(jstr); 368 contentLen = (int)cson_string_length_bytes(jstr); 291 if(contentLen){ 369 if(contentLen){ 292 blob_append(&content, cson_string_cstr(jstr),contentLen); 370 blob_append(&content, cson_string_cstr(jstr),contentLen); 293 } 371 } ................................................................................................................................................................................ 339 417 340 /* 418 /* 341 ** Implementation of /json/wiki/list. 419 ** Implementation of /json/wiki/list. 342 */ 420 */ 343 static cson_value * json_wiki_list(){ 421 static cson_value * json_wiki_list(){ 344 cson_value * listV = NULL; 422 cson_value * listV = NULL; 345 cson_array * list = NULL; 423 cson_array * list = NULL; > 424 char const * zGlob = NULL; 346 Stmt q; | 425 Stmt q = empty_Stmt; > 426 Blob sql = empty_blob; 347 char const verbose = json_find_option_bool("verbose",NULL,"v",0); 427 char const verbose = json_find_option_bool("verbose",NULL,"v",0); > 428 char fInvert = json_find_option_bool("invert",NULL,"i",0);; 348 429 349 if( !g.perm.RdWiki && !g.perm.Read ){ 430 if( !g.perm.RdWiki && !g.perm.Read ){ 350 json_set_err(FSL_JSON_E_DENIED, 431 json_set_err(FSL_JSON_E_DENIED, 351 "Requires 'j' or 'o' permissions."); 432 "Requires 'j' or 'o' permissions."); 352 return NULL; 433 return NULL; 353 } 434 } 354 db_prepare(&q,"SELECT" | 435 blob_append(&sql,"SELECT" 355 " substr(tagname,6) as name" | 436 " substr(tagname,6) as name" 356 " FROM tag WHERE tagname GLOB 'wiki-*'" | 437 " FROM tag WHERE tagname GLOB 'wiki-*'", > 438 -1); > 439 zGlob = json_find_option_cstr("glob",NULL,"g"); > 440 if(zGlob && *zGlob){ > 441 blob_appendf(&sql," AND name %s GLOB %Q", > 442 fInvert ? "NOT" : "", zGlob); > 443 }else{ > 444 zGlob = json_find_option_cstr("like",NULL,"l"); > 445 if(zGlob && *zGlob){ > 446 blob_appendf(&sql," AND name %s LIKE %Q", > 447 fInvert ? "NOT" : "", > 448 zGlob); > 449 } > 450 } 357 " ORDER BY lower(name)"); | 451 blob_append(&sql," ORDER BY lower(name)", -1); > 452 db_prepare(&q,"%s", blob_str(&sql)); > 453 blob_reset(&sql); 358 listV = cson_value_new_array(); 454 listV = cson_value_new_array(); 359 list = cson_value_get_array(listV); 455 list = cson_value_get_array(listV); 360 while( SQLITE_ROW == db_step(&q) ){ 456 while( SQLITE_ROW == db_step(&q) ){ 361 cson_value * v; 457 cson_value * v; 362 if( verbose ){ 458 if( verbose ){ 363 char const * name = db_column_text(&q,0); 459 char const * name = db_column_text(&q,0); 364 v = json_get_wiki_page_by_name(name,0); 460 v = json_get_wiki_page_by_name(name,0); ................................................................................................................................................................................ 382 assert(0 != g.json.resultCode); 478 assert(0 != g.json.resultCode); 383 cson_value_free(listV); 479 cson_value_free(listV); 384 listV = NULL; 480 listV = NULL; 385 end: 481 end: 386 db_finalize(&q); 482 db_finalize(&q); 387 return listV; 483 return listV; 388 } 484 } > 485 > 486 static cson_value * json_wiki_diff(){ > 487 char const * zV1 = NULL; > 488 char const * zV2 = NULL; > 489 cson_object * pay = NULL; > 490 int argPos = g.json.dispatchDepth; > 491 int r1 = 0, r2 = 0; > 492 Manifest * pW1 = NULL, *pW2 = NULL; > 493 Blob w1 = empty_blob, w2 = empty_blob, d = empty_blob; > 494 char const * zErrTag = NULL; > 495 int diffFlags; > 496 char * zUuid = NULL; > 497 if( !g.perm.History ){ > 498 json_set_err(FSL_JSON_E_DENIED, > 499 "Requires 'h' permissions."); > 500 return NULL; > 501 } > 502 > 503 > 504 zV1 = json_find_option_cstr2( "v1",NULL, NULL, ++argPos ); > 505 zV2 = json_find_option_cstr2( "v2",NULL, NULL, ++argPos ); > 506 if(!zV1 || !*zV1 || !zV2 || !*zV2) { > 507 json_set_err(FSL_JSON_E_INVALID_ARGS, > 508 "Requires both 'v1' and 'v2' arguments."); > 509 return NULL; > 510 } > 511 > 512 r1 = symbolic_name_to_rid( zV1, "w" ); > 513 zErrTag = zV1; > 514 if(r1<0){ > 515 goto ambiguous; > 516 }else if(0==r1){ > 517 goto invalid; > 518 } > 519 > 520 r2 = symbolic_name_to_rid( zV2, "w" ); > 521 zErrTag = zV2; > 522 if(r2<0){ > 523 goto ambiguous; > 524 }else if(0==r2){ > 525 goto invalid; > 526 } > 527 > 528 zErrTag = zV1; > 529 pW1 = manifest_get(r1, CFTYPE_WIKI); > 530 if( pW1==0 ) { > 531 goto manifest; > 532 } > 533 zErrTag = zV2; > 534 pW2 = manifest_get(r2, CFTYPE_WIKI); > 535 if( pW2==0 ) { > 536 goto manifest; > 537 } > 538 > 539 blob_init(&w1, pW1->zWiki, -1); > 540 blob_zero(&w2); > 541 blob_init(&w2, pW2->zWiki, -1); > 542 blob_zero(&d); > 543 diffFlags = DIFF_IGNORE_EOLWS | DIFF_INLINE; > 544 text_diff(&w2, &w1, &d, diffFlags); > 545 blob_reset(&w1); > 546 blob_reset(&w2); > 547 > 548 pay = cson_new_object(); > 549 > 550 zUuid = json_wiki_get_uuid_for_rid( pW1->rid ); > 551 cson_object_set(pay, "v1", json_new_string(zUuid) ); > 552 free(zUuid); > 553 zUuid = json_wiki_get_uuid_for_rid( pW2->rid ); > 554 cson_object_set(pay, "v2", json_new_string(zUuid) ); > 555 free(zUuid); > 556 zUuid = NULL; > 557 > 558 manifest_destroy(pW1); > 559 manifest_destroy(pW2); > 560 > 561 cson_object_set(pay, "diff", > 562 cson_value_new_string( blob_str(&d), > 563 (unsigned int)blob_size(&d))); > 564 > 565 return cson_object_value(pay); > 566 > 567 manifest: > 568 json_set_err(FSL_JSON_E_UNKNOWN, > 569 "Could not load wiki manifest for UUID [%s].", > 570 zErrTag); > 571 goto end; > 572 > 573 ambiguous: > 574 json_set_err(FSL_JSON_E_AMBIGUOUS_UUID, > 575 "UUID [%s] is ambiguous.", zErrTag); > 576 goto end; > 577 > 578 invalid: > 579 json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, > 580 "UUID [%s] not found.", zErrTag); > 581 goto end; > 582 > 583 end: > 584 cson_free_object(pay); > 585 return NULL; > 586 } > 587 389 #endif /* FOSSIL_ENABLE_JSON */ 588 #endif /* FOSSIL_ENABLE_JSON */
Changes to src/login.c
259 char ** zDest /* Optional: store generated cookie value. */ 259 char ** zDest /* Optional: store generated cookie value. */ 260 ){ 260 ){ 261 const char *zCookieName = login_cookie_name(); 261 const char *zCookieName = login_cookie_name(); 262 const char *zExpire = db_get("cookie-expire","8766"); 262 const char *zExpire = db_get("cookie-expire","8766"); 263 int expires = atoi(zExpire)*3600; 263 int expires = atoi(zExpire)*3600; 264 char *zHash; 264 char *zHash; 265 char *zCookie; 265 char *zCookie; 266 char const * zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for l | 266 char const *zIpAddr = PD("REMOTE_ADDR","nil"); /* IP address of user */ 267 char * zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */ | 267 char *zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */ > 268 268 assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data."); 269 assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data."); > 270 zHash = db_text(0, > 271 "SELECT cookie FROM user" > 272 " WHERE uid=%d" > 273 " AND ipaddr=%Q" > 274 " AND cexpire>julianday('now')" > 275 " AND length(cookie)>30", > 276 uid, zRemoteAddr); 269 zHash = db_text(0, "SELECT hex(randomblob(25))"); | 277 if( zHash==0 ) zHash = db_text(0, "SELECT hex(randomblob(25))"); 270 zCookie = login_gen_user_cookie_value(zUsername, zHash); 278 zCookie = login_gen_user_cookie_value(zUsername, zHash); 271 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); 279 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); 272 record_login_attempt(zUsername, zIpAddr, 1); 280 record_login_attempt(zUsername, zIpAddr, 1); 273 db_multi_exec( 281 db_multi_exec( 274 "UPDATE user SET cookie=%Q, ipaddr=%Q, " 282 "UPDATE user SET cookie=%Q, ipaddr=%Q, " 275 " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", 283 " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", 276 zHash, zRemoteAddr, expires, uid 284 zHash, zRemoteAddr, expires, uid ................................................................................................................................................................................ 928 } 936 } 929 } 937 } 930 938 931 /* 939 /* 932 ** Flags passed into the 2nd argument of login_set/replace_capabilities(). 940 ** Flags passed into the 2nd argument of login_set/replace_capabilities(). 933 */ 941 */ 934 #if INTERFACE 942 #if INTERFACE 935 #define LOGIN_IGNORE_U 0x01 /* Ignore "u" */ | 943 #define LOGIN_IGNORE_UV 0x01 /* Ignore "u" and "v" */ 936 #define LOGIN_IGNORE_V 0x01 /* Ignore "v" */ < 937 #endif 944 #endif 938 945 939 /* 946 /* 940 ** Adds all capability flags in zCap to g.perm. 947 ** Adds all capability flags in zCap to g.perm. 941 */ 948 */ 942 void login_set_capabilities(const char *zCap, unsigned flags){ 949 void login_set_capabilities(const char *zCap, unsigned flags){ 943 int i; 950 int i; ................................................................................................................................................................................ 976 case 't': g.perm.TktFmt = 1; break; 983 case 't': g.perm.TktFmt = 1; break; 977 case 'b': g.perm.Attach = 1; break; 984 case 'b': g.perm.Attach = 1; break; 978 case 'x': g.perm.Private = 1; break; 985 case 'x': g.perm.Private = 1; break; 979 986 980 /* The "u" privileges is a little different. It recursively 987 /* The "u" privileges is a little different. It recursively 981 ** inherits all privileges of the user named "reader" */ 988 ** inherits all privileges of the user named "reader" */ 982 case 'u': { 989 case 'u': { 983 if( (flags & LOGIN_IGNORE_U)==0 ){ | 990 if( (flags & LOGIN_IGNORE_UV)==0 ){ 984 const char *zUser; 991 const char *zUser; 985 zUser = db_text("", "SELECT cap FROM user WHERE login='reader'"); 992 zUser = db_text("", "SELECT cap FROM user WHERE login='reader'"); 986 login_set_capabilities(zUser, flags | LOGIN_IGNORE_U); | 993 login_set_capabilities(zUser, flags | LOGIN_IGNORE_UV); 987 } 994 } 988 break; 995 break; 989 } 996 } 990 997 991 /* The "v" privileges is a little different. It recursively 998 /* The "v" privileges is a little different. It recursively 992 ** inherits all privileges of the user named "developer" */ 999 ** inherits all privileges of the user named "developer" */ 993 case 'v': { 1000 case 'v': { 994 if( (flags & LOGIN_IGNORE_V)==0 ){ | 1001 if( (flags & LOGIN_IGNORE_UV)==0 ){ 995 const char *zDev; 1002 const char *zDev; 996 zDev = db_text("", "SELECT cap FROM user WHERE login='developer'"); 1003 zDev = db_text("", "SELECT cap FROM user WHERE login='developer'"); 997 login_set_capabilities(zDev, flags | LOGIN_IGNORE_V); | 1004 login_set_capabilities(zDev, flags | LOGIN_IGNORE_UV); 998 } 1005 } 999 break; 1006 break; 1000 } 1007 } 1001 } 1008 } 1002 } 1009 } 1003 } 1010 } 1004 1011 ................................................................................................................................................................................ 1205 * this %s(zUsername), or at least I don't know how to force it to.*/ 1212 * this %s(zUsername), or at least I don't know how to force it to.*/ 1206 @ <p><span class="loginError"> 1213 @ <p><span class="loginError"> 1207 @ %s(zUsername) already exists. 1214 @ %s(zUsername) already exists. 1208 @ </span></p> 1215 @ </span></p> 1209 }else{ 1216 }else{ 1210 char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0); 1217 char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0); 1211 int uid; 1218 int uid; 1212 char *zCookie; < 1213 const char *zCookieName; < 1214 const char *zExpire; < 1215 int expires; < 1216 const char *zIpAddr; < 1217 db_multi_exec( 1219 db_multi_exec( 1218 "INSERT INTO user(login,pw,cap,info)" 1220 "INSERT INTO user(login,pw,cap,info)" 1219 "VALUES(%B,%Q,%B,%B)", 1221 "VALUES(%B,%Q,%B,%B)", 1220 &login, zPw, &caps, &contact 1222 &login, zPw, &caps, &contact 1221 ); 1223 ); 1222 free(zPw); 1224 free(zPw); 1223 1225 1224 /* The user is registered, now just log him in. */ 1226 /* The user is registered, now just log him in. */ 1225 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUsername); 1227 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUsername); 1226 zCookieName = login_cookie_name(); | 1228 login_set_user_cookie( zUsername, uid, NULL ); 1227 zExpire = db_get("cookie-expire","8766"); < 1228 expires = atoi(zExpire)*3600; < 1229 zIpAddr = PD("REMOTE_ADDR","nil"); < 1230 < 1231 zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid); < 1232 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); < 1233 record_login_attempt(zUsername, zIpAddr, 1); < 1234 db_multi_exec( < 1235 "UPDATE user SET cookie=%Q, ipaddr=%Q, " < 1236 " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d", < 1237 zCookie, ipPrefix(zIpAddr), expires, uid < 1238 ); < 1239 redirect_to_g(); 1229 redirect_to_g(); 1240 1230 1241 } 1231 } 1242 } 1232 } 1243 } 1233 } 1244 1234 1245 /* Prepare the captcha. */ 1235 /* Prepare the captcha. */ ................................................................................................................................................................................ 1394 char *zSql; /* SQL to run on all peers */ 1384 char *zSql; /* SQL to run on all peers */ 1395 const char *zSelf; /* The ATTACH name of our repository */ 1385 const char *zSelf; /* The ATTACH name of our repository */ 1396 1386 1397 *pzErrMsg = 0; /* Default to no errors */ 1387 *pzErrMsg = 0; /* Default to no errors */ 1398 zSelf = db_name("repository"); 1388 zSelf = db_name("repository"); 1399 1389 1400 /* Get the full pathname of the other repository */ 1390 /* Get the full pathname of the other repository */ 1401 file_canonical_name(zRepo, &fullName); | 1391 file_canonical_name(zRepo, &fullName, 0); 1402 zRepo = mprintf(blob_str(&fullName)); 1392 zRepo = mprintf(blob_str(&fullName)); 1403 blob_reset(&fullName); 1393 blob_reset(&fullName); 1404 1394 1405 /* Get the full pathname for our repository. Also the project code 1395 /* Get the full pathname for our repository. Also the project code 1406 ** and project name for ourself. */ 1396 ** and project name for ourself. */ 1407 file_canonical_name(g.zRepositoryName, &fullName); | 1397 file_canonical_name(g.zRepositoryName, &fullName, 0); 1408 zSelfRepo = mprintf(blob_str(&fullName)); 1398 zSelfRepo = mprintf(blob_str(&fullName)); 1409 blob_reset(&fullName); 1399 blob_reset(&fullName); 1410 zSelfProjCode = db_get("project-code", "unknown"); 1400 zSelfProjCode = db_get("project-code", "unknown"); 1411 zSelfLabel = db_get("project-name", 0); 1401 zSelfLabel = db_get("project-name", 0); 1412 if( zSelfLabel==0 ){ 1402 if( zSelfLabel==0 ){ 1413 zSelfLabel = zSelfProjCode; 1403 zSelfLabel = zSelfProjCode; 1414 } 1404 }
Changes to src/main.c
444 if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ 444 if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ 445 zCmdName = "cgi"; 445 zCmdName = "cgi"; 446 g.isHTTP = 1; 446 g.isHTTP = 1; 447 }else if( argc<2 ){ 447 }else if( argc<2 ){ 448 fossil_print( 448 fossil_print( 449 "Usage: %s COMMAND ...\n" 449 "Usage: %s COMMAND ...\n" 450 " or: %s help -- for a list of common commands\n" 450 " or: %s help -- for a list of common commands\n" 451 " or: %s help COMMMAND -- for help with the named command\n" | 451 " or: %s help COMMMAND -- for help with the named command\n", 452 " or: %s commands -- for a list of all commands\n", < 453 argv[0], argv[0], argv[0], argv[0]); | 452 argv[0], argv[0], argv[0]); 454 fossil_exit(1); 453 fossil_exit(1); 455 }else{ 454 }else{ 456 g.isHTTP = 0; 455 g.isHTTP = 0; 457 g.fQuiet = find_option("quiet", 0, 0)!=0; 456 g.fQuiet = find_option("quiet", 0, 0)!=0; 458 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; 457 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; 459 g.fSqlStats = find_option("sqlstats", 0, 0)!=0; 458 g.fSqlStats = find_option("sqlstats", 0, 0)!=0; 460 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; 459 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; ................................................................................................................................................................................ 1111 #if !defined(_WIN32) 1110 #if !defined(_WIN32) 1112 if( getuid()==0 ){ 1111 if( getuid()==0 ){ 1113 int i; 1112 int i; 1114 struct stat sStat; 1113 struct stat sStat; 1115 Blob dir; 1114 Blob dir; 1116 char *zDir; 1115 char *zDir; 1117 1116 1118 file_canonical_name(zRepo, &dir); | 1117 file_canonical_name(zRepo, &dir, 0); 1119 zDir = blob_str(&dir); 1118 zDir = blob_str(&dir); 1120 if( file_isdir(zDir)==1 ){ 1119 if( file_isdir(zDir)==1 ){ 1121 if( chdir(zDir) || chroot(zDir) || chdir("/") ){ 1120 if( chdir(zDir) || chroot(zDir) || chdir("/") ){ 1122 fossil_fatal("unable to chroot into %s", zDir); 1121 fossil_fatal("unable to chroot into %s", zDir); 1123 } 1122 } 1124 zRepo = "/"; 1123 zRepo = "/"; 1125 }else{ 1124 }else{ ................................................................................................................................................................................ 1199 i++; 1198 i++; 1200 continue; 1199 continue; 1201 } 1200 } 1202 zRepo[j] = '.'; 1201 zRepo[j] = '.'; 1203 } 1202 } 1204 1203 1205 if( szFile<1024 ){ 1204 if( szFile<1024 ){ > 1205 set_base_url(); 1206 if( zNotFound ){ 1206 if( zNotFound ){ 1207 cgi_redirect(zNotFound); 1207 cgi_redirect(zNotFound); 1208 }else{ 1208 }else{ 1209 #ifdef FOSSIL_ENABLE_JSON 1209 #ifdef FOSSIL_ENABLE_JSON 1210 if(g.json.isJsonMode){ 1210 if(g.json.isJsonMode){ 1211 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); 1211 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); 1212 return; 1212 return; ................................................................................................................................................................................ 1286 zAltRepo += jj+1; 1286 zAltRepo += jj+1; 1287 }else{ 1287 }else{ 1288 zUser = "nobody"; 1288 zUser = "nobody"; 1289 } 1289 } 1290 if( g.zLogin==0 ) zUser = "nobody"; 1290 if( g.zLogin==0 ) zUser = "nobody"; 1291 if( zAltRepo[0]!='/' ){ 1291 if( zAltRepo[0]!='/' ){ 1292 zAltRepo = mprintf("%s/../%s", g.zRepositoryName, zAltRepo); 1292 zAltRepo = mprintf("%s/../%s", g.zRepositoryName, zAltRepo); 1293 file_simplify_name(zAltRepo, -1); | 1293 file_simplify_name(zAltRepo, -1, 0); 1294 } 1294 } 1295 db_close(1); 1295 db_close(1); 1296 db_open_repository(zAltRepo); 1296 db_open_repository(zAltRepo); 1297 login_as_user(zUser); 1297 login_as_user(zUser); 1298 g.perm.Password = 0; 1298 g.perm.Password = 0; 1299 zPath += i; 1299 zPath += i; 1300 nHost = g.zTop - g.zBaseURL; 1300 nHost = g.zTop - g.zBaseURL; ................................................................................................................................................................................ 1303 continue; 1303 continue; 1304 } 1304 } 1305 }else{ 1305 }else{ 1306 g.zExtra = 0; 1306 g.zExtra = 0; 1307 } 1307 } 1308 break; 1308 break; 1309 } 1309 } > 1310 #ifdef FOSSIL_ENABLE_JSON > 1311 /* > 1312 ** Workaround to allow us to customize some following behaviour for > 1313 ** JSON mode. The problem is, we don't always know if we're in JSON > 1314 ** mode at this point (namely, for GET mode we don't know but POST > 1315 ** we do), so we snoop g.zPath and cheat a bit. > 1316 */ > 1317 if( g.zPath && (0==strcmp("json",g.zPath)) ){ > 1318 g.json.isJsonMode = 1; > 1319 } > 1320 #endif 1310 if( g.zExtra ){ 1321 if( g.zExtra ){ 1311 /* CGI parameters get this treatment elsewhere, but places like getfile 1322 /* CGI parameters get this treatment elsewhere, but places like getfile 1312 ** will use g.zExtra directly. 1323 ** will use g.zExtra directly. 1313 ** Reminder: the login mechanism uses 'name' differently, and may 1324 ** Reminder: the login mechanism uses 'name' differently, and may 1314 ** eventually have a problem/collision with this. 1325 ** eventually have a problem/collision with this. > 1326 ** > 1327 ** Disabled by stephan when running in JSON mode because this > 1328 ** particular parameter name is very common and i have had no end > 1329 ** of grief with this handling. The JSON API never relies on the > 1330 ** handling below, and by disabling it in JSON mode i can remove > 1331 ** lots of special-case handling in several JSON handlers. 1315 */ 1332 */ > 1333 #ifdef FOSSIL_ENABLE_JSON > 1334 if(!g.json.isJsonMode){ > 1335 #endif 1316 dehttpize(g.zExtra); | 1336 dehttpize(g.zExtra); 1317 cgi_set_parameter_nocopy("name", g.zExtra); | 1337 cgi_set_parameter_nocopy("name", g.zExtra); > 1338 #ifdef FOSSIL_ENABLE_JSON > 1339 } > 1340 #endif 1318 } 1341 } 1319 1342 1320 /* Locate the method specified by the path and execute the function 1343 /* Locate the method specified by the path and execute the function 1321 ** that implements that method. 1344 ** that implements that method. 1322 */ 1345 */ 1323 if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) && 1346 if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) && 1324 name_search("not_found", aWebpage, count(aWebpage), &idx) ){ 1347 name_search("not_found", aWebpage, count(aWebpage), &idx) ){ ................................................................................................................................................................................ 1525 ** is disallowed. 1548 ** is disallowed. 1526 */ 1549 */ 1527 static void find_server_repository(int disallowDir){ 1550 static void find_server_repository(int disallowDir){ 1528 if( g.argc<3 ){ 1551 if( g.argc<3 ){ 1529 db_must_be_within_tree(); 1552 db_must_be_within_tree(); 1530 }else if( !disallowDir && file_isdir(g.argv[2])==1 ){ 1553 }else if( !disallowDir && file_isdir(g.argv[2])==1 ){ 1531 g.zRepositoryName = mprintf("%s", g.argv[2]); 1554 g.zRepositoryName = mprintf("%s", g.argv[2]); 1532 file_simplify_name(g.zRepositoryName, -1); | 1555 file_simplify_name(g.zRepositoryName, -1, 0); 1533 }else{ 1556 }else{ 1534 db_open_repository(g.argv[2]); 1557 db_open_repository(g.argv[2]); 1535 } 1558 } 1536 } 1559 } 1537 1560 1538 /* 1561 /* 1539 ** undocumented format: 1562 ** undocumented format:
Changes to src/main.mk
53 $(SRCDIR)/import.c \ 53 $(SRCDIR)/import.c \ 54 $(SRCDIR)/info.c \ 54 $(SRCDIR)/info.c \ 55 $(SRCDIR)/json.c \ 55 $(SRCDIR)/json.c \ 56 $(SRCDIR)/json_artifact.c \ 56 $(SRCDIR)/json_artifact.c \ 57 $(SRCDIR)/json_branch.c \ 57 $(SRCDIR)/json_branch.c \ 58 $(SRCDIR)/json_config.c \ 58 $(SRCDIR)/json_config.c \ 59 $(SRCDIR)/json_diff.c \ 59 $(SRCDIR)/json_diff.c \ > 60 $(SRCDIR)/json_dir.c \ > 61 $(SRCDIR)/json_finfo.c \ 60 $(SRCDIR)/json_login.c \ 62 $(SRCDIR)/json_login.c \ 61 $(SRCDIR)/json_query.c \ 63 $(SRCDIR)/json_query.c \ 62 $(SRCDIR)/json_report.c \ 64 $(SRCDIR)/json_report.c \ 63 $(SRCDIR)/json_tag.c \ 65 $(SRCDIR)/json_tag.c \ 64 $(SRCDIR)/json_timeline.c \ 66 $(SRCDIR)/json_timeline.c \ 65 $(SRCDIR)/json_user.c \ 67 $(SRCDIR)/json_user.c \ 66 $(SRCDIR)/json_wiki.c \ 68 $(SRCDIR)/json_wiki.c \ ................................................................................................................................................................................ 150 $(OBJDIR)/import_.c \ 152 $(OBJDIR)/import_.c \ 151 $(OBJDIR)/info_.c \ 153 $(OBJDIR)/info_.c \ 152 $(OBJDIR)/json_.c \ 154 $(OBJDIR)/json_.c \ 153 $(OBJDIR)/json_artifact_.c \ 155 $(OBJDIR)/json_artifact_.c \ 154 $(OBJDIR)/json_branch_.c \ 156 $(OBJDIR)/json_branch_.c \ 155 $(OBJDIR)/json_config_.c \ 157 $(OBJDIR)/json_config_.c \ 156 $(OBJDIR)/json_diff_.c \ 158 $(OBJDIR)/json_diff_.c \ > 159 $(OBJDIR)/json_dir_.c \ > 160 $(OBJDIR)/json_finfo_.c \ 157 $(OBJDIR)/json_login_.c \ 161 $(OBJDIR)/json_login_.c \ 158 $(OBJDIR)/json_query_.c \ 162 $(OBJDIR)/json_query_.c \ 159 $(OBJDIR)/json_report_.c \ 163 $(OBJDIR)/json_report_.c \ 160 $(OBJDIR)/json_tag_.c \ 164 $(OBJDIR)/json_tag_.c \ 161 $(OBJDIR)/json_timeline_.c \ 165 $(OBJDIR)/json_timeline_.c \ 162 $(OBJDIR)/json_user_.c \ 166 $(OBJDIR)/json_user_.c \ 163 $(OBJDIR)/json_wiki_.c \ 167 $(OBJDIR)/json_wiki_.c \ ................................................................................................................................................................................ 247 $(OBJDIR)/import.o \ 251 $(OBJDIR)/import.o \ 248 $(OBJDIR)/info.o \ 252 $(OBJDIR)/info.o \ 249 $(OBJDIR)/json.o \ 253 $(OBJDIR)/json.o \ 250 $(OBJDIR)/json_artifact.o \ 254 $(OBJDIR)/json_artifact.o \ 251 $(OBJDIR)/json_branch.o \ 255 $(OBJDIR)/json_branch.o \ 252 $(OBJDIR)/json_config.o \ 256 $(OBJDIR)/json_config.o \ 253 $(OBJDIR)/json_diff.o \ 257 $(OBJDIR)/json_diff.o \ > 258 $(OBJDIR)/json_dir.o \ > 259 $(OBJDIR)/json_finfo.o \ 254 $(OBJDIR)/json_login.o \ 260 $(OBJDIR)/json_login.o \ 255 $(OBJDIR)/json_query.o \ 261 $(OBJDIR)/json_query.o \ 256 $(OBJDIR)/json_report.o \ 262 $(OBJDIR)/json_report.o \ 257 $(OBJDIR)/json_tag.o \ 263 $(OBJDIR)/json_tag.o \ 258 $(OBJDIR)/json_timeline.o \ 264 $(OBJDIR)/json_timeline.o \ 259 $(OBJDIR)/json_user.o \ 265 $(OBJDIR)/json_user.o \ 260 $(OBJDIR)/json_wiki.o \ 266 $(OBJDIR)/json_wiki.o \ ................................................................................................................................................................................ 366 clean: 372 clean: 367 rm -rf $(OBJDIR)/* $(APPNAME) 373 rm -rf $(OBJDIR)/* $(APPNAME) 368 374 369 375 370 $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex 376 $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex 371 $(OBJDIR)/mkindex $(TRANS_SRC) >$@ 377 $(OBJDIR)/mkindex $(TRANS_SRC) >$@ 372 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/V 378 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/V 373 $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrep | 379 $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrep 374 touch $(OBJDIR)/headers 380 touch $(OBJDIR)/headers 375 $(OBJDIR)/headers: Makefile 381 $(OBJDIR)/headers: Makefile 376 $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/jso | 382 $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/jso 377 Makefile: 383 Makefile: 378 $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate 384 $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate 379 $(OBJDIR)/translate $(SRCDIR)/add.c >$(OBJDIR)/add_.c 385 $(OBJDIR)/translate $(SRCDIR)/add.c >$(OBJDIR)/add_.c 380 386 381 $(OBJDIR)/add.o: $(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h 387 $(OBJDIR)/add.o: $(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h 382 $(XTCC) -o $(OBJDIR)/add.o -c $(OBJDIR)/add_.c 388 $(XTCC) -o $(OBJDIR)/add.o -c $(OBJDIR)/add_.c 383 389 ................................................................................................................................................................................ 672 $(OBJDIR)/json_diff_.c: $(SRCDIR)/json_diff.c $(OBJDIR)/translate 678 $(OBJDIR)/json_diff_.c: $(SRCDIR)/json_diff.c $(OBJDIR)/translate 673 $(OBJDIR)/translate $(SRCDIR)/json_diff.c >$(OBJDIR)/json_diff_.c 679 $(OBJDIR)/translate $(SRCDIR)/json_diff.c >$(OBJDIR)/json_diff_.c 674 680 675 $(OBJDIR)/json_diff.o: $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h $(SRCDIR)/ 681 $(OBJDIR)/json_diff.o: $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h $(SRCDIR)/ 676 $(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c 682 $(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c 677 683 678 $(OBJDIR)/json_diff.h: $(OBJDIR)/headers 684 $(OBJDIR)/json_diff.h: $(OBJDIR)/headers > 685 $(OBJDIR)/json_dir_.c: $(SRCDIR)/json_dir.c $(OBJDIR)/translate > 686 $(OBJDIR)/translate $(SRCDIR)/json_dir.c >$(OBJDIR)/json_dir_.c > 687 > 688 $(OBJDIR)/json_dir.o: $(OBJDIR)/json_dir_.c $(OBJDIR)/json_dir.h $(SRCDIR)/co > 689 $(XTCC) -o $(OBJDIR)/json_dir.o -c $(OBJDIR)/json_dir_.c > 690 > 691 $(OBJDIR)/json_dir.h: $(OBJDIR)/headers > 692 $(OBJDIR)/json_finfo_.c: $(SRCDIR)/json_finfo.c $(OBJDIR)/translate > 693 $(OBJDIR)/translate $(SRCDIR)/json_finfo.c >$(OBJDIR)/json_finfo_.c > 694 > 695 $(OBJDIR)/json_finfo.o: $(OBJDIR)/json_finfo_.c $(OBJDIR)/json_finfo.h $(SRCDIR > 696 $(XTCC) -o $(OBJDIR)/json_finfo.o -c $(OBJDIR)/json_finfo_.c > 697 > 698 $(OBJDIR)/json_finfo.h: $(OBJDIR)/headers 679 $(OBJDIR)/json_login_.c: $(SRCDIR)/json_login.c $(OBJDIR)/translate 699 $(OBJDIR)/json_login_.c: $(SRCDIR)/json_login.c $(OBJDIR)/translate 680 $(OBJDIR)/translate $(SRCDIR)/json_login.c >$(OBJDIR)/json_login_.c 700 $(OBJDIR)/translate $(SRCDIR)/json_login.c >$(OBJDIR)/json_login_.c 681 701 682 $(OBJDIR)/json_login.o: $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h $(SRCDIR 702 $(OBJDIR)/json_login.o: $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h $(SRCDIR 683 $(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c 703 $(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c 684 704 685 $(OBJDIR)/json_login.h: $(OBJDIR)/headers 705 $(OBJDIR)/json_login.h: $(OBJDIR)/headers
Changes to src/makemake.tcl
56 import 56 import 57 info 57 info 58 json 58 json 59 json_artifact 59 json_artifact 60 json_branch 60 json_branch 61 json_config 61 json_config 62 json_diff 62 json_diff > 63 json_dir > 64 json_finfo 63 json_login 65 json_login 64 json_query 66 json_query 65 json_report 67 json_report 66 json_tag 68 json_tag 67 json_timeline 69 json_timeline 68 json_user 70 json_user 69 json_wiki 71 json_wiki ................................................................................................................................................................................ 256 append mhargs " \$(OBJDIR)/VERSION.h" 258 append mhargs " \$(OBJDIR)/VERSION.h" 257 writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex" 259 writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex" 258 writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >$@" 260 writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >$@" 259 writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/makeheaders \$( 261 writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/makeheaders \$( 260 writeln "\t\$(OBJDIR)/makeheaders $mhargs" 262 writeln "\t\$(OBJDIR)/makeheaders $mhargs" 261 writeln "\ttouch \$(OBJDIR)/headers" 263 writeln "\ttouch \$(OBJDIR)/headers" 262 writeln "\$(OBJDIR)/headers: Makefile" 264 writeln "\$(OBJDIR)/headers: Makefile" 263 writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \ | 265 writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \ 264 writeln "Makefile:" 266 writeln "Makefile:" 265 set extra_h(main) \$(OBJDIR)/page_index.h 267 set extra_h(main) \$(OBJDIR)/page_index.h 266 268 267 foreach s [lsort $src] { 269 foreach s [lsort $src] { 268 writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(OBJDIR)/translate" 270 writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(OBJDIR)/translate" 269 writeln "\t\$(OBJDIR)/translate \$(SRCDIR)/$s.c >\$(OBJDIR)/${s}_.c\n" 271 writeln "\t\$(OBJDIR)/translate \$(SRCDIR)/$s.c >\$(OBJDIR)/${s}_.c\n" 270 writeln "\$(OBJDIR)/$s.o:\t\$(OBJDIR)/${s}_.c \$(OBJDIR)/$s.h $extra_h($s) \$( 272 writeln "\$(OBJDIR)/$s.o:\t\$(OBJDIR)/${s}_.c \$(OBJDIR)/$s.h $extra_h($s) \$( ................................................................................................................................................................................ 465 467 466 #### These libraries MUST appear in the same order as they do for Tcl 468 #### These libraries MUST appear in the same order as they do for Tcl 467 # or linking with it will not work (exact reason unknown). 469 # or linking with it will not work (exact reason unknown). 468 # 470 # 469 ifdef FOSSIL_ENABLE_TCL 471 ifdef FOSSIL_ENABLE_TCL 470 LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 472 LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 471 else 473 else 472 LIB += -lws2_32 | 474 LIB += -lkernel32 -lws2_32 473 endif 475 endif 474 476 475 #### Tcl shell for use in running the fossil test suite. This is only 477 #### Tcl shell for use in running the fossil test suite. This is only 476 # used for testing. 478 # used for testing. 477 # 479 # 478 TCLSH = tclsh 480 TCLSH = tclsh 479 481 ................................................................................................................................................................................ 608 writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c" 610 writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c" 609 set opt $SQLITE_OPTIONS 611 set opt $SQLITE_OPTIONS 610 writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" 612 writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" 611 613 612 set opt {} 614 set opt {} 613 writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" 615 writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" 614 writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_am 616 writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_am 615 writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \ | 617 writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \ 616 618 617 writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" 619 writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" 618 set opt {-Dmain=sqlite3_shell} 620 set opt {-Dmain=sqlite3_shell} 619 append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" 621 append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" 620 writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n" 622 writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n" 621 623 622 writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" 624 writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" ................................................................................................................................................................................ 665 667 666 #SSL = -DFOSSIL_ENABLE_SSL=1 668 #SSL = -DFOSSIL_ENABLE_SSL=1 667 SSL = 669 SSL = 668 670 669 CFLAGS = -o 671 CFLAGS = -o 670 BCC = $(DMDIR)\bin\dmc $(CFLAGS) 672 BCC = $(DMDIR)\bin\dmc $(CFLAGS) 671 TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) 673 TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) 672 LIBS = $(DMDIR)\extra\lib\ zlib wsock32 | 674 LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 673 } 675 } 674 writeln "SQLITE_OPTIONS = $SQLITE_OPTIONS\n" 676 writeln "SQLITE_OPTIONS = $SQLITE_OPTIONS\n" 675 writeln -nonewline "SRC = " 677 writeln -nonewline "SRC = " 676 foreach s [lsort $src] { 678 foreach s [lsort $src] { 677 writeln -nonewline "${s}_.c " 679 writeln -nonewline "${s}_.c " 678 } 680 } 679 writeln "\n" 681 writeln "\n" ................................................................................................................................................................................ 752 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E 754 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E 753 755 754 $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h 756 $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h 755 $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h 757 $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h 756 $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h 758 $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h 757 $(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h 759 $(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h 758 $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h 760 $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h > 761 $(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h > 762 $(OBJDIR)\json_finfo$O : $(SRCDIR)\json_detail.h 759 $(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h 763 $(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h 760 $(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h 764 $(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h 761 $(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h 765 $(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h 762 $(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h 766 $(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h 763 $(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h 767 $(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h 764 $(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h 768 $(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h 765 $(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h 769 $(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h ................................................................................................................................................................................ 910 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E 914 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E 911 915 912 $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h 916 $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h 913 $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h 917 $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h 914 $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h 918 $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h 915 $(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h 919 $(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h 916 $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h 920 $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h > 921 $(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h > 922 $(OBJDIR)\json_finfo$O : $(SRCDIR)\json_detail.h 917 $(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h 923 $(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h 918 $(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h 924 $(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h 919 $(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h 925 $(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h 920 $(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h 926 $(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h 921 $(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h 927 $(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h 922 $(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h 928 $(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h 923 $(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h 929 $(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
Changes to src/merge3.c
400 char *zPivot; /* Name of the pivot file */ 400 char *zPivot; /* Name of the pivot file */ 401 char *zOrig; /* Name of the original content file */ 401 char *zOrig; /* Name of the original content file */ 402 char *zOther; /* Name of the merge file */ 402 char *zOther; /* Name of the merge file */ 403 403 404 blob_read_from_file(&v1, zV1); 404 blob_read_from_file(&v1, zV1); 405 rc = blob_merge(pPivot, &v1, pV2, pOut); 405 rc = blob_merge(pPivot, &v1, pV2, pOut); 406 if( rc!=0 ){ 406 if( rc!=0 ){ 407 const char *zGMerge; /* Name of the gmerge command */ < 408 < 409 zPivot = file_newname(zV1, "baseline", 1); 407 zPivot = file_newname(zV1, "baseline", 1); 410 blob_write_to_file(pPivot, zPivot); 408 blob_write_to_file(pPivot, zPivot); 411 zOrig = file_newname(zV1, "original", 1); 409 zOrig = file_newname(zV1, "original", 1); 412 blob_write_to_file(&v1, zOrig); 410 blob_write_to_file(&v1, zOrig); 413 zOther = file_newname(zV1, "merge", 1); 411 zOther = file_newname(zV1, "merge", 1); 414 blob_write_to_file(pV2, zOther); 412 blob_write_to_file(pV2, zOther); 415 } 413 }
Changes to src/name.c
67 ** match any known object. Or return -1 if the name is ambiguious. 67 ** match any known object. Or return -1 if the name is ambiguious. 68 ** 68 ** 69 ** The zType parameter specifies the type of artifact: ci, t, w, e, g. 69 ** The zType parameter specifies the type of artifact: ci, t, w, e, g. 70 ** If zType is NULL or "" or "*" then any type of artifact will serve. 70 ** If zType is NULL or "" or "*" then any type of artifact will serve. 71 ** zType is "ci" in most use cases since we are usually searching for 71 ** zType is "ci" in most use cases since we are usually searching for 72 ** a check-in. 72 ** a check-in. 73 */ 73 */ 74 static int symbolic_name_to_rid(const char *zTag, const char *zType){ | 74 int symbolic_name_to_rid(const char *zTag, const char *zType){ 75 int vid; 75 int vid; 76 int rid = 0; 76 int rid = 0; 77 int nTag; 77 int nTag; 78 int i; 78 int i; 79 79 80 if( zType==0 || zType[0]==0 ) zType = "*"; 80 if( zType==0 || zType[0]==0 ) zType = "*"; 81 if( zTag==0 || zTag[0]==0 ) return 0; 81 if( zTag==0 || zTag[0]==0 ) return 0; ................................................................................................................................................................................ 271 return 1; 271 return 1; 272 }else{ 272 }else{ 273 blob_reset(pName); 273 blob_reset(pName); 274 db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid); 274 db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid); 275 return 0; 275 return 0; 276 } 276 } 277 } 277 } > 278 > 279 /* > 280 ** This routine is similar to name_to_uuid() except in the form it > 281 ** takes its parameters and returns its value, and in that it does not > 282 ** treat errors as fatal. zName must be a UUID, as described for > 283 ** name_to_uuid(). zType is also as described for that function. If > 284 ** zName does not resolve, 0 is returned. If it is ambiguous, a > 285 ** negative value is returned. On success the rid is returned and > 286 ** pUuid (if it is not NULL) is set to the a newly-allocated string, > 287 ** the full UUID, which must eventually be free()d by the caller. > 288 */ > 289 int name_to_uuid2(char const *zName, const char *zType, char **pUuid){ > 290 int rid = symbolic_name_to_rid(zName, zType); > 291 if((rid>0) && pUuid){ > 292 *pUuid = db_text(NULL, "SELECT uuid FROM blob WHERE rid=%d", rid); > 293 } > 294 return rid; > 295 } > 296 > 297 278 298 279 /* 299 /* 280 ** COMMAND: test-name-to-id 300 ** COMMAND: test-name-to-id 281 ** 301 ** 282 ** Convert a name to a full artifact ID. 302 ** Convert a name to a full artifact ID. 283 */ 303 */ 284 void test_name_to_id(void){ 304 void test_name_to_id(void){
Changes to src/setup.c
82 setup_menu_entry("CSS", "setup_editcss", 82 setup_menu_entry("CSS", "setup_editcss", 83 "Edit the Cascading Style Sheet used by all pages of this repository"); 83 "Edit the Cascading Style Sheet used by all pages of this repository"); 84 setup_menu_entry("Header", "setup_header", 84 setup_menu_entry("Header", "setup_header", 85 "Edit HTML text inserted at the top of every page"); 85 "Edit HTML text inserted at the top of every page"); 86 setup_menu_entry("Footer", "setup_footer", 86 setup_menu_entry("Footer", "setup_footer", 87 "Edit HTML text inserted at the bottom of every page"); 87 "Edit HTML text inserted at the bottom of every page"); 88 setup_menu_entry("Logo", "setup_logo", 88 setup_menu_entry("Logo", "setup_logo", 89 "Change the logo image for the server"); | 89 "Change the logo and background images for the server"); 90 setup_menu_entry("Shunned", "shun", 90 setup_menu_entry("Shunned", "shun", 91 "Show artifacts that are shunned by this repository"); 91 "Show artifacts that are shunned by this repository"); 92 setup_menu_entry("Log", "rcvfromlist", 92 setup_menu_entry("Log", "rcvfromlist", 93 "A record of received artifacts and their sources"); 93 "A record of received artifacts and their sources"); 94 setup_menu_entry("User-Log", "access_log", 94 setup_menu_entry("User-Log", "access_log", 95 "A record of login attempts"); 95 "A record of login attempts"); 96 setup_menu_entry("Stats", "stat", 96 setup_menu_entry("Stats", "stat", ................................................................................................................................................................................ 866 "remote_user_ok", "remote_user_ok", 0); 866 "remote_user_ok", "remote_user_ok", 0); 867 @ <p>When enabled, if the REMOTE_USER environment variable is set to the 867 @ <p>When enabled, if the REMOTE_USER environment variable is set to the 868 @ login name of a valid user and no other login credentials are available, 868 @ login name of a valid user and no other login credentials are available, 869 @ then the REMOTE_USER is accepted as an authenticated user. 869 @ then the REMOTE_USER is accepted as an authenticated user. 870 @ </p> 870 @ </p> 871 @ 871 @ 872 @ <hr /> 872 @ <hr /> 873 entry_attribute("IP address turns used in login cookie", 3, "ip-prefix-terms", | 873 entry_attribute("IP address terms used in login cookie", 3, 874 "2"); | 874 "ip-prefix-terms", "ipt", "2"); 875 @ <p>The number of octets of of the IP address used in the login cookie. Set | 875 @ <p>The number of octets of of the IP address used in the login cookie. 876 @ zero to omit the IP address from the login cookie. A value of 2 is recommen | 876 @ Set to zero to omit the IP address from the login cookie. A value of > 877 @ 2 is recommended. 877 @ </p> 878 @ </p> 878 @ 879 @ 879 @ <hr /> 880 @ <hr /> 880 entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766"); 881 entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766"); 881 @ <p>The number of hours for which a login is valid. This must be a 882 @ <p>The number of hours for which a login is valid. This must be a 882 @ positive number. The default is 8760 hours which is approximately equal 883 @ positive number. The default is 8760 hours which is approximately equal 883 @ to a year.</p> 884 @ to a year.</p> ................................................................................................................................................................................ 948 const char *zPw = PD("pw", ""); 949 const char *zPw = PD("pw", ""); 949 const char *zNewName = PD("newname", "New Login Group"); 950 const char *zNewName = PD("newname", "New Login Group"); 950 951 951 login_check_credentials(); 952 login_check_credentials(); 952 if( !g.perm.Setup ){ 953 if( !g.perm.Setup ){ 953 login_needed(); 954 login_needed(); 954 } 955 } 955 file_canonical_name(g.zRepositoryName, &fullName); | 956 file_canonical_name(g.zRepositoryName, &fullName, 0); 956 zSelfRepo = mprintf(blob_str(&fullName)); 957 zSelfRepo = mprintf(blob_str(&fullName)); 957 blob_reset(&fullName); 958 blob_reset(&fullName); 958 if( P("join")!=0 ){ 959 if( P("join")!=0 ){ 959 login_group_join(zRepo, zLogin, zPw, zNewName, &zErrMsg); 960 login_group_join(zRepo, zLogin, zPw, zNewName, &zErrMsg); 960 }else if( P("leave") ){ 961 }else if( P("leave") ){ 961 login_group_leave(&zErrMsg); 962 login_group_leave(&zErrMsg); 962 } 963 } ................................................................................................................................................................................ 1316 db_end_transaction(0); 1317 db_end_transaction(0); 1317 } 1318 } 1318 1319 1319 /* 1320 /* 1320 ** WEBPAGE: setup_logo 1321 ** WEBPAGE: setup_logo 1321 */ 1322 */ 1322 void setup_logo(void){ 1323 void setup_logo(void){ 1323 const char *zMime = db_get("logo-mimetype","image/gif"); | 1324 const char *zLogoMime = db_get("logo-mimetype","image/gif"); > 1325 const char *aLogoImg = P("logoim"); > 1326 int szLogoImg = atoi(PD("logoim:bytes","0")); > 1327 const char *zBgMime = db_get("background-mimetype","image/gif"); 1324 const char *aImg = P("im"); | 1328 const char *aBgImg = P("bgim"); 1325 int szImg = atoi(PD("im:bytes","0")); | 1329 int szBgImg = atoi(PD("bgim:bytes","0")); > 1330 if( szLogoImg>0 ){ > 1331 zLogoMime = PD("logoim:mimetype","image/gif"); > 1332 } 1326 if( szImg>0 ){ | 1333 if( szBgImg>0 ){ 1327 zMime = PD("im:mimetype","image/gif"); | 1334 zBgMime = PD("bgim:mimetype","image/gif"); 1328 } 1335 } 1329 login_check_credentials(); 1336 login_check_credentials(); 1330 if( !g.perm.Setup ){ 1337 if( !g.perm.Setup ){ 1331 login_needed(); 1338 login_needed(); 1332 } 1339 } 1333 db_begin_transaction(); 1340 db_begin_transaction(); 1334 if( P("set")!=0 && zMime && zMime[0] && szImg>0 ){ | 1341 if( P("setlogo")!=0 && zLogoMime && zLogoMime[0] && szLogoImg>0 ){ 1335 Blob img; 1342 Blob img; 1336 Stmt ins; 1343 Stmt ins; 1337 blob_init(&img, aImg, szImg); | 1344 blob_init(&img, aLogoImg, szLogoImg); 1338 db_prepare(&ins, 1345 db_prepare(&ins, 1339 "REPLACE INTO config(name,value,mtime)" 1346 "REPLACE INTO config(name,value,mtime)" 1340 " VALUES('logo-image',:bytes,now())" 1347 " VALUES('logo-image',:bytes,now())" 1341 ); 1348 ); 1342 db_bind_blob(&ins, ":bytes", &img); 1349 db_bind_blob(&ins, ":bytes", &img); 1343 db_step(&ins); 1350 db_step(&ins); 1344 db_finalize(&ins); 1351 db_finalize(&ins); 1345 db_multi_exec( 1352 db_multi_exec( 1346 "REPLACE INTO config(name,value,mtime) VALUES('logo-mimetype',%Q,now())", 1353 "REPLACE INTO config(name,value,mtime) VALUES('logo-mimetype',%Q,now())", > 1354 zLogoMime > 1355 ); > 1356 db_end_transaction(0); > 1357 cgi_redirect("setup_logo"); > 1358 }else if( P("clrlogo")!=0 ){ > 1359 db_multi_exec( > 1360 "DELETE FROM config WHERE name IN " > 1361 "('logo-image','logo-mimetype')" > 1362 ); > 1363 db_end_transaction(0); > 1364 cgi_redirect("setup_logo"); > 1365 }else if( P("setbg")!=0 && zBgMime && zBgMime[0] && szBgImg>0 ){ > 1366