| | @@ -1389,11 +1389,15 @@ |
| 1389 | 1389 | id); |
| 1390 | 1390 | if( !needCaptcha ){ |
| 1391 | 1391 | /* The new subscription has been added on behalf of a logged-in user. |
| 1392 | 1392 | ** No verification is required. Jump immediately to /alerts page. |
| 1393 | 1393 | */ |
| 1394 | | - cgi_redirectf("%R/alerts/%s", zCode); |
| 1394 | + if( g.perm.Admin ){ |
| 1395 | + cgi_redirectf("%R/alerts/%.32s", zCode); |
| 1396 | + }else{ |
| 1397 | + cgi_redirectf("%R/alerts"); |
| 1398 | + } |
| 1395 | 1399 | return; |
| 1396 | 1400 | }else{ |
| 1397 | 1401 | /* We need to send a verification email */ |
| 1398 | 1402 | Blob hdr, body; |
| 1399 | 1403 | AlertSender *pSender = alert_sender_new(0,0); |
| | @@ -1534,21 +1538,20 @@ |
| 1534 | 1538 | /* |
| 1535 | 1539 | ** Either shutdown or completely delete a subscription entry given |
| 1536 | 1540 | ** by the hex value zName. Then paint a webpage that explains that |
| 1537 | 1541 | ** the entry has been removed. |
| 1538 | 1542 | */ |
| 1539 | | -static void alert_unsubscribe(const char *zName){ |
| 1543 | +static void alert_unsubscribe(int sid){ |
| 1540 | 1544 | char *zEmail; |
| 1541 | 1545 | zEmail = db_text(0, "SELECT semail FROM subscriber" |
| 1542 | | - " WHERE subscriberCode=hextoblob(%Q)", zName); |
| 1546 | + " WHERE subscriberId=%d", sid); |
| 1543 | 1547 | if( zEmail==0 ){ |
| 1544 | 1548 | style_header("Unsubscribe Fail"); |
| 1545 | 1549 | @ <p>Unable to locate a subscriber with the requested key</p> |
| 1546 | 1550 | }else{ |
| 1547 | 1551 | db_multi_exec( |
| 1548 | | - "DELETE FROM subscriber WHERE subscriberCode=hextoblob(%Q)", |
| 1549 | | - zName |
| 1552 | + "DELETE FROM subscriber WHERE subscriberId=%d", sid |
| 1550 | 1553 | ); |
| 1551 | 1554 | style_header("Unsubscribed"); |
| 1552 | 1555 | @ <p>The "%h(zEmail)" email address has been delisted. |
| 1553 | 1556 | @ All traces of that email address have been removed</p> |
| 1554 | 1557 | } |
| | @@ -1559,46 +1562,75 @@ |
| 1559 | 1562 | /* |
| 1560 | 1563 | ** WEBPAGE: alerts |
| 1561 | 1564 | ** |
| 1562 | 1565 | ** Edit email alert and notification settings. |
| 1563 | 1566 | ** |
| 1564 | | -** The subscriber is identified in either of two ways: |
| 1567 | +** The subscriber is identified in several ways: |
| 1568 | +** |
| 1569 | +** (1) The name= query parameter contains the complete subscriberCode. |
| 1570 | +** This only happens when the user receives a verification |
| 1571 | +** email and clicks on the link in the email. When a |
| 1572 | +** compilete subscriberCode is seen on the name= query parameter, |
| 1573 | +** that constitutes verification of the email address. |
| 1565 | 1574 | ** |
| 1566 | | -** (1) The name= query parameter contains the subscriberCode. |
| 1575 | +** (2) The sid= query parameter contains an integer subscriberId. |
| 1576 | +** This only works for the administrator. It allows the |
| 1577 | +** administrator to edit any subscription. |
| 1567 | 1578 | ** |
| 1568 | | -** (2) The user is logged into an account other than "nobody" or |
| 1579 | +** (3) The user is logged into an account other than "nobody" or |
| 1569 | 1580 | ** "anonymous". In that case the notification settings |
| 1570 | 1581 | ** associated with that account can be edited without needing |
| 1571 | 1582 | ** to know the subscriber code. |
| 1583 | +** |
| 1584 | +** (4) The name= query parameter contains a 32-digit prefix of |
| 1585 | +** subscriber code. (Subscriber codes are normally 64 hex digits |
| 1586 | +** in length.) This uniquely identifies the subscriber without |
| 1587 | +** revealing the complete subscriber code, and hence without |
| 1588 | +** verifying the email address. |
| 1572 | 1589 | */ |
| 1573 | 1590 | void alert_page(void){ |
| 1574 | | - const char *zName = P("name"); |
| 1575 | | - Stmt q; |
| 1576 | | - int sa, sc, sf, st, sw, sx; |
| 1577 | | - int sdigest = 0, sdonotcall = 0, sverified = 0; |
| 1578 | | - int isLogin; /* Logged in as an individual */ |
| 1579 | | - const char *ssub = 0; |
| 1580 | | - const char *semail = 0; |
| 1581 | | - const char *smip; |
| 1582 | | - const char *suname = 0; |
| 1583 | | - const char *mtime; |
| 1584 | | - const char *sctime; |
| 1585 | | - int eErr = 0; |
| 1586 | | - char *zErr = 0; |
| 1591 | + const char *zName = 0; /* Value of the name= query parameter */ |
| 1592 | + Stmt q; /* For querying the database */ |
| 1593 | + int sa, sc, sf, st, sw, sx; /* Types of notifications requested */ |
| 1594 | + int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */ |
| 1595 | + int isLogin; /* True if logged in as an individual */ |
| 1596 | + const char *ssub = 0; /* Subscription flags */ |
| 1597 | + const char *semail = 0; /* Email address */ |
| 1598 | + const char *smip; /* */ |
| 1599 | + const char *suname = 0; /* Corresponding user.login value */ |
| 1600 | + const char *mtime; /* */ |
| 1601 | + const char *sctime; /* Time subscription created */ |
| 1602 | + int eErr = 0; /* Type of error */ |
| 1603 | + char *zErr = 0; /* Error message text */ |
| 1604 | + int sid = 0; /* Subscriber ID */ |
| 1605 | + int nName; /* Length of zName in bytes */ |
| 1606 | + char *zHalfCode; /* prefix of subscriberCode */ |
| 1587 | 1607 | |
| 1588 | 1608 | if( alert_webpages_disabled() ) return; |
| 1589 | 1609 | login_check_credentials(); |
| 1590 | 1610 | if( !g.perm.EmailAlert ){ |
| 1591 | 1611 | login_needed(g.anon.EmailAlert); |
| 1592 | 1612 | return; |
| 1593 | 1613 | } |
| 1594 | 1614 | isLogin = login_is_individual(); |
| 1595 | | - if( zName==0 && isLogin ){ |
| 1596 | | - zName = db_text(0, "SELECT hex(subscriberCode) FROM subscriber" |
| 1597 | | - " WHERE suname=%Q", g.zLogin); |
| 1615 | + zName = P("name"); |
| 1616 | + nName = zName ? (int)strlen(zName) : 0; |
| 1617 | + if( g.perm.Admin && P("sid")!=0 ){ |
| 1618 | + sid = atoi(P("sid")); |
| 1619 | + } |
| 1620 | + if( sid==0 && nName>=32 ){ |
| 1621 | + sid = db_int(0, |
| 1622 | + "SELECT CASE WHEN hex(subscriberCode) LIKE (%Q||'%%')" |
| 1623 | + " THEN subscriberId ELSE 0 END" |
| 1624 | + " FROM subscriber WHERE subscriberCode>=hextoblob(%Q)" |
| 1625 | + " LIMIT 1", zName, zName); |
| 1626 | + } |
| 1627 | + if( sid==0 && isLogin ){ |
| 1628 | + sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 1629 | + " WHERE suname=%Q", g.zLogin); |
| 1598 | 1630 | } |
| 1599 | | - if( zName==0 || !validate16(zName, -1) ){ |
| 1631 | + if( sid==0 ){ |
| 1600 | 1632 | cgi_redirect("subscribe"); |
| 1601 | 1633 | return; |
| 1602 | 1634 | } |
| 1603 | 1635 | alert_submenu_common(); |
| 1604 | 1636 | if( P("submit")!=0 && cgi_csrf_safe(1) ){ |
| | @@ -1644,11 +1676,11 @@ |
| 1644 | 1676 | if( semail==0 || email_address_is_valid(semail,0)==0 ){ |
| 1645 | 1677 | eErr = 8; |
| 1646 | 1678 | } |
| 1647 | 1679 | blob_append_sql(&update, ", semail=%Q", semail); |
| 1648 | 1680 | } |
| 1649 | | - blob_append_sql(&update," WHERE subscriberCode=hextoblob(%Q)", zName); |
| 1681 | + blob_append_sql(&update," WHERE subscriberId=%d", sid); |
| 1650 | 1682 | if( eErr==0 ){ |
| 1651 | 1683 | db_exec_sql(blob_str(&update)); |
| 1652 | 1684 | ssub = 0; |
| 1653 | 1685 | } |
| 1654 | 1686 | blob_reset(&update); |
| | @@ -1657,11 +1689,11 @@ |
| 1657 | 1689 | if( !PB("dodelete") ){ |
| 1658 | 1690 | eErr = 9; |
| 1659 | 1691 | zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to" |
| 1660 | 1692 | " unsubscribe"); |
| 1661 | 1693 | }else{ |
| 1662 | | - alert_unsubscribe(zName); |
| 1694 | + alert_unsubscribe(sid); |
| 1663 | 1695 | return; |
| 1664 | 1696 | } |
| 1665 | 1697 | } |
| 1666 | 1698 | style_header("Update Subscription"); |
| 1667 | 1699 | db_prepare(&q, |
| | @@ -1672,12 +1704,13 @@ |
| 1672 | 1704 | " sdigest," /* 3 */ |
| 1673 | 1705 | " ssub," /* 4 */ |
| 1674 | 1706 | " smip," /* 5 */ |
| 1675 | 1707 | " suname," /* 6 */ |
| 1676 | 1708 | " datetime(mtime,'unixepoch')," /* 7 */ |
| 1677 | | - " datetime(sctime,'unixepoch')" /* 8 */ |
| 1678 | | - " FROM subscriber WHERE subscriberCode=hextoblob(%Q)", zName); |
| 1709 | + " datetime(sctime,'unixepoch')," /* 8 */ |
| 1710 | + " hex(subscriberCode)" /* 9 */ |
| 1711 | + " FROM subscriber WHERE subscriberId=%d", sid); |
| 1679 | 1712 | if( db_step(&q)!=SQLITE_ROW ){ |
| 1680 | 1713 | db_finalize(&q); |
| 1681 | 1714 | cgi_redirect("subscribe"); |
| 1682 | 1715 | return; |
| 1683 | 1716 | } |
| | @@ -1699,23 +1732,32 @@ |
| 1699 | 1732 | sx = strchr(ssub,'x')!=0; |
| 1700 | 1733 | smip = db_column_text(&q, 5); |
| 1701 | 1734 | mtime = db_column_text(&q, 7); |
| 1702 | 1735 | sctime = db_column_text(&q, 8); |
| 1703 | 1736 | if( !g.perm.Admin && !sverified ){ |
| 1704 | | - db_multi_exec( |
| 1705 | | - "UPDATE subscriber SET sverified=1 WHERE subscriberCode=hextoblob(%Q)", |
| 1706 | | - zName); |
| 1707 | | - @ <h1>Your email alert subscription has been verified!</h1> |
| 1708 | | - @ <p>Use the form below to update your subscription information.</p> |
| 1709 | | - @ <p>Hint: Bookmark this page so that you can more easily update |
| 1710 | | - @ your subscription information in the future</p> |
| 1737 | + if( nName==64 ){ |
| 1738 | + db_multi_exec( |
| 1739 | + "UPDATE subscriber SET sverified=1 WHERE subscriberCode=hextoblob(%Q)", |
| 1740 | + zName); |
| 1741 | + @ <h1>Your email alert subscription has been verified!</h1> |
| 1742 | + @ <p>Use the form below to update your subscription information.</p> |
| 1743 | + @ <p>Hint: Bookmark this page so that you can more easily update |
| 1744 | + @ your subscription information in the future</p> |
| 1745 | + }else{ |
| 1746 | + @ <h2>Your email address is unverified</h2> |
| 1747 | + @ <p>You should have received an email message containing a link |
| 1748 | + @ that you must visit to verify your account. No email notifications |
| 1749 | + @ will be sent until your email address has been verified.</p> |
| 1750 | + } |
| 1711 | 1751 | }else{ |
| 1712 | 1752 | @ <p>Make changes to the email subscription shown below and |
| 1713 | 1753 | @ press "Submit".</p> |
| 1714 | 1754 | } |
| 1715 | 1755 | form_begin(0, "%R/alerts"); |
| 1716 | | - @ <input type="hidden" name="name" value="%h(zName)"> |
| 1756 | + zHalfCode = db_text("x","SELECT hex(substr(subscriberCode,1,16))" |
| 1757 | + " FROM subscriber WHERE subscriberId=%d", sid); |
| 1758 | + @ <input type="hidden" name="name" value="%h(zHalfCode)"> |
| 1717 | 1759 | @ <table class="subscribe"> |
| 1718 | 1760 | @ <tr> |
| 1719 | 1761 | @ <td class="form_label">Email Address:</td> |
| 1720 | 1762 | if( isLogin ){ |
| 1721 | 1763 | @ <td><input type="text" name="semail" value="%h(semail)" size="30">\ |
| | @@ -1742,10 +1784,13 @@ |
| 1742 | 1784 | @ </tr> |
| 1743 | 1785 | @ <tr> |
| 1744 | 1786 | @ <td class='form_label'>IP Address:</td> |
| 1745 | 1787 | @ <td>%h(smip)</td> |
| 1746 | 1788 | @ </tr> |
| 1789 | + @ <tr> |
| 1790 | + @ <td class='form_label'>Subscriber Code:</td> |
| 1791 | + @ <td>%h(db_column_text(&q,9))</td> |
| 1747 | 1792 | @ <tr> |
| 1748 | 1793 | @ <td class="form_label">User:</td> |
| 1749 | 1794 | @ <td><input type="text" name="suname" value="%h(suname?suname:"")" \ |
| 1750 | 1795 | @ size="30">\ |
| 1751 | 1796 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", suname); |
| | @@ -1783,14 +1828,10 @@ |
| 1783 | 1828 | @ <td><select size="1" name="sdigest"> |
| 1784 | 1829 | @ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option> |
| 1785 | 1830 | @ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option> |
| 1786 | 1831 | @ </select></td> |
| 1787 | 1832 | @ </tr> |
| 1788 | | -#if 0 |
| 1789 | | - @ <label><input type="checkbox" name="sdigest" %s(sdigest?"checked":"")>\ |
| 1790 | | - @ Daily digest only</label><br> |
| 1791 | | -#endif |
| 1792 | 1833 | if( g.perm.Admin ){ |
| 1793 | 1834 | @ <tr> |
| 1794 | 1835 | @ <td class="form_label">Admin Options:</td><td> |
| 1795 | 1836 | @ <label><input type="checkbox" name="sdonotcall" \ |
| 1796 | 1837 | @ %s(sdonotcall?"checked":"")> Do not disturb</label><br> |
| | @@ -1855,18 +1896,19 @@ |
| 1855 | 1896 | char *zCaptcha = 0; |
| 1856 | 1897 | int dx; |
| 1857 | 1898 | int bSubmit; |
| 1858 | 1899 | const char *zEAddr; |
| 1859 | 1900 | char *zCode = 0; |
| 1901 | + int sid = 0; |
| 1860 | 1902 | |
| 1861 | 1903 | /* If a valid subscriber code is supplied, then unsubscribe immediately. |
| 1862 | 1904 | */ |
| 1863 | 1905 | if( zName |
| 1864 | | - && db_exists("SELECT 1 FROM subscriber WHERE subscriberCode=hextoblob(%Q)", |
| 1865 | | - zName) |
| 1906 | + && (sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 1907 | + " WHERE subscriberCode=hextoblob(%Q)", zName))!=0 |
| 1866 | 1908 | ){ |
| 1867 | | - alert_unsubscribe(zName); |
| 1909 | + alert_unsubscribe(sid); |
| 1868 | 1910 | return; |
| 1869 | 1911 | } |
| 1870 | 1912 | |
| 1871 | 1913 | /* Logged in users are redirected to the /alerts page */ |
| 1872 | 1914 | login_check_credentials(); |
| | @@ -2024,11 +2066,11 @@ |
| 2024 | 2066 | if( nDel>0 ){ |
| 2025 | 2067 | @ <p>*** %d(nDel) pending subscriptions deleted ***</p> |
| 2026 | 2068 | } |
| 2027 | 2069 | blob_init(&sql, 0, 0); |
| 2028 | 2070 | blob_append_sql(&sql, |
| 2029 | | - "SELECT hex(subscriberCode)," /* 0 */ |
| 2071 | + "SELECT subscriberId," /* 0 */ |
| 2030 | 2072 | " semail," /* 1 */ |
| 2031 | 2073 | " ssub," /* 2 */ |
| 2032 | 2074 | " suname," /* 3 */ |
| 2033 | 2075 | " sverified," /* 4 */ |
| 2034 | 2076 | " sdigest," /* 5 */ |
| | @@ -2061,11 +2103,11 @@ |
| 2061 | 2103 | sqlite3_int64 iMtime = db_column_int64(&q, 6); |
| 2062 | 2104 | double rAge = (iNow - iMtime)/86400.0; |
| 2063 | 2105 | int uid = db_column_int(&q, 8); |
| 2064 | 2106 | const char *zUname = db_column_text(&q, 3); |
| 2065 | 2107 | @ <tr> |
| 2066 | | - @ <td><a href='%R/alerts/%s(db_column_text(&q,0))'>\ |
| 2108 | + @ <td><a href='%R/alerts?sid=%d(db_column_int(&q,0))'>\ |
| 2067 | 2109 | @ %h(db_column_text(&q,1))</a></td> |
| 2068 | 2110 | @ <td>%h(db_column_text(&q,2))</td> |
| 2069 | 2111 | @ <td>%s(db_column_int(&q,5)?"digest":"")</td> |
| 2070 | 2112 | if( uid ){ |
| 2071 | 2113 | @ <td><a href='%R/setup_uedit?id=%d(uid)'>%h(zUname)</a> |
| 2072 | 2114 | |