Fossil SCM
Allow for multiple captcha-secret values. The primary is still 'captcha-secret'. Backups are in 'captcha-secret-N' where N is a small integer. The backups are only valid for 6 hours. This allows the captcha-secret to be changed without disrupting anonymous logins and/or captcha dialogs that are in progress when the secret changes.
Commit
8659d84aff2874737b58cda1b23f781f17509e71f16c32bc6c28d03380901045
Parent
06a72cea3aa06bd…
5 files changed
+3
-3
+53
-15
+1
-1
+32
-19
+1
-1
+3
-3
| --- src/alerts.c | ||
| +++ src/alerts.c | ||
| @@ -1649,11 +1649,11 @@ | ||
| 1649 | 1649 | uSeed = strtoul(P("captchaseed"),0,10); |
| 1650 | 1650 | zInit = P("captcha"); |
| 1651 | 1651 | }else{ |
| 1652 | 1652 | uSeed = captcha_seed(); |
| 1653 | 1653 | } |
| 1654 | - zDecoded = captcha_decode(uSeed); | |
| 1654 | + zDecoded = captcha_decode(uSeed, 0); | |
| 1655 | 1655 | zCaptcha = captcha_render(zDecoded); |
| 1656 | 1656 | @ <tr> |
| 1657 | 1657 | @ <td class="form_label">Security Code:</td> |
| 1658 | 1658 | @ <td><input type="text" name="captcha" value="%h(zInit)" size="30"> |
| 1659 | 1659 | captcha_speakit_button(uSeed, "Speak the code"); |
| @@ -2353,11 +2353,11 @@ | ||
| 2353 | 2353 | if( eErr==1 ){ |
| 2354 | 2354 | @ <td><span class="loginError">← %h(zErr)</span></td> |
| 2355 | 2355 | } |
| 2356 | 2356 | @ </tr> |
| 2357 | 2357 | uSeed = captcha_seed(); |
| 2358 | - zDecoded = captcha_decode(uSeed); | |
| 2358 | + zDecoded = captcha_decode(uSeed, 0); | |
| 2359 | 2359 | zCaptcha = captcha_render(zDecoded); |
| 2360 | 2360 | @ <tr> |
| 2361 | 2361 | @ <td class="form_label">Security Code:</td> |
| 2362 | 2362 | @ <td><input type="text" name="captcha" value="" size="30"> |
| 2363 | 2363 | captcha_speakit_button(uSeed, "Speak the code"); |
| @@ -3356,11 +3356,11 @@ | ||
| 3356 | 3356 | style_finish_page(); |
| 3357 | 3357 | return; |
| 3358 | 3358 | } |
| 3359 | 3359 | if( captcha_needed() ){ |
| 3360 | 3360 | uSeed = captcha_seed(); |
| 3361 | - zDecoded = captcha_decode(uSeed); | |
| 3361 | + zDecoded = captcha_decode(uSeed, 0); | |
| 3362 | 3362 | zCaptcha = captcha_render(zDecoded); |
| 3363 | 3363 | } |
| 3364 | 3364 | style_set_current_feature("alerts"); |
| 3365 | 3365 | style_header("Message To Administrator"); |
| 3366 | 3366 | form_begin(0, "%R/contact_admin"); |
| 3367 | 3367 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -1649,11 +1649,11 @@ | |
| 1649 | uSeed = strtoul(P("captchaseed"),0,10); |
| 1650 | zInit = P("captcha"); |
| 1651 | }else{ |
| 1652 | uSeed = captcha_seed(); |
| 1653 | } |
| 1654 | zDecoded = captcha_decode(uSeed); |
| 1655 | zCaptcha = captcha_render(zDecoded); |
| 1656 | @ <tr> |
| 1657 | @ <td class="form_label">Security Code:</td> |
| 1658 | @ <td><input type="text" name="captcha" value="%h(zInit)" size="30"> |
| 1659 | captcha_speakit_button(uSeed, "Speak the code"); |
| @@ -2353,11 +2353,11 @@ | |
| 2353 | if( eErr==1 ){ |
| 2354 | @ <td><span class="loginError">← %h(zErr)</span></td> |
| 2355 | } |
| 2356 | @ </tr> |
| 2357 | uSeed = captcha_seed(); |
| 2358 | zDecoded = captcha_decode(uSeed); |
| 2359 | zCaptcha = captcha_render(zDecoded); |
| 2360 | @ <tr> |
| 2361 | @ <td class="form_label">Security Code:</td> |
| 2362 | @ <td><input type="text" name="captcha" value="" size="30"> |
| 2363 | captcha_speakit_button(uSeed, "Speak the code"); |
| @@ -3356,11 +3356,11 @@ | |
| 3356 | style_finish_page(); |
| 3357 | return; |
| 3358 | } |
| 3359 | if( captcha_needed() ){ |
| 3360 | uSeed = captcha_seed(); |
| 3361 | zDecoded = captcha_decode(uSeed); |
| 3362 | zCaptcha = captcha_render(zDecoded); |
| 3363 | } |
| 3364 | style_set_current_feature("alerts"); |
| 3365 | style_header("Message To Administrator"); |
| 3366 | form_begin(0, "%R/contact_admin"); |
| 3367 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -1649,11 +1649,11 @@ | |
| 1649 | uSeed = strtoul(P("captchaseed"),0,10); |
| 1650 | zInit = P("captcha"); |
| 1651 | }else{ |
| 1652 | uSeed = captcha_seed(); |
| 1653 | } |
| 1654 | zDecoded = captcha_decode(uSeed, 0); |
| 1655 | zCaptcha = captcha_render(zDecoded); |
| 1656 | @ <tr> |
| 1657 | @ <td class="form_label">Security Code:</td> |
| 1658 | @ <td><input type="text" name="captcha" value="%h(zInit)" size="30"> |
| 1659 | captcha_speakit_button(uSeed, "Speak the code"); |
| @@ -2353,11 +2353,11 @@ | |
| 2353 | if( eErr==1 ){ |
| 2354 | @ <td><span class="loginError">← %h(zErr)</span></td> |
| 2355 | } |
| 2356 | @ </tr> |
| 2357 | uSeed = captcha_seed(); |
| 2358 | zDecoded = captcha_decode(uSeed, 0); |
| 2359 | zCaptcha = captcha_render(zDecoded); |
| 2360 | @ <tr> |
| 2361 | @ <td class="form_label">Security Code:</td> |
| 2362 | @ <td><input type="text" name="captcha" value="" size="30"> |
| 2363 | captcha_speakit_button(uSeed, "Speak the code"); |
| @@ -3356,11 +3356,11 @@ | |
| 3356 | style_finish_page(); |
| 3357 | return; |
| 3358 | } |
| 3359 | if( captcha_needed() ){ |
| 3360 | uSeed = captcha_seed(); |
| 3361 | zDecoded = captcha_decode(uSeed, 0); |
| 3362 | zCaptcha = captcha_render(zDecoded); |
| 3363 | } |
| 3364 | style_set_current_feature("alerts"); |
| 3365 | style_header("Message To Administrator"); |
| 3366 | form_begin(0, "%R/contact_admin"); |
| 3367 |
+53
-15
| --- src/captcha.c | ||
| +++ src/captcha.c | ||
| @@ -508,39 +508,74 @@ | ||
| 508 | 508 | unsigned int x; |
| 509 | 509 | sqlite3_randomness(sizeof(x), &x); |
| 510 | 510 | x &= 0x7fffffff; |
| 511 | 511 | return x; |
| 512 | 512 | } |
| 513 | + | |
| 514 | +/* | |
| 515 | +** Return the value of the N-th more recent captcha-secret. The | |
| 516 | +** most recent captch-secret is 0. Others are prior captcha-secrets | |
| 517 | +** that have expired, but are retained for a limited period of time | |
| 518 | +** so that pending anonymous login cookies and/or captcha dialogs | |
| 519 | +** don't malfunction when the captcha-secret changes. | |
| 520 | +** | |
| 521 | +** Clients should start by using the 0-th captcha-secret. Only if | |
| 522 | +** that one does not work should they advance to 1 and 2 and so forth, | |
| 523 | +** until this routine returns a NULL pointer. | |
| 524 | +** | |
| 525 | +** The value returned is a string obtained from fossil_malloc() and | |
| 526 | +** should be freed by the caller. | |
| 527 | +** | |
| 528 | +** The 0-th captcha secret is the value of Config.Name='captcha-secret'. | |
| 529 | +** For N>0, the value is in Config.Name='captcha-secret-$N'. | |
| 530 | +*/ | |
| 531 | +char *captcha_secret(int N){ | |
| 532 | + if( N==0 ){ | |
| 533 | + return db_text(0, "SELECT value FROM config WHERE name='captcha-secret'"); | |
| 534 | + }else{ | |
| 535 | + return db_text(0, | |
| 536 | + "SELECT value FROM config" | |
| 537 | + " WHERE name='captcha-secret-%d'" | |
| 538 | + " AND mtime>unixepoch('now','-6 hours')", N); | |
| 539 | + } | |
| 540 | +} | |
| 513 | 541 | |
| 514 | 542 | /* |
| 515 | 543 | ** Translate a captcha seed value into the captcha password string. |
| 516 | 544 | ** The returned string is static and overwritten on each call to |
| 517 | 545 | ** this function. |
| 546 | +** | |
| 547 | +** Use the N-th captcha secret to compute the password. When N==0, | |
| 548 | +** a valid password is always returned. A new captcha-secret will | |
| 549 | +** be created if necessary. But for N>0, the return value might | |
| 550 | +** be NULL to indicate that there is no N-th captcha-secret. | |
| 518 | 551 | */ |
| 519 | -const char *captcha_decode(unsigned int seed){ | |
| 520 | - const char *zSecret; | |
| 552 | +const char *captcha_decode(unsigned int seed, int N){ | |
| 553 | + char *zSecret; | |
| 521 | 554 | const char *z; |
| 522 | 555 | Blob b; |
| 523 | 556 | static char zRes[20]; |
| 524 | 557 | |
| 525 | - zSecret = db_get("captcha-secret", 0); | |
| 558 | + zSecret = captcha_secret(N); | |
| 526 | 559 | if( zSecret==0 ){ |
| 560 | + if( N>0 ) return 0; | |
| 527 | 561 | db_unprotect(PROTECT_CONFIG); |
| 528 | 562 | db_multi_exec( |
| 529 | 563 | "REPLACE INTO config(name,value)" |
| 530 | 564 | " VALUES('captcha-secret', lower(hex(randomblob(20))));" |
| 531 | 565 | ); |
| 532 | 566 | db_protect_pop(); |
| 533 | - zSecret = db_get("captcha-secret", 0); | |
| 567 | + zSecret = captcha_secret(0); | |
| 534 | 568 | assert( zSecret!=0 ); |
| 535 | 569 | } |
| 536 | 570 | blob_init(&b, 0, 0); |
| 537 | 571 | blob_appendf(&b, "%s-%x", zSecret, seed); |
| 538 | 572 | sha1sum_blob(&b, &b); |
| 539 | 573 | z = blob_buffer(&b); |
| 540 | 574 | memcpy(zRes, z, 8); |
| 541 | 575 | zRes[8] = 0; |
| 576 | + fossil_free(zSecret); | |
| 542 | 577 | return zRes; |
| 543 | 578 | } |
| 544 | 579 | |
| 545 | 580 | /* |
| 546 | 581 | ** Return true if a CAPTCHA is required for editing wiki or tickets or for |
| @@ -569,26 +604,29 @@ | ||
| 569 | 604 | const char *zSeed; |
| 570 | 605 | const char *zEntered; |
| 571 | 606 | const char *zDecode; |
| 572 | 607 | char z[30]; |
| 573 | 608 | int i; |
| 609 | + int n = 0; | |
| 574 | 610 | if( !bAlwaysNeeded && !captcha_needed() ){ |
| 575 | 611 | return 1; /* No captcha needed */ |
| 576 | 612 | } |
| 577 | 613 | zSeed = P("captchaseed"); |
| 578 | 614 | if( zSeed==0 ) return 0; |
| 579 | 615 | zEntered = P("captcha"); |
| 580 | 616 | if( zEntered==0 || strlen(zEntered)!=8 ) return 0; |
| 581 | - zDecode = captcha_decode((unsigned int)atoi(zSeed)); | |
| 582 | - assert( strlen(zDecode)==8 ); | |
| 583 | - for(i=0; i<8; i++){ | |
| 584 | - char c = zEntered[i]; | |
| 585 | - if( c>='A' && c<='F' ) c += 'a' - 'A'; | |
| 586 | - if( c=='O' ) c = '0'; | |
| 587 | - z[i] = c; | |
| 588 | - } | |
| 589 | - if( strncmp(zDecode,z,8)!=0 ) return 0; | |
| 617 | + do{ | |
| 618 | + zDecode = captcha_decode((unsigned int)atoi(zSeed), n++); | |
| 619 | + if( zDecode==0 ) return 0; | |
| 620 | + assert( strlen(zDecode)==8 ); | |
| 621 | + for(i=0; i<8; i++){ | |
| 622 | + char c = zEntered[i]; | |
| 623 | + if( c>='A' && c<='F' ) c += 'a' - 'A'; | |
| 624 | + if( c=='O' ) c = '0'; | |
| 625 | + z[i] = c; | |
| 626 | + } | |
| 627 | + }while( strncmp(zDecode,z,8)!=0 ); | |
| 590 | 628 | return 1; |
| 591 | 629 | } |
| 592 | 630 | |
| 593 | 631 | /* |
| 594 | 632 | ** Generate a captcha display together with the necessary hidden parameter |
| @@ -607,11 +645,11 @@ | ||
| 607 | 645 | const char *zDecoded; |
| 608 | 646 | char *zCaptcha; |
| 609 | 647 | |
| 610 | 648 | if( !captcha_needed() && (mFlags & 0x02)==0 ) return; |
| 611 | 649 | uSeed = captcha_seed(); |
| 612 | - zDecoded = captcha_decode(uSeed); | |
| 650 | + zDecoded = captcha_decode(uSeed, 0); | |
| 613 | 651 | zCaptcha = captcha_render(zDecoded); |
| 614 | 652 | @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> |
| 615 | 653 | @ %h(zCaptcha) |
| 616 | 654 | @ </pre> |
| 617 | 655 | @ Enter security code shown above: |
| @@ -808,11 +846,11 @@ | ||
| 808 | 846 | ** Return a WAV file that pronounces the digits of the captcha that |
| 809 | 847 | ** is determined by the seed given in the name= query parameter. |
| 810 | 848 | */ |
| 811 | 849 | void captcha_wav_page(void){ |
| 812 | 850 | const char *zSeed = PD("name","0"); |
| 813 | - const char *zDecode = captcha_decode((unsigned int)atoi(zSeed)); | |
| 851 | + const char *zDecode = captcha_decode((unsigned int)atoi(zSeed), 0); | |
| 814 | 852 | Blob audio; |
| 815 | 853 | captcha_wav(zDecode, &audio); |
| 816 | 854 | cgi_set_content_type("audio/wav"); |
| 817 | 855 | cgi_set_content(&audio); |
| 818 | 856 | } |
| 819 | 857 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -508,39 +508,74 @@ | |
| 508 | unsigned int x; |
| 509 | sqlite3_randomness(sizeof(x), &x); |
| 510 | x &= 0x7fffffff; |
| 511 | return x; |
| 512 | } |
| 513 | |
| 514 | /* |
| 515 | ** Translate a captcha seed value into the captcha password string. |
| 516 | ** The returned string is static and overwritten on each call to |
| 517 | ** this function. |
| 518 | */ |
| 519 | const char *captcha_decode(unsigned int seed){ |
| 520 | const char *zSecret; |
| 521 | const char *z; |
| 522 | Blob b; |
| 523 | static char zRes[20]; |
| 524 | |
| 525 | zSecret = db_get("captcha-secret", 0); |
| 526 | if( zSecret==0 ){ |
| 527 | db_unprotect(PROTECT_CONFIG); |
| 528 | db_multi_exec( |
| 529 | "REPLACE INTO config(name,value)" |
| 530 | " VALUES('captcha-secret', lower(hex(randomblob(20))));" |
| 531 | ); |
| 532 | db_protect_pop(); |
| 533 | zSecret = db_get("captcha-secret", 0); |
| 534 | assert( zSecret!=0 ); |
| 535 | } |
| 536 | blob_init(&b, 0, 0); |
| 537 | blob_appendf(&b, "%s-%x", zSecret, seed); |
| 538 | sha1sum_blob(&b, &b); |
| 539 | z = blob_buffer(&b); |
| 540 | memcpy(zRes, z, 8); |
| 541 | zRes[8] = 0; |
| 542 | return zRes; |
| 543 | } |
| 544 | |
| 545 | /* |
| 546 | ** Return true if a CAPTCHA is required for editing wiki or tickets or for |
| @@ -569,26 +604,29 @@ | |
| 569 | const char *zSeed; |
| 570 | const char *zEntered; |
| 571 | const char *zDecode; |
| 572 | char z[30]; |
| 573 | int i; |
| 574 | if( !bAlwaysNeeded && !captcha_needed() ){ |
| 575 | return 1; /* No captcha needed */ |
| 576 | } |
| 577 | zSeed = P("captchaseed"); |
| 578 | if( zSeed==0 ) return 0; |
| 579 | zEntered = P("captcha"); |
| 580 | if( zEntered==0 || strlen(zEntered)!=8 ) return 0; |
| 581 | zDecode = captcha_decode((unsigned int)atoi(zSeed)); |
| 582 | assert( strlen(zDecode)==8 ); |
| 583 | for(i=0; i<8; i++){ |
| 584 | char c = zEntered[i]; |
| 585 | if( c>='A' && c<='F' ) c += 'a' - 'A'; |
| 586 | if( c=='O' ) c = '0'; |
| 587 | z[i] = c; |
| 588 | } |
| 589 | if( strncmp(zDecode,z,8)!=0 ) return 0; |
| 590 | return 1; |
| 591 | } |
| 592 | |
| 593 | /* |
| 594 | ** Generate a captcha display together with the necessary hidden parameter |
| @@ -607,11 +645,11 @@ | |
| 607 | const char *zDecoded; |
| 608 | char *zCaptcha; |
| 609 | |
| 610 | if( !captcha_needed() && (mFlags & 0x02)==0 ) return; |
| 611 | uSeed = captcha_seed(); |
| 612 | zDecoded = captcha_decode(uSeed); |
| 613 | zCaptcha = captcha_render(zDecoded); |
| 614 | @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> |
| 615 | @ %h(zCaptcha) |
| 616 | @ </pre> |
| 617 | @ Enter security code shown above: |
| @@ -808,11 +846,11 @@ | |
| 808 | ** Return a WAV file that pronounces the digits of the captcha that |
| 809 | ** is determined by the seed given in the name= query parameter. |
| 810 | */ |
| 811 | void captcha_wav_page(void){ |
| 812 | const char *zSeed = PD("name","0"); |
| 813 | const char *zDecode = captcha_decode((unsigned int)atoi(zSeed)); |
| 814 | Blob audio; |
| 815 | captcha_wav(zDecode, &audio); |
| 816 | cgi_set_content_type("audio/wav"); |
| 817 | cgi_set_content(&audio); |
| 818 | } |
| 819 |
| --- src/captcha.c | |
| +++ src/captcha.c | |
| @@ -508,39 +508,74 @@ | |
| 508 | unsigned int x; |
| 509 | sqlite3_randomness(sizeof(x), &x); |
| 510 | x &= 0x7fffffff; |
| 511 | return x; |
| 512 | } |
| 513 | |
| 514 | /* |
| 515 | ** Return the value of the N-th more recent captcha-secret. The |
| 516 | ** most recent captch-secret is 0. Others are prior captcha-secrets |
| 517 | ** that have expired, but are retained for a limited period of time |
| 518 | ** so that pending anonymous login cookies and/or captcha dialogs |
| 519 | ** don't malfunction when the captcha-secret changes. |
| 520 | ** |
| 521 | ** Clients should start by using the 0-th captcha-secret. Only if |
| 522 | ** that one does not work should they advance to 1 and 2 and so forth, |
| 523 | ** until this routine returns a NULL pointer. |
| 524 | ** |
| 525 | ** The value returned is a string obtained from fossil_malloc() and |
| 526 | ** should be freed by the caller. |
| 527 | ** |
| 528 | ** The 0-th captcha secret is the value of Config.Name='captcha-secret'. |
| 529 | ** For N>0, the value is in Config.Name='captcha-secret-$N'. |
| 530 | */ |
| 531 | char *captcha_secret(int N){ |
| 532 | if( N==0 ){ |
| 533 | return db_text(0, "SELECT value FROM config WHERE name='captcha-secret'"); |
| 534 | }else{ |
| 535 | return db_text(0, |
| 536 | "SELECT value FROM config" |
| 537 | " WHERE name='captcha-secret-%d'" |
| 538 | " AND mtime>unixepoch('now','-6 hours')", N); |
| 539 | } |
| 540 | } |
| 541 | |
| 542 | /* |
| 543 | ** Translate a captcha seed value into the captcha password string. |
| 544 | ** The returned string is static and overwritten on each call to |
| 545 | ** this function. |
| 546 | ** |
| 547 | ** Use the N-th captcha secret to compute the password. When N==0, |
| 548 | ** a valid password is always returned. A new captcha-secret will |
| 549 | ** be created if necessary. But for N>0, the return value might |
| 550 | ** be NULL to indicate that there is no N-th captcha-secret. |
| 551 | */ |
| 552 | const char *captcha_decode(unsigned int seed, int N){ |
| 553 | char *zSecret; |
| 554 | const char *z; |
| 555 | Blob b; |
| 556 | static char zRes[20]; |
| 557 | |
| 558 | zSecret = captcha_secret(N); |
| 559 | if( zSecret==0 ){ |
| 560 | if( N>0 ) return 0; |
| 561 | db_unprotect(PROTECT_CONFIG); |
| 562 | db_multi_exec( |
| 563 | "REPLACE INTO config(name,value)" |
| 564 | " VALUES('captcha-secret', lower(hex(randomblob(20))));" |
| 565 | ); |
| 566 | db_protect_pop(); |
| 567 | zSecret = captcha_secret(0); |
| 568 | assert( zSecret!=0 ); |
| 569 | } |
| 570 | blob_init(&b, 0, 0); |
| 571 | blob_appendf(&b, "%s-%x", zSecret, seed); |
| 572 | sha1sum_blob(&b, &b); |
| 573 | z = blob_buffer(&b); |
| 574 | memcpy(zRes, z, 8); |
| 575 | zRes[8] = 0; |
| 576 | fossil_free(zSecret); |
| 577 | return zRes; |
| 578 | } |
| 579 | |
| 580 | /* |
| 581 | ** Return true if a CAPTCHA is required for editing wiki or tickets or for |
| @@ -569,26 +604,29 @@ | |
| 604 | const char *zSeed; |
| 605 | const char *zEntered; |
| 606 | const char *zDecode; |
| 607 | char z[30]; |
| 608 | int i; |
| 609 | int n = 0; |
| 610 | if( !bAlwaysNeeded && !captcha_needed() ){ |
| 611 | return 1; /* No captcha needed */ |
| 612 | } |
| 613 | zSeed = P("captchaseed"); |
| 614 | if( zSeed==0 ) return 0; |
| 615 | zEntered = P("captcha"); |
| 616 | if( zEntered==0 || strlen(zEntered)!=8 ) return 0; |
| 617 | do{ |
| 618 | zDecode = captcha_decode((unsigned int)atoi(zSeed), n++); |
| 619 | if( zDecode==0 ) return 0; |
| 620 | assert( strlen(zDecode)==8 ); |
| 621 | for(i=0; i<8; i++){ |
| 622 | char c = zEntered[i]; |
| 623 | if( c>='A' && c<='F' ) c += 'a' - 'A'; |
| 624 | if( c=='O' ) c = '0'; |
| 625 | z[i] = c; |
| 626 | } |
| 627 | }while( strncmp(zDecode,z,8)!=0 ); |
| 628 | return 1; |
| 629 | } |
| 630 | |
| 631 | /* |
| 632 | ** Generate a captcha display together with the necessary hidden parameter |
| @@ -607,11 +645,11 @@ | |
| 645 | const char *zDecoded; |
| 646 | char *zCaptcha; |
| 647 | |
| 648 | if( !captcha_needed() && (mFlags & 0x02)==0 ) return; |
| 649 | uSeed = captcha_seed(); |
| 650 | zDecoded = captcha_decode(uSeed, 0); |
| 651 | zCaptcha = captcha_render(zDecoded); |
| 652 | @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> |
| 653 | @ %h(zCaptcha) |
| 654 | @ </pre> |
| 655 | @ Enter security code shown above: |
| @@ -808,11 +846,11 @@ | |
| 846 | ** Return a WAV file that pronounces the digits of the captcha that |
| 847 | ** is determined by the seed given in the name= query parameter. |
| 848 | */ |
| 849 | void captcha_wav_page(void){ |
| 850 | const char *zSeed = PD("name","0"); |
| 851 | const char *zDecode = captcha_decode((unsigned int)atoi(zSeed), 0); |
| 852 | Blob audio; |
| 853 | captcha_wav(zDecode, &audio); |
| 854 | cgi_set_content_type("audio/wav"); |
| 855 | cgi_set_content(&audio); |
| 856 | } |
| 857 |
+1
-1
| --- src/json_login.c | ||
| +++ src/json_login.c | ||
| @@ -213,11 +213,11 @@ | ||
| 213 | 213 | */ |
| 214 | 214 | cson_value * json_page_anon_password(void){ |
| 215 | 215 | cson_value * v = cson_value_new_object(); |
| 216 | 216 | cson_object * o = cson_value_get_object(v); |
| 217 | 217 | unsigned const int seed = captcha_seed(); |
| 218 | - char const * zCaptcha = captcha_decode(seed); | |
| 218 | + char const * zCaptcha = captcha_decode(seed, 0); | |
| 219 | 219 | cson_object_set(o, "seed", |
| 220 | 220 | cson_value_new_integer( (cson_int_t)seed ) |
| 221 | 221 | ); |
| 222 | 222 | cson_object_set(o, "password", |
| 223 | 223 | cson_value_new_string( zCaptcha, strlen(zCaptcha) ) |
| 224 | 224 |
| --- src/json_login.c | |
| +++ src/json_login.c | |
| @@ -213,11 +213,11 @@ | |
| 213 | */ |
| 214 | cson_value * json_page_anon_password(void){ |
| 215 | cson_value * v = cson_value_new_object(); |
| 216 | cson_object * o = cson_value_get_object(v); |
| 217 | unsigned const int seed = captcha_seed(); |
| 218 | char const * zCaptcha = captcha_decode(seed); |
| 219 | cson_object_set(o, "seed", |
| 220 | cson_value_new_integer( (cson_int_t)seed ) |
| 221 | ); |
| 222 | cson_object_set(o, "password", |
| 223 | cson_value_new_string( zCaptcha, strlen(zCaptcha) ) |
| 224 |
| --- src/json_login.c | |
| +++ src/json_login.c | |
| @@ -213,11 +213,11 @@ | |
| 213 | */ |
| 214 | cson_value * json_page_anon_password(void){ |
| 215 | cson_value * v = cson_value_new_object(); |
| 216 | cson_object * o = cson_value_get_object(v); |
| 217 | unsigned const int seed = captcha_seed(); |
| 218 | char const * zCaptcha = captcha_decode(seed, 0); |
| 219 | cson_object_set(o, "seed", |
| 220 | cson_value_new_integer( (cson_int_t)seed ) |
| 221 | ); |
| 222 | cson_object_set(o, "password", |
| 223 | cson_value_new_string( zCaptcha, strlen(zCaptcha) ) |
| 224 |
+32
-19
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -154,17 +154,22 @@ | ||
| 154 | 154 | const char *zPassword, /* The supplied password */ |
| 155 | 155 | const char *zCS /* The captcha seed value */ |
| 156 | 156 | ){ |
| 157 | 157 | const char *zPw; /* The correct password shown in the captcha */ |
| 158 | 158 | int uid; /* The user ID of anonymous */ |
| 159 | + int n = 0; /* Counter of captcha-secrets */ | |
| 159 | 160 | |
| 160 | 161 | if( zUsername==0 ) return 0; |
| 161 | 162 | else if( zPassword==0 ) return 0; |
| 162 | 163 | else if( zCS==0 ) return 0; |
| 163 | 164 | else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0; |
| 164 | - zPw = captcha_decode((unsigned int)atoi(zCS)); | |
| 165 | - if( fossil_stricmp(zPw, zPassword)!=0 ) return 0; | |
| 165 | + while( 1/*exit-by-break*/ ){ | |
| 166 | + zPw = captcha_decode((unsigned int)atoi(zCS), n); | |
| 167 | + if( zPw==0 ) return 0; | |
| 168 | + if( fossil_stricmp(zPw, zPassword)==0 ) break; | |
| 169 | + n++; | |
| 170 | + } | |
| 166 | 171 | uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'" |
| 167 | 172 | " AND octet_length(pw)>0 AND octet_length(cap)>0"); |
| 168 | 173 | return uid; |
| 169 | 174 | } |
| 170 | 175 | |
| @@ -346,29 +351,30 @@ | ||
| 346 | 351 | ** *zCookieDest and the caller must eventually free() it. |
| 347 | 352 | ** |
| 348 | 353 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| 349 | 354 | */ |
| 350 | 355 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 351 | - const char *zNow; /* Current time (julian day number) */ | |
| 356 | + char *zNow; /* Current time (julian day number) */ | |
| 352 | 357 | char *zCookie; /* The login cookie */ |
| 353 | 358 | const char *zCookieName; /* Name of the login cookie */ |
| 354 | 359 | Blob b; /* Blob used during cookie construction */ |
| 355 | 360 | int expires = bSessionCookie ? 0 : 6*3600; |
| 356 | 361 | zCookieName = login_cookie_name(); |
| 357 | 362 | zNow = db_text("0", "SELECT julianday('now')"); |
| 358 | 363 | assert( zCookieName && zNow ); |
| 359 | 364 | blob_init(&b, zNow, -1); |
| 360 | - blob_appendf(&b, "/%s", db_get("captcha-secret","")); | |
| 365 | + blob_appendf(&b, "/%z", captcha_secret(0)); | |
| 361 | 366 | sha1sum_blob(&b, &b); |
| 362 | 367 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 363 | 368 | blob_reset(&b); |
| 364 | 369 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 365 | 370 | if( zCookieDest ){ |
| 366 | 371 | *zCookieDest = zCookie; |
| 367 | 372 | }else{ |
| 368 | 373 | free(zCookie); |
| 369 | 374 | } |
| 375 | + fossil_free(zNow); | |
| 370 | 376 | } |
| 371 | 377 | |
| 372 | 378 | /* |
| 373 | 379 | ** "Unsets" the login cookie (insofar as cookies can be unset) and |
| 374 | 380 | ** clears the current user's (g.userUid) login information from the |
| @@ -806,11 +812,11 @@ | ||
| 806 | 812 | @ <td><input type="submit" name="pwreset" value="Reset My Password"> |
| 807 | 813 | @ </tr> |
| 808 | 814 | } |
| 809 | 815 | @ </table> |
| 810 | 816 | if( zAnonPw && !noAnon ){ |
| 811 | - const char *zDecoded = captcha_decode(uSeed); | |
| 817 | + const char *zDecoded = captcha_decode(uSeed, 0); | |
| 812 | 818 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 813 | 819 | char *zCaptcha = captcha_render(zDecoded); |
| 814 | 820 | |
| 815 | 821 | @ <p><input type="hidden" name="cs" value="%u(uSeed)"> |
| 816 | 822 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| @@ -1428,22 +1434,29 @@ | ||
| 1428 | 1434 | ** too old and the sha1 hash of TIME/SECRET must match HASH. |
| 1429 | 1435 | ** SECRET is the "captcha-secret" value in the repository. |
| 1430 | 1436 | */ |
| 1431 | 1437 | double rTime = atof(zArg); |
| 1432 | 1438 | Blob b; |
| 1433 | - blob_zero(&b); | |
| 1434 | - blob_appendf(&b, "%s/%s", zArg, db_get("captcha-secret","")); | |
| 1435 | - sha1sum_blob(&b, &b); | |
| 1436 | - if( fossil_strcmp(zHash, blob_str(&b))==0 ){ | |
| 1437 | - uid = db_int(0, | |
| 1438 | - "SELECT uid FROM user WHERE login='anonymous'" | |
| 1439 | - " AND octet_length(cap)>0" | |
| 1440 | - " AND octet_length(pw)>0" | |
| 1441 | - " AND %.17g+0.25>julianday('now')", | |
| 1442 | - rTime | |
| 1443 | - ); | |
| 1444 | - } | |
| 1439 | + char *zSecret; | |
| 1440 | + int n = 0; | |
| 1441 | + | |
| 1442 | + do{ | |
| 1443 | + blob_zero(&b); | |
| 1444 | + zSecret = captcha_secret(n++); | |
| 1445 | + if( zSecret==0 ) break; | |
| 1446 | + blob_appendf(&b, "%s/%s", zArg, zSecret); | |
| 1447 | + sha1sum_blob(&b, &b); | |
| 1448 | + if( fossil_strcmp(zHash, blob_str(&b))==0 ){ | |
| 1449 | + uid = db_int(0, | |
| 1450 | + "SELECT uid FROM user WHERE login='anonymous'" | |
| 1451 | + " AND octet_length(cap)>0" | |
| 1452 | + " AND octet_length(pw)>0" | |
| 1453 | + " AND %.17g+0.25>julianday('now')", | |
| 1454 | + rTime | |
| 1455 | + ); | |
| 1456 | + } | |
| 1457 | + }while( uid==0 ); | |
| 1445 | 1458 | blob_reset(&b); |
| 1446 | 1459 | }else{ |
| 1447 | 1460 | /* Cookies of the form "HASH/CODE/USER". Search first in the |
| 1448 | 1461 | ** local user table, then the user table for project CODE if we |
| 1449 | 1462 | ** are part of a login-group. |
| @@ -2213,11 +2226,11 @@ | ||
| 2213 | 2226 | if( captchaIsCorrect ){ |
| 2214 | 2227 | uSeed = strtoul(P("captchaseed"),0,10); |
| 2215 | 2228 | }else{ |
| 2216 | 2229 | uSeed = captcha_seed(); |
| 2217 | 2230 | } |
| 2218 | - zDecoded = captcha_decode(uSeed); | |
| 2231 | + zDecoded = captcha_decode(uSeed, 0); | |
| 2219 | 2232 | zCaptcha = captcha_render(zDecoded); |
| 2220 | 2233 | |
| 2221 | 2234 | style_header("Register"); |
| 2222 | 2235 | /* Print out the registration form. */ |
| 2223 | 2236 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| @@ -2423,11 +2436,11 @@ | ||
| 2423 | 2436 | if( captchaIsCorrect ){ |
| 2424 | 2437 | uSeed = strtoul(P("captchaseed"),0,10); |
| 2425 | 2438 | }else{ |
| 2426 | 2439 | uSeed = captcha_seed(); |
| 2427 | 2440 | } |
| 2428 | - zDecoded = captcha_decode(uSeed); | |
| 2441 | + zDecoded = captcha_decode(uSeed, 0); | |
| 2429 | 2442 | zCaptcha = captcha_render(zDecoded); |
| 2430 | 2443 | |
| 2431 | 2444 | style_header("Request Password Reset"); |
| 2432 | 2445 | /* Print out the registration form. */ |
| 2433 | 2446 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| 2434 | 2447 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -154,17 +154,22 @@ | |
| 154 | const char *zPassword, /* The supplied password */ |
| 155 | const char *zCS /* The captcha seed value */ |
| 156 | ){ |
| 157 | const char *zPw; /* The correct password shown in the captcha */ |
| 158 | int uid; /* The user ID of anonymous */ |
| 159 | |
| 160 | if( zUsername==0 ) return 0; |
| 161 | else if( zPassword==0 ) return 0; |
| 162 | else if( zCS==0 ) return 0; |
| 163 | else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0; |
| 164 | zPw = captcha_decode((unsigned int)atoi(zCS)); |
| 165 | if( fossil_stricmp(zPw, zPassword)!=0 ) return 0; |
| 166 | uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'" |
| 167 | " AND octet_length(pw)>0 AND octet_length(cap)>0"); |
| 168 | return uid; |
| 169 | } |
| 170 | |
| @@ -346,29 +351,30 @@ | |
| 346 | ** *zCookieDest and the caller must eventually free() it. |
| 347 | ** |
| 348 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| 349 | */ |
| 350 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 351 | const char *zNow; /* Current time (julian day number) */ |
| 352 | char *zCookie; /* The login cookie */ |
| 353 | const char *zCookieName; /* Name of the login cookie */ |
| 354 | Blob b; /* Blob used during cookie construction */ |
| 355 | int expires = bSessionCookie ? 0 : 6*3600; |
| 356 | zCookieName = login_cookie_name(); |
| 357 | zNow = db_text("0", "SELECT julianday('now')"); |
| 358 | assert( zCookieName && zNow ); |
| 359 | blob_init(&b, zNow, -1); |
| 360 | blob_appendf(&b, "/%s", db_get("captcha-secret","")); |
| 361 | sha1sum_blob(&b, &b); |
| 362 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 363 | blob_reset(&b); |
| 364 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 365 | if( zCookieDest ){ |
| 366 | *zCookieDest = zCookie; |
| 367 | }else{ |
| 368 | free(zCookie); |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | /* |
| 373 | ** "Unsets" the login cookie (insofar as cookies can be unset) and |
| 374 | ** clears the current user's (g.userUid) login information from the |
| @@ -806,11 +812,11 @@ | |
| 806 | @ <td><input type="submit" name="pwreset" value="Reset My Password"> |
| 807 | @ </tr> |
| 808 | } |
| 809 | @ </table> |
| 810 | if( zAnonPw && !noAnon ){ |
| 811 | const char *zDecoded = captcha_decode(uSeed); |
| 812 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 813 | char *zCaptcha = captcha_render(zDecoded); |
| 814 | |
| 815 | @ <p><input type="hidden" name="cs" value="%u(uSeed)"> |
| 816 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| @@ -1428,22 +1434,29 @@ | |
| 1428 | ** too old and the sha1 hash of TIME/SECRET must match HASH. |
| 1429 | ** SECRET is the "captcha-secret" value in the repository. |
| 1430 | */ |
| 1431 | double rTime = atof(zArg); |
| 1432 | Blob b; |
| 1433 | blob_zero(&b); |
| 1434 | blob_appendf(&b, "%s/%s", zArg, db_get("captcha-secret","")); |
| 1435 | sha1sum_blob(&b, &b); |
| 1436 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1437 | uid = db_int(0, |
| 1438 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1439 | " AND octet_length(cap)>0" |
| 1440 | " AND octet_length(pw)>0" |
| 1441 | " AND %.17g+0.25>julianday('now')", |
| 1442 | rTime |
| 1443 | ); |
| 1444 | } |
| 1445 | blob_reset(&b); |
| 1446 | }else{ |
| 1447 | /* Cookies of the form "HASH/CODE/USER". Search first in the |
| 1448 | ** local user table, then the user table for project CODE if we |
| 1449 | ** are part of a login-group. |
| @@ -2213,11 +2226,11 @@ | |
| 2213 | if( captchaIsCorrect ){ |
| 2214 | uSeed = strtoul(P("captchaseed"),0,10); |
| 2215 | }else{ |
| 2216 | uSeed = captcha_seed(); |
| 2217 | } |
| 2218 | zDecoded = captcha_decode(uSeed); |
| 2219 | zCaptcha = captcha_render(zDecoded); |
| 2220 | |
| 2221 | style_header("Register"); |
| 2222 | /* Print out the registration form. */ |
| 2223 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| @@ -2423,11 +2436,11 @@ | |
| 2423 | if( captchaIsCorrect ){ |
| 2424 | uSeed = strtoul(P("captchaseed"),0,10); |
| 2425 | }else{ |
| 2426 | uSeed = captcha_seed(); |
| 2427 | } |
| 2428 | zDecoded = captcha_decode(uSeed); |
| 2429 | zCaptcha = captcha_render(zDecoded); |
| 2430 | |
| 2431 | style_header("Request Password Reset"); |
| 2432 | /* Print out the registration form. */ |
| 2433 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| 2434 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -154,17 +154,22 @@ | |
| 154 | const char *zPassword, /* The supplied password */ |
| 155 | const char *zCS /* The captcha seed value */ |
| 156 | ){ |
| 157 | const char *zPw; /* The correct password shown in the captcha */ |
| 158 | int uid; /* The user ID of anonymous */ |
| 159 | int n = 0; /* Counter of captcha-secrets */ |
| 160 | |
| 161 | if( zUsername==0 ) return 0; |
| 162 | else if( zPassword==0 ) return 0; |
| 163 | else if( zCS==0 ) return 0; |
| 164 | else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0; |
| 165 | while( 1/*exit-by-break*/ ){ |
| 166 | zPw = captcha_decode((unsigned int)atoi(zCS), n); |
| 167 | if( zPw==0 ) return 0; |
| 168 | if( fossil_stricmp(zPw, zPassword)==0 ) break; |
| 169 | n++; |
| 170 | } |
| 171 | uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'" |
| 172 | " AND octet_length(pw)>0 AND octet_length(cap)>0"); |
| 173 | return uid; |
| 174 | } |
| 175 | |
| @@ -346,29 +351,30 @@ | |
| 351 | ** *zCookieDest and the caller must eventually free() it. |
| 352 | ** |
| 353 | ** If bSessionCookie is true, the cookie will be a session cookie. |
| 354 | */ |
| 355 | void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ |
| 356 | char *zNow; /* Current time (julian day number) */ |
| 357 | char *zCookie; /* The login cookie */ |
| 358 | const char *zCookieName; /* Name of the login cookie */ |
| 359 | Blob b; /* Blob used during cookie construction */ |
| 360 | int expires = bSessionCookie ? 0 : 6*3600; |
| 361 | zCookieName = login_cookie_name(); |
| 362 | zNow = db_text("0", "SELECT julianday('now')"); |
| 363 | assert( zCookieName && zNow ); |
| 364 | blob_init(&b, zNow, -1); |
| 365 | blob_appendf(&b, "/%z", captcha_secret(0)); |
| 366 | sha1sum_blob(&b, &b); |
| 367 | zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); |
| 368 | blob_reset(&b); |
| 369 | cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); |
| 370 | if( zCookieDest ){ |
| 371 | *zCookieDest = zCookie; |
| 372 | }else{ |
| 373 | free(zCookie); |
| 374 | } |
| 375 | fossil_free(zNow); |
| 376 | } |
| 377 | |
| 378 | /* |
| 379 | ** "Unsets" the login cookie (insofar as cookies can be unset) and |
| 380 | ** clears the current user's (g.userUid) login information from the |
| @@ -806,11 +812,11 @@ | |
| 812 | @ <td><input type="submit" name="pwreset" value="Reset My Password"> |
| 813 | @ </tr> |
| 814 | } |
| 815 | @ </table> |
| 816 | if( zAnonPw && !noAnon ){ |
| 817 | const char *zDecoded = captcha_decode(uSeed, 0); |
| 818 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 819 | char *zCaptcha = captcha_render(zDecoded); |
| 820 | |
| 821 | @ <p><input type="hidden" name="cs" value="%u(uSeed)"> |
| 822 | @ Visitors may enter <b>anonymous</b> as the user-ID with |
| @@ -1428,22 +1434,29 @@ | |
| 1434 | ** too old and the sha1 hash of TIME/SECRET must match HASH. |
| 1435 | ** SECRET is the "captcha-secret" value in the repository. |
| 1436 | */ |
| 1437 | double rTime = atof(zArg); |
| 1438 | Blob b; |
| 1439 | char *zSecret; |
| 1440 | int n = 0; |
| 1441 | |
| 1442 | do{ |
| 1443 | blob_zero(&b); |
| 1444 | zSecret = captcha_secret(n++); |
| 1445 | if( zSecret==0 ) break; |
| 1446 | blob_appendf(&b, "%s/%s", zArg, zSecret); |
| 1447 | sha1sum_blob(&b, &b); |
| 1448 | if( fossil_strcmp(zHash, blob_str(&b))==0 ){ |
| 1449 | uid = db_int(0, |
| 1450 | "SELECT uid FROM user WHERE login='anonymous'" |
| 1451 | " AND octet_length(cap)>0" |
| 1452 | " AND octet_length(pw)>0" |
| 1453 | " AND %.17g+0.25>julianday('now')", |
| 1454 | rTime |
| 1455 | ); |
| 1456 | } |
| 1457 | }while( uid==0 ); |
| 1458 | blob_reset(&b); |
| 1459 | }else{ |
| 1460 | /* Cookies of the form "HASH/CODE/USER". Search first in the |
| 1461 | ** local user table, then the user table for project CODE if we |
| 1462 | ** are part of a login-group. |
| @@ -2213,11 +2226,11 @@ | |
| 2226 | if( captchaIsCorrect ){ |
| 2227 | uSeed = strtoul(P("captchaseed"),0,10); |
| 2228 | }else{ |
| 2229 | uSeed = captcha_seed(); |
| 2230 | } |
| 2231 | zDecoded = captcha_decode(uSeed, 0); |
| 2232 | zCaptcha = captcha_render(zDecoded); |
| 2233 | |
| 2234 | style_header("Register"); |
| 2235 | /* Print out the registration form. */ |
| 2236 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| @@ -2423,11 +2436,11 @@ | |
| 2436 | if( captchaIsCorrect ){ |
| 2437 | uSeed = strtoul(P("captchaseed"),0,10); |
| 2438 | }else{ |
| 2439 | uSeed = captcha_seed(); |
| 2440 | } |
| 2441 | zDecoded = captcha_decode(uSeed, 0); |
| 2442 | zCaptcha = captcha_render(zDecoded); |
| 2443 | |
| 2444 | style_header("Request Password Reset"); |
| 2445 | /* Print out the registration form. */ |
| 2446 | g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */ |
| 2447 |
+1
-1
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -1355,11 +1355,11 @@ | ||
| 1355 | 1355 | ** WEBPAGE: honeypot |
| 1356 | 1356 | ** This page is a honeypot for spiders and bots. |
| 1357 | 1357 | */ |
| 1358 | 1358 | void honeypot_page(void){ |
| 1359 | 1359 | unsigned int uSeed = captcha_seed(); |
| 1360 | - const char *zDecoded = captcha_decode(uSeed); | |
| 1360 | + const char *zDecoded = captcha_decode(uSeed, 0); | |
| 1361 | 1361 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 1362 | 1362 | char *zCaptcha = captcha_render(zDecoded); |
| 1363 | 1363 | style_header("I think you are a robot"); |
| 1364 | 1364 | @ <p>You seem like a robot.</p> |
| 1365 | 1365 | @ |
| 1366 | 1366 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1355,11 +1355,11 @@ | |
| 1355 | ** WEBPAGE: honeypot |
| 1356 | ** This page is a honeypot for spiders and bots. |
| 1357 | */ |
| 1358 | void honeypot_page(void){ |
| 1359 | unsigned int uSeed = captcha_seed(); |
| 1360 | const char *zDecoded = captcha_decode(uSeed); |
| 1361 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 1362 | char *zCaptcha = captcha_render(zDecoded); |
| 1363 | style_header("I think you are a robot"); |
| 1364 | @ <p>You seem like a robot.</p> |
| 1365 | @ |
| 1366 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1355,11 +1355,11 @@ | |
| 1355 | ** WEBPAGE: honeypot |
| 1356 | ** This page is a honeypot for spiders and bots. |
| 1357 | */ |
| 1358 | void honeypot_page(void){ |
| 1359 | unsigned int uSeed = captcha_seed(); |
| 1360 | const char *zDecoded = captcha_decode(uSeed, 0); |
| 1361 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| 1362 | char *zCaptcha = captcha_render(zDecoded); |
| 1363 | style_header("I think you are a robot"); |
| 1364 | @ <p>You seem like a robot.</p> |
| 1365 | @ |
| 1366 |