Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Non-working code for the /subscribe and /alerts web pages. This is an incremental check-in. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | email-alerts |
Files: | files | file ages | folders |
SHA3-256: |
e91143e8132cb42e0fbec2645a023a7f |
User & Date: | drh 2018-06-21 12:34:02.739 |
Context
2018-06-21
| ||
15:19 | Merge the popen() on windows fix from trunk. ... (check-in: ef2426dc user: drh tags: email-alerts) | |
12:34 | Non-working code for the /subscribe and /alerts web pages. This is an incremental check-in. ... (check-in: e91143e8 user: drh tags: email-alerts) | |
12:24 | Remove the configuration of the show-version-diffs property from the /setup_timeline page as that property is no longer used. ... (check-in: 8d230be4 user: drh tags: trunk) | |
Changes
Changes to src/cgi.c.
︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 64 65 66 67 | ** does the same except "y" is returned in place of NULL if there is not match. */ #define P(x) cgi_parameter((x),0) #define PD(x,y) cgi_parameter((x),(y)) #define PT(x) cgi_parameter_trimmed((x),0) #define PDT(x,y) cgi_parameter_trimmed((x),(y)) #define PB(x) cgi_parameter_boolean(x) /* ** Destinations for output text. */ #define CGI_HEADER 0 #define CGI_BODY 1 | > > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | ** does the same except "y" is returned in place of NULL if there is not match. */ #define P(x) cgi_parameter((x),0) #define PD(x,y) cgi_parameter((x),(y)) #define PT(x) cgi_parameter_trimmed((x),0) #define PDT(x,y) cgi_parameter_trimmed((x),(y)) #define PB(x) cgi_parameter_boolean(x) #define P10(x) cgi_parameter_01(x,1) #define P01(x) cgi_parameter_01(x,0) /* ** Destinations for output text. */ #define CGI_HEADER 0 #define CGI_BODY 1 |
︙ | ︙ | |||
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 | ** or "no" or "off". */ int cgi_parameter_boolean(const char *zName){ const char *zIn = cgi_parameter(zName, 0); if( zIn==0 ) return 0; return zIn[0]==0 || is_truth(zIn); } /* ** Return the name of the i-th CGI parameter. Return NULL if there ** are fewer than i registered CGI parameters. */ const char *cgi_parameter_name(int i){ if( i>=0 && i<nUsedQP ){ | > > > > > > > > > | 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 | ** or "no" or "off". */ int cgi_parameter_boolean(const char *zName){ const char *zIn = cgi_parameter(zName, 0); if( zIn==0 ) return 0; return zIn[0]==0 || is_truth(zIn); } /* ** Return 0 or 1 according to the value of CGI parameter zName. */ int cgi_parameter_01(const char *zName, int iDefault){ const char *zIn = cgi_parameter(zName, 0); if( zIn==0 ) return iDefault; return zIn[0]==0 || is_truth(zIn); } /* ** Return the name of the i-th CGI parameter. Return NULL if there ** are fewer than i registered CGI parameters. */ const char *cgi_parameter_name(int i){ if( i>=0 && i<nUsedQP ){ |
︙ | ︙ |
Changes to src/db.c.
︙ | ︙ | |||
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 | if( g.fTimeFormat==1 ){ sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC); }else{ sqlite3_result_text(context, "utc", -1, SQLITE_STATIC); } } /* ** Register the SQL functions that are useful both to the internal ** representation and to the "fossil sql" command. */ void db_add_aux_functions(sqlite3 *db){ sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_UTF8, 0, db_checkin_mtime_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0, db_now_function, 0, 0); sqlite3_create_function(db, "toLocal", 0, SQLITE_UTF8, 0, db_tolocal_function, 0, 0); sqlite3_create_function(db, "fromLocal", 0, SQLITE_UTF8, 0, db_fromlocal_function, 0, 0); } #if USE_SEE /* ** This is a pointer to the saved database encryption key string. */ static char *zSavedKey = 0; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 | if( g.fTimeFormat==1 ){ sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC); }else{ sqlite3_result_text(context, "utc", -1, SQLITE_STATIC); } } /* ** If the input is a hexadecimal string, convert that string into a BLOB. ** If the input is not a hexadecimal string, return NULL. */ void db_hextoblob( sqlite3_context *context, int argc, sqlite3_value **argv ){ const unsigned char *zIn = sqlite3_value_text(argv[0]); int nIn = sqlite3_value_bytes(argv[0]); unsigned char *zOut; if( zIn==0 ) return; if( nIn&1 ) return; if( !validate16(zIn, nIn) ) return; zOut = sqlite3_malloc64( nIn/2 ); if( zOut==0 ){ sqlite3_result_error_nomem(context); return; } decode16(zIn, zOut, nIn); sqlite3_result_blob(context, zOut, nIn/2, sqlite3_free); } /* ** /* ** Register the SQL functions that are useful both to the internal ** representation and to the "fossil sql" command. */ void db_add_aux_functions(sqlite3 *db){ sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_UTF8, 0, db_checkin_mtime_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0, db_now_function, 0, 0); sqlite3_create_function(db, "toLocal", 0, SQLITE_UTF8, 0, db_tolocal_function, 0, 0); sqlite3_create_function(db, "fromLocal", 0, SQLITE_UTF8, 0, db_fromlocal_function, 0, 0); sqlite3_create_function(db, "hextoblob", 1, SQLITE_UTF8, 0, db_hextoblob, 0, 0); } #if USE_SEE /* ** This is a pointer to the saved database encryption key string. */ static char *zSavedKey = 0; |
︙ | ︙ |
Changes to src/email.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** ** Email notification features */ #include "config.h" #include "email.h" #include <assert.h> /* ** SQL code to implement the tables needed by the email notification ** system. */ static const char zEmailInit[] = @ -- Subscribers are distinct from users. A person can have a log-in in @ -- the USER table without being a subscriber. Or a person can be a | > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ** ** Email notification features */ #include "config.h" #include "email.h" #include <assert.h> /* ** Maximum size of the subscriberCode blob, in bytes */ #define SUBSCRIBER_CODE_SZ 32 /* ** SQL code to implement the tables needed by the email notification ** system. */ static const char zEmailInit[] = @ -- Subscribers are distinct from users. A person can have a log-in in @ -- the USER table without being a subscriber. Or a person can be a |
︙ | ︙ | |||
40 41 42 43 44 45 46 | @ -- w - Wiki changes @ -- Probably different codes will be added in the future. In the future @ -- we might also add a separate table that allows subscribing to email @ -- notifications for specific branches or tags or tickets. @ -- @ CREATE TABLE repository.subscriber( @ subscriberId INTEGER PRIMARY KEY, -- numeric subscriber ID. Internal use | | | | < < | > > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | @ -- w - Wiki changes @ -- Probably different codes will be added in the future. In the future @ -- we might also add a separate table that allows subscribing to email @ -- notifications for specific branches or tags or tickets. @ -- @ CREATE TABLE repository.subscriber( @ subscriberId INTEGER PRIMARY KEY, -- numeric subscriber ID. Internal use @ subscriberCode BLOB UNIQUE, -- UUID for subscriber. External use @ semail TEXT, -- email address @ suname TEXT, -- corresponding USER entry @ sverify BOOLEAN, -- email address verified @ sdonotcall BOOLEAN, -- true for Do Not Call @ sdigest BOOLEAN, -- true for daily digests only @ ssub TEXT, -- baseline subscriptions @ sctime DATE, -- When this entry was created. JulianDay @ smtime DATE, -- Last change. JulianDay @ smip TEXT -- IP address of last change @ ); @ CREATE INDEX repository.subscriberUname @ ON subscriber(suname) WHERE suname IS NOT NULL; @ @ -- Email notifications that need to be sent. @ -- @ -- If the eventid key is an integer, then it corresponds to the @ -- EVENT.OBJID table. Other kinds of eventids are reserved for @ -- future expansion. @ -- |
︙ | ︙ | |||
452 453 454 455 456 457 458 459 460 | const char *zDecoded; char *zCaptcha; login_check_credentials(); if( !g.perm.EmailAlert ){ login_needed(g.anon.EmailAlert); return; } style_header("Email Subscription"); | > > > > > > | < | < > | | | < < < < | | | > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 | const char *zDecoded; char *zCaptcha; login_check_credentials(); if( !g.perm.EmailAlert ){ login_needed(g.anon.EmailAlert); return; } if( login_is_individual() && db_exists("SELECT 1 FROM subscriber WHERE suname=%Q",g.zLogin) ){ cgi_redirect("%R/alerts"); return; } style_header("Email Subscription"); needCaptcha = P("usecaptcha")!=0 || !login_is_individual(); form_begin(0, "%R/subscribe"); @ <table class="subscribe"> @ <tr> @ <td class="form_label">Email Address:</td> @ <td><input type="text" name="e" value="%h(PD("e",""))" size="30"></td> @ <td></td> @ </tr> if( needCaptcha ){ uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); @ <tr> @ <td class="form_label">Security Code:</td> @ <td><input type="text" name="captcha" value="" size="30"> @ <input type="hidden" name="usecaptcha" value="1"></td> @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> @ </tr> } if( g.perm.Admin ){ @ <tr> @ <td class="form_label">User:</td> @ <td><input type="text" name="suname" value="%h(PD("suname",g.zLogin))" \ @ size="30"></td> @ <td></td> @ </tr> } @ <tr> @ <td class="form_label">Options:</td> @ <td><label><input type="checkbox" name="sa" value="%d(P10("sa"))">\ @ Announcements</label><br> @ <label><input type="checkbox" name="sc" value="%d(P01("sc"))">\ @ Check-ins</label><br> @ <label><input type="checkbox" name="st" value="%d(P01("st"))">\ @ Ticket changes</label><br> @ <label><input type="checkbox" name="sw" value="%d(P01("sw"))">\ @ Wiki</label><br> @ <label><input type="checkbox" name="di" value="%d(P01("di"))">\ @ Daily digest only</label><br></td> @ </tr> @ <tr> @ <td></td> @ <td><input type="submit" value="Submit"></td> @ </tr> @ </table> if( needCaptcha ){ @ <div class="captcha"><table class="captcha"><tr><td><pre> @ %h(zCaptcha) @ </pre> @ Enter the 8 characters above in the "Security Code" box @ </td></tr></table></div> } @ </form> style_footer(); } /* ** WEBPAGE: alerts ** ** Edit email alert and notification settings. ** ** The subscriber entry is identified in either of two ways: ** ** (1) The name= query parameter contains the subscriberCode. ** ** (2) The user is logged into an account other than "nobody" or ** "anonymous". In that case the notification settings ** associated with that account can be edited without needing ** to know the subscriber code. */ void alerts_page(void){ const char *zName = P("name"); Stmt q; int sa, sc, st, sw; int sdigest, sdonotcall, sverify; const char *ssub; const char *semail; const char *sctime; const char *smtime; const char *smip; int i; login_check_credentials(); if( !g.perm.EmailAlert ){ cgi_redirect("subscribe"); return; } if( zName==0 && login_is_individual() ){ zName = db_text(0, "SELECT hex(subscriberCode) FROM subscriber" " WHERE suname=%Q", g.zLogin); } if( zName==0 || !validate16(zName, -1) ){ cgi_redirect("subscribe"); return; } if( P("submit")!=0 && cgi_csrf_safe(1) ){ int sdonotcall = PB("sdonotcall"); int sdigest = PB("sdigest"); char ssub[10]; int nsub = 0; if( PB("sa") ) ssub[nsub++] = 'a'; if( PB("sc") ) ssub[nsub++] = 'c'; if( PB("st") ) ssub[nsub++] = 't'; if( PB("sw") ) ssub[nsub++] = 'w'; ssub[nsub] = 0; db_multi_exec( "UPDATE subscriber SET" " sdonotcall=%d," " sdigest=%d," " ssub=%Q," " smtime=julianday('now')," " smip=%Q" " WHERE subscriberCode=hextoblob(%Q)", sdonotcall, sdigest, ssub, g.zIpAddr, zName ); } if( PB("dodelete") && P("delete")!=0 && cgi_csrf_safe(1) ){ db_multi_exec( "DELETE FROM subscriber WHERE subscriberCode=hextoblob(%Q)", zName ); } db_prepare(&q, "SELECT" " semail," " sverify," " sdonotcall," " sdigest," " ssub," " datetime(sctime)," " datetime(smtime)," " smip" " FROM subscriber WHERE subscriberCode=hextoblob(%Q)", zName); if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); cgi_redirect("subscribe"); return; } style_header("Update Subscription"); semail = db_column_text(&q, 0); sverify = db_column_int(&q, 1); sdonotcall = db_column_int(&q, 2); sdigest = db_column_int(&q, 3); ssub = db_column_text(&q, 4); sa = strchr(ssub,'a')!=0; sc = strchr(ssub,'c')!=0; st = strchr(ssub,'t')!=0; sw = strchr(ssub,'w')!=0; sctime = db_column_text(&q, 5); smtime = db_column_text(&q, 6); smip = db_column_text(&q, 7); form_begin(0, "%R/alerts"); @ <table class="subscribe"> @ <tr> @ <td class="form_label">Email Address:</td> @ <td>%h(semail)</td> @ </tr> if( g.perm.Admin ){ @ <tr> @ <td class='form_label'>IP Address:</td> @ <td>%h(smip)</td> @ </tr> } @ <tr> @ <td class="form_label">Options:</td> @ <td><label><input type="checkbox" name="sa" value="%d(sa)">\ @ Announcements</label><br> @ <label><input type="checkbox" name="sc" value="%d(sc)">\ @ Check-ins</label><br> @ <label><input type="checkbox" name="st" value="%d(st)">\ @ Ticket changes</label><br> @ <label><input type="checkbox" name="sw" value="%d(sw)">\ @ Wiki</label><br> @ <label><input type="checkbox" name="sdigest" value="%d(sdigest)">\ @ Daily digest only</label><br> if( g.perm.Admin ){ @ <label><input type="checkbox" name="sdonotcall" value="%d(sdonotcall)">\ @ Do not call</label><br> @ <label><input type="checkbox" name="sverify" value="%d(sverify)">\ @ Verified</label><br> } @ </td></tr> @ <tr> @ <td></td> @ <td><input type="submit" value="Submit"></td> @ </tr> @ <tr> @ <td></td> @ <td><label><input type="checkbox" name="dodelete" value="0"> @ Delete this subscription</label> @ <input type="submit" name="delete" value="Delete"></td> @ </tr> @ </table> @ </form> db_finalize(&q); style_footer(); } |
Changes to src/encode.c.
︙ | ︙ | |||
632 633 634 635 636 637 638 639 640 641 642 643 644 645 | /* ** Return true if the input string contains only valid base-16 digits. ** If any invalid characters appear in the string, return false. */ int validate16(const char *zIn, int nIn){ int i; for(i=0; i<nIn; i++, zIn++){ if( zDecode[zIn[0]&0xff]>63 ){ return zIn[0]==0; } } return 1; } | > | 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 | /* ** Return true if the input string contains only valid base-16 digits. ** If any invalid characters appear in the string, return false. */ int validate16(const char *zIn, int nIn){ int i; if( nIn<0 ) nIn = (int)strlen(zIn); for(i=0; i<nIn; i++, zIn++){ if( zDecode[zIn[0]&0xff]>63 ){ return zIn[0]==0; } } return 1; } |
︙ | ︙ |
Changes to src/login.c.
︙ | ︙ | |||
1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 | /* ** Return true if the user is "nobody" */ int login_is_nobody(void){ return g.zLogin==0 || g.zLogin[0]==0 || fossil_strcmp(g.zLogin,"nobody")==0; } /* ** Return the login name. If no login name is specified, return "nobody". */ const char *login_name(void){ return (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody"; } | > > > > > > > > > | 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 | /* ** Return true if the user is "nobody" */ int login_is_nobody(void){ return g.zLogin==0 || g.zLogin[0]==0 || fossil_strcmp(g.zLogin,"nobody")==0; } /* ** Return true if the user is a specific individual, not "nobody" or ** "anonymous". */ int login_is_individual(void){ return g.zLogin!=0 && g.zLogin[0]!=0 && fossil_strcmp(g.zLogin,"nobody")!=0 && fossil_strcmp(g.zLogin,"anonymous")!=0; } /* ** Return the login name. If no login name is specified, return "nobody". */ const char *login_name(void){ return (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody"; } |
︙ | ︙ |