Fossil SCM
Add the ability to restrict subscriptions to specific email domains selected by GLOB patterns, and to require email verification before self-registration becomes effective.
Commit
c00e9123cb89aacdc2992917af19bd3e30f2d021337c42d416baacfc8d7bd5bc
Parent
15e15298f8eebef…
9 files changed
+60
-15
+1
-1
+54
-10
+8
-2
+1
-1
+34
-7
+1
-1
+1
-11
+15
+60
-15
| --- src/alerts.c | ||
| +++ src/alerts.c | ||
| @@ -1208,10 +1208,19 @@ | ||
| 1208 | 1208 | int i, j, n; |
| 1209 | 1209 | char c; |
| 1210 | 1210 | |
| 1211 | 1211 | *peErr = 0; |
| 1212 | 1212 | *pzErr = 0; |
| 1213 | + | |
| 1214 | + /* Verify the captcha first */ | |
| 1215 | + if( needCaptcha ){ | |
| 1216 | + if( !captcha_is_correct(1) ){ | |
| 1217 | + *peErr = 2; | |
| 1218 | + *pzErr = mprintf("incorrect security code"); | |
| 1219 | + return 0; | |
| 1220 | + } | |
| 1221 | + } | |
| 1213 | 1222 | |
| 1214 | 1223 | /* Check the validity of the email address. |
| 1215 | 1224 | ** |
| 1216 | 1225 | ** (1) Exactly one '@' character. |
| 1217 | 1226 | ** (2) No other characters besides [a-zA-Z0-9._+-] |
| @@ -1219,11 +1228,15 @@ | ||
| 1219 | 1228 | ** The local part is currently more restrictive than RFC 5322 allows: |
| 1220 | 1229 | ** https://stackoverflow.com/a/2049510/142454 We will expand this as |
| 1221 | 1230 | ** necessary. |
| 1222 | 1231 | */ |
| 1223 | 1232 | zEAddr = P("e"); |
| 1224 | - if( zEAddr==0 ) return 0; | |
| 1233 | + if( zEAddr==0 ){ | |
| 1234 | + *peErr = 1; | |
| 1235 | + *pzErr = mprintf("required"); | |
| 1236 | + return 0; | |
| 1237 | + } | |
| 1225 | 1238 | for(i=j=n=0; (c = zEAddr[i])!=0; i++){ |
| 1226 | 1239 | if( c=='@' ){ |
| 1227 | 1240 | n = i; |
| 1228 | 1241 | j++; |
| 1229 | 1242 | continue; |
| @@ -1249,14 +1262,13 @@ | ||
| 1249 | 1262 | *peErr = 1; |
| 1250 | 1263 | *pzErr = mprintf("email domain too short"); |
| 1251 | 1264 | return 0; |
| 1252 | 1265 | } |
| 1253 | 1266 | |
| 1254 | - /* Verify the captcha */ | |
| 1255 | - if( needCaptcha && !captcha_is_correct(1) ){ | |
| 1256 | - *peErr = 2; | |
| 1257 | - *pzErr = mprintf("incorrect security code"); | |
| 1267 | + if( authorized_subscription_email(zEAddr)==0 ){ | |
| 1268 | + *peErr = 1; | |
| 1269 | + *pzErr = mprintf("not an authorized email address"); | |
| 1258 | 1270 | return 0; |
| 1259 | 1271 | } |
| 1260 | 1272 | |
| 1261 | 1273 | /* Check to make sure the email address is available for reuse */ |
| 1262 | 1274 | if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q", zEAddr) ){ |
| @@ -1347,10 +1359,14 @@ | ||
| 1347 | 1359 | /* Everybody else jumps to the page to administer their own |
| 1348 | 1360 | ** account only. */ |
| 1349 | 1361 | cgi_redirectf("%R/alerts"); |
| 1350 | 1362 | return; |
| 1351 | 1363 | } |
| 1364 | + } | |
| 1365 | + if( !g.perm.Admin && !db_get_boolean("anon-subscribe",1) ){ | |
| 1366 | + register_page(); | |
| 1367 | + return; | |
| 1352 | 1368 | } |
| 1353 | 1369 | alert_submenu_common(); |
| 1354 | 1370 | needCaptcha = !login_is_individual(); |
| 1355 | 1371 | if( P("submit") |
| 1356 | 1372 | && cgi_csrf_safe(1) |
| @@ -1415,11 +1431,11 @@ | ||
| 1415 | 1431 | @ <blockquote><pre> |
| 1416 | 1432 | @ %h(pSender->zErr) |
| 1417 | 1433 | @ </pre></blockquote> |
| 1418 | 1434 | }else{ |
| 1419 | 1435 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1420 | - @ hyperlink that you must click on in order to activate your | |
| 1436 | + @ hyperlink that you must click to activate your | |
| 1421 | 1437 | @ subscription.</p> |
| 1422 | 1438 | } |
| 1423 | 1439 | alert_sender_free(pSender); |
| 1424 | 1440 | style_footer(); |
| 1425 | 1441 | } |
| @@ -1447,16 +1463,22 @@ | ||
| 1447 | 1463 | if( eErr==1 ){ |
| 1448 | 1464 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1449 | 1465 | } |
| 1450 | 1466 | @ </tr> |
| 1451 | 1467 | if( needCaptcha ){ |
| 1452 | - uSeed = captcha_seed(); | |
| 1468 | + const char *zInit = ""; | |
| 1469 | + if( P("captchaseed")!=0 && eErr!=2 ){ | |
| 1470 | + uSeed = strtoul(P("captchaseed"),0,10); | |
| 1471 | + zInit = P("captcha"); | |
| 1472 | + }else{ | |
| 1473 | + uSeed = captcha_seed(); | |
| 1474 | + } | |
| 1453 | 1475 | zDecoded = captcha_decode(uSeed); |
| 1454 | 1476 | zCaptcha = captcha_render(zDecoded); |
| 1455 | 1477 | @ <tr> |
| 1456 | 1478 | @ <td class="form_label">Security Code:</td> |
| 1457 | - @ <td><input type="text" name="captcha" value="" size="30"> | |
| 1479 | + @ <td><input type="text" name="captcha" value="%h(zInit)" size="30"> | |
| 1458 | 1480 | captcha_speakit_button(uSeed, "Speak the code"); |
| 1459 | 1481 | @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> |
| 1460 | 1482 | @ </tr> |
| 1461 | 1483 | if( eErr==2 ){ |
| 1462 | 1484 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| @@ -1603,15 +1625,20 @@ | ||
| 1603 | 1625 | char *zErr = 0; /* Error message text */ |
| 1604 | 1626 | int sid = 0; /* Subscriber ID */ |
| 1605 | 1627 | int nName; /* Length of zName in bytes */ |
| 1606 | 1628 | char *zHalfCode; /* prefix of subscriberCode */ |
| 1607 | 1629 | |
| 1608 | - if( alert_webpages_disabled() ) return; | |
| 1630 | + db_begin_transaction(); | |
| 1631 | + if( alert_webpages_disabled() ){ | |
| 1632 | + db_commit_transaction(); | |
| 1633 | + return; | |
| 1634 | + } | |
| 1609 | 1635 | login_check_credentials(); |
| 1610 | 1636 | if( !g.perm.EmailAlert ){ |
| 1637 | + db_commit_transaction(); | |
| 1611 | 1638 | login_needed(g.anon.EmailAlert); |
| 1612 | - return; | |
| 1639 | + /*NOTREACHED*/ | |
| 1613 | 1640 | } |
| 1614 | 1641 | isLogin = login_is_individual(); |
| 1615 | 1642 | zName = P("name"); |
| 1616 | 1643 | nName = zName ? (int)strlen(zName) : 0; |
| 1617 | 1644 | if( g.perm.Admin && P("sid")!=0 ){ |
| @@ -1627,12 +1654,13 @@ | ||
| 1627 | 1654 | if( sid==0 && isLogin ){ |
| 1628 | 1655 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 1629 | 1656 | " WHERE suname=%Q", g.zLogin); |
| 1630 | 1657 | } |
| 1631 | 1658 | if( sid==0 ){ |
| 1659 | + db_commit_transaction(); | |
| 1632 | 1660 | cgi_redirect("subscribe"); |
| 1633 | - return; | |
| 1661 | + /*NOTREACHED*/ | |
| 1634 | 1662 | } |
| 1635 | 1663 | alert_submenu_common(); |
| 1636 | 1664 | if( P("submit")!=0 && cgi_csrf_safe(1) ){ |
| 1637 | 1665 | char newSsub[10]; |
| 1638 | 1666 | int nsub = 0; |
| @@ -1690,11 +1718,12 @@ | ||
| 1690 | 1718 | eErr = 9; |
| 1691 | 1719 | zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to" |
| 1692 | 1720 | " unsubscribe"); |
| 1693 | 1721 | }else{ |
| 1694 | 1722 | alert_unsubscribe(sid); |
| 1695 | - return; | |
| 1723 | + db_commit_transaction(); | |
| 1724 | + return; | |
| 1696 | 1725 | } |
| 1697 | 1726 | } |
| 1698 | 1727 | style_header("Update Subscription"); |
| 1699 | 1728 | db_prepare(&q, |
| 1700 | 1729 | "SELECT" |
| @@ -1709,12 +1738,13 @@ | ||
| 1709 | 1738 | " datetime(sctime,'unixepoch')," /* 8 */ |
| 1710 | 1739 | " hex(subscriberCode)" /* 9 */ |
| 1711 | 1740 | " FROM subscriber WHERE subscriberId=%d", sid); |
| 1712 | 1741 | if( db_step(&q)!=SQLITE_ROW ){ |
| 1713 | 1742 | db_finalize(&q); |
| 1743 | + db_commit_transaction(); | |
| 1714 | 1744 | cgi_redirect("subscribe"); |
| 1715 | - return; | |
| 1745 | + /*NOTREACHED*/ | |
| 1716 | 1746 | } |
| 1717 | 1747 | if( ssub==0 ){ |
| 1718 | 1748 | semail = db_column_text(&q, 0); |
| 1719 | 1749 | sdonotcall = db_column_int(&q, 2); |
| 1720 | 1750 | sdigest = db_column_int(&q, 3); |
| @@ -1733,13 +1763,26 @@ | ||
| 1733 | 1763 | smip = db_column_text(&q, 5); |
| 1734 | 1764 | mtime = db_column_text(&q, 7); |
| 1735 | 1765 | sctime = db_column_text(&q, 8); |
| 1736 | 1766 | if( !g.perm.Admin && !sverified ){ |
| 1737 | 1767 | if( nName==64 ){ |
| 1738 | - db_multi_exec( | |
| 1739 | - "UPDATE subscriber SET sverified=1 WHERE subscriberCode=hextoblob(%Q)", | |
| 1768 | + db_multi_exec( | |
| 1769 | + "UPDATE subscriber SET sverified=1" | |
| 1770 | + " WHERE subscriberCode=hextoblob(%Q)", | |
| 1740 | 1771 | zName); |
| 1772 | + if( db_get_boolean("selfreg-verify",0) ){ | |
| 1773 | + char *zNewCap = db_get("default-perms","u"); | |
| 1774 | + db_multi_exec( | |
| 1775 | + "UPDATE user" | |
| 1776 | + " SET cap=%Q" | |
| 1777 | + " WHERE cap='7' AND login=(" | |
| 1778 | + " SELECT suname FROM subscriber" | |
| 1779 | + " WHERE subscriberCode=hextoblob(%Q))", | |
| 1780 | + zNewCap, zName | |
| 1781 | + ); | |
| 1782 | + login_set_capabilities(zNewCap, 0); | |
| 1783 | + } | |
| 1741 | 1784 | @ <h1>Your email alert subscription has been verified!</h1> |
| 1742 | 1785 | @ <p>Use the form below to update your subscription information.</p> |
| 1743 | 1786 | @ <p>Hint: Bookmark this page so that you can more easily update |
| 1744 | 1787 | @ your subscription information in the future</p> |
| 1745 | 1788 | }else{ |
| @@ -1855,10 +1898,12 @@ | ||
| 1855 | 1898 | @ </table> |
| 1856 | 1899 | @ </form> |
| 1857 | 1900 | fossil_free(zErr); |
| 1858 | 1901 | db_finalize(&q); |
| 1859 | 1902 | style_footer(); |
| 1903 | + db_commit_transaction(); | |
| 1904 | + return; | |
| 1860 | 1905 | } |
| 1861 | 1906 | |
| 1862 | 1907 | /* This is the message that gets sent to describe how to change |
| 1863 | 1908 | ** or modify a subscription |
| 1864 | 1909 | */ |
| 1865 | 1910 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -1208,10 +1208,19 @@ | |
| 1208 | int i, j, n; |
| 1209 | char c; |
| 1210 | |
| 1211 | *peErr = 0; |
| 1212 | *pzErr = 0; |
| 1213 | |
| 1214 | /* Check the validity of the email address. |
| 1215 | ** |
| 1216 | ** (1) Exactly one '@' character. |
| 1217 | ** (2) No other characters besides [a-zA-Z0-9._+-] |
| @@ -1219,11 +1228,15 @@ | |
| 1219 | ** The local part is currently more restrictive than RFC 5322 allows: |
| 1220 | ** https://stackoverflow.com/a/2049510/142454 We will expand this as |
| 1221 | ** necessary. |
| 1222 | */ |
| 1223 | zEAddr = P("e"); |
| 1224 | if( zEAddr==0 ) return 0; |
| 1225 | for(i=j=n=0; (c = zEAddr[i])!=0; i++){ |
| 1226 | if( c=='@' ){ |
| 1227 | n = i; |
| 1228 | j++; |
| 1229 | continue; |
| @@ -1249,14 +1262,13 @@ | |
| 1249 | *peErr = 1; |
| 1250 | *pzErr = mprintf("email domain too short"); |
| 1251 | return 0; |
| 1252 | } |
| 1253 | |
| 1254 | /* Verify the captcha */ |
| 1255 | if( needCaptcha && !captcha_is_correct(1) ){ |
| 1256 | *peErr = 2; |
| 1257 | *pzErr = mprintf("incorrect security code"); |
| 1258 | return 0; |
| 1259 | } |
| 1260 | |
| 1261 | /* Check to make sure the email address is available for reuse */ |
| 1262 | if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q", zEAddr) ){ |
| @@ -1347,10 +1359,14 @@ | |
| 1347 | /* Everybody else jumps to the page to administer their own |
| 1348 | ** account only. */ |
| 1349 | cgi_redirectf("%R/alerts"); |
| 1350 | return; |
| 1351 | } |
| 1352 | } |
| 1353 | alert_submenu_common(); |
| 1354 | needCaptcha = !login_is_individual(); |
| 1355 | if( P("submit") |
| 1356 | && cgi_csrf_safe(1) |
| @@ -1415,11 +1431,11 @@ | |
| 1415 | @ <blockquote><pre> |
| 1416 | @ %h(pSender->zErr) |
| 1417 | @ </pre></blockquote> |
| 1418 | }else{ |
| 1419 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1420 | @ hyperlink that you must click on in order to activate your |
| 1421 | @ subscription.</p> |
| 1422 | } |
| 1423 | alert_sender_free(pSender); |
| 1424 | style_footer(); |
| 1425 | } |
| @@ -1447,16 +1463,22 @@ | |
| 1447 | if( eErr==1 ){ |
| 1448 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1449 | } |
| 1450 | @ </tr> |
| 1451 | if( needCaptcha ){ |
| 1452 | uSeed = captcha_seed(); |
| 1453 | zDecoded = captcha_decode(uSeed); |
| 1454 | zCaptcha = captcha_render(zDecoded); |
| 1455 | @ <tr> |
| 1456 | @ <td class="form_label">Security Code:</td> |
| 1457 | @ <td><input type="text" name="captcha" value="" size="30"> |
| 1458 | captcha_speakit_button(uSeed, "Speak the code"); |
| 1459 | @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> |
| 1460 | @ </tr> |
| 1461 | if( eErr==2 ){ |
| 1462 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| @@ -1603,15 +1625,20 @@ | |
| 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 */ |
| 1607 | |
| 1608 | if( alert_webpages_disabled() ) return; |
| 1609 | login_check_credentials(); |
| 1610 | if( !g.perm.EmailAlert ){ |
| 1611 | login_needed(g.anon.EmailAlert); |
| 1612 | return; |
| 1613 | } |
| 1614 | isLogin = login_is_individual(); |
| 1615 | zName = P("name"); |
| 1616 | nName = zName ? (int)strlen(zName) : 0; |
| 1617 | if( g.perm.Admin && P("sid")!=0 ){ |
| @@ -1627,12 +1654,13 @@ | |
| 1627 | if( sid==0 && isLogin ){ |
| 1628 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 1629 | " WHERE suname=%Q", g.zLogin); |
| 1630 | } |
| 1631 | if( sid==0 ){ |
| 1632 | cgi_redirect("subscribe"); |
| 1633 | return; |
| 1634 | } |
| 1635 | alert_submenu_common(); |
| 1636 | if( P("submit")!=0 && cgi_csrf_safe(1) ){ |
| 1637 | char newSsub[10]; |
| 1638 | int nsub = 0; |
| @@ -1690,11 +1718,12 @@ | |
| 1690 | eErr = 9; |
| 1691 | zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to" |
| 1692 | " unsubscribe"); |
| 1693 | }else{ |
| 1694 | alert_unsubscribe(sid); |
| 1695 | return; |
| 1696 | } |
| 1697 | } |
| 1698 | style_header("Update Subscription"); |
| 1699 | db_prepare(&q, |
| 1700 | "SELECT" |
| @@ -1709,12 +1738,13 @@ | |
| 1709 | " datetime(sctime,'unixepoch')," /* 8 */ |
| 1710 | " hex(subscriberCode)" /* 9 */ |
| 1711 | " FROM subscriber WHERE subscriberId=%d", sid); |
| 1712 | if( db_step(&q)!=SQLITE_ROW ){ |
| 1713 | db_finalize(&q); |
| 1714 | cgi_redirect("subscribe"); |
| 1715 | return; |
| 1716 | } |
| 1717 | if( ssub==0 ){ |
| 1718 | semail = db_column_text(&q, 0); |
| 1719 | sdonotcall = db_column_int(&q, 2); |
| 1720 | sdigest = db_column_int(&q, 3); |
| @@ -1733,13 +1763,26 @@ | |
| 1733 | smip = db_column_text(&q, 5); |
| 1734 | mtime = db_column_text(&q, 7); |
| 1735 | sctime = db_column_text(&q, 8); |
| 1736 | if( !g.perm.Admin && !sverified ){ |
| 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{ |
| @@ -1855,10 +1898,12 @@ | |
| 1855 | @ </table> |
| 1856 | @ </form> |
| 1857 | fossil_free(zErr); |
| 1858 | db_finalize(&q); |
| 1859 | style_footer(); |
| 1860 | } |
| 1861 | |
| 1862 | /* This is the message that gets sent to describe how to change |
| 1863 | ** or modify a subscription |
| 1864 | */ |
| 1865 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -1208,10 +1208,19 @@ | |
| 1208 | int i, j, n; |
| 1209 | char c; |
| 1210 | |
| 1211 | *peErr = 0; |
| 1212 | *pzErr = 0; |
| 1213 | |
| 1214 | /* Verify the captcha first */ |
| 1215 | if( needCaptcha ){ |
| 1216 | if( !captcha_is_correct(1) ){ |
| 1217 | *peErr = 2; |
| 1218 | *pzErr = mprintf("incorrect security code"); |
| 1219 | return 0; |
| 1220 | } |
| 1221 | } |
| 1222 | |
| 1223 | /* Check the validity of the email address. |
| 1224 | ** |
| 1225 | ** (1) Exactly one '@' character. |
| 1226 | ** (2) No other characters besides [a-zA-Z0-9._+-] |
| @@ -1219,11 +1228,15 @@ | |
| 1228 | ** The local part is currently more restrictive than RFC 5322 allows: |
| 1229 | ** https://stackoverflow.com/a/2049510/142454 We will expand this as |
| 1230 | ** necessary. |
| 1231 | */ |
| 1232 | zEAddr = P("e"); |
| 1233 | if( zEAddr==0 ){ |
| 1234 | *peErr = 1; |
| 1235 | *pzErr = mprintf("required"); |
| 1236 | return 0; |
| 1237 | } |
| 1238 | for(i=j=n=0; (c = zEAddr[i])!=0; i++){ |
| 1239 | if( c=='@' ){ |
| 1240 | n = i; |
| 1241 | j++; |
| 1242 | continue; |
| @@ -1249,14 +1262,13 @@ | |
| 1262 | *peErr = 1; |
| 1263 | *pzErr = mprintf("email domain too short"); |
| 1264 | return 0; |
| 1265 | } |
| 1266 | |
| 1267 | if( authorized_subscription_email(zEAddr)==0 ){ |
| 1268 | *peErr = 1; |
| 1269 | *pzErr = mprintf("not an authorized email address"); |
| 1270 | return 0; |
| 1271 | } |
| 1272 | |
| 1273 | /* Check to make sure the email address is available for reuse */ |
| 1274 | if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q", zEAddr) ){ |
| @@ -1347,10 +1359,14 @@ | |
| 1359 | /* Everybody else jumps to the page to administer their own |
| 1360 | ** account only. */ |
| 1361 | cgi_redirectf("%R/alerts"); |
| 1362 | return; |
| 1363 | } |
| 1364 | } |
| 1365 | if( !g.perm.Admin && !db_get_boolean("anon-subscribe",1) ){ |
| 1366 | register_page(); |
| 1367 | return; |
| 1368 | } |
| 1369 | alert_submenu_common(); |
| 1370 | needCaptcha = !login_is_individual(); |
| 1371 | if( P("submit") |
| 1372 | && cgi_csrf_safe(1) |
| @@ -1415,11 +1431,11 @@ | |
| 1431 | @ <blockquote><pre> |
| 1432 | @ %h(pSender->zErr) |
| 1433 | @ </pre></blockquote> |
| 1434 | }else{ |
| 1435 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1436 | @ hyperlink that you must click to activate your |
| 1437 | @ subscription.</p> |
| 1438 | } |
| 1439 | alert_sender_free(pSender); |
| 1440 | style_footer(); |
| 1441 | } |
| @@ -1447,16 +1463,22 @@ | |
| 1463 | if( eErr==1 ){ |
| 1464 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1465 | } |
| 1466 | @ </tr> |
| 1467 | if( needCaptcha ){ |
| 1468 | const char *zInit = ""; |
| 1469 | if( P("captchaseed")!=0 && eErr!=2 ){ |
| 1470 | uSeed = strtoul(P("captchaseed"),0,10); |
| 1471 | zInit = P("captcha"); |
| 1472 | }else{ |
| 1473 | uSeed = captcha_seed(); |
| 1474 | } |
| 1475 | zDecoded = captcha_decode(uSeed); |
| 1476 | zCaptcha = captcha_render(zDecoded); |
| 1477 | @ <tr> |
| 1478 | @ <td class="form_label">Security Code:</td> |
| 1479 | @ <td><input type="text" name="captcha" value="%h(zInit)" size="30"> |
| 1480 | captcha_speakit_button(uSeed, "Speak the code"); |
| 1481 | @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> |
| 1482 | @ </tr> |
| 1483 | if( eErr==2 ){ |
| 1484 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| @@ -1603,15 +1625,20 @@ | |
| 1625 | char *zErr = 0; /* Error message text */ |
| 1626 | int sid = 0; /* Subscriber ID */ |
| 1627 | int nName; /* Length of zName in bytes */ |
| 1628 | char *zHalfCode; /* prefix of subscriberCode */ |
| 1629 | |
| 1630 | db_begin_transaction(); |
| 1631 | if( alert_webpages_disabled() ){ |
| 1632 | db_commit_transaction(); |
| 1633 | return; |
| 1634 | } |
| 1635 | login_check_credentials(); |
| 1636 | if( !g.perm.EmailAlert ){ |
| 1637 | db_commit_transaction(); |
| 1638 | login_needed(g.anon.EmailAlert); |
| 1639 | /*NOTREACHED*/ |
| 1640 | } |
| 1641 | isLogin = login_is_individual(); |
| 1642 | zName = P("name"); |
| 1643 | nName = zName ? (int)strlen(zName) : 0; |
| 1644 | if( g.perm.Admin && P("sid")!=0 ){ |
| @@ -1627,12 +1654,13 @@ | |
| 1654 | if( sid==0 && isLogin ){ |
| 1655 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 1656 | " WHERE suname=%Q", g.zLogin); |
| 1657 | } |
| 1658 | if( sid==0 ){ |
| 1659 | db_commit_transaction(); |
| 1660 | cgi_redirect("subscribe"); |
| 1661 | /*NOTREACHED*/ |
| 1662 | } |
| 1663 | alert_submenu_common(); |
| 1664 | if( P("submit")!=0 && cgi_csrf_safe(1) ){ |
| 1665 | char newSsub[10]; |
| 1666 | int nsub = 0; |
| @@ -1690,11 +1718,12 @@ | |
| 1718 | eErr = 9; |
| 1719 | zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to" |
| 1720 | " unsubscribe"); |
| 1721 | }else{ |
| 1722 | alert_unsubscribe(sid); |
| 1723 | db_commit_transaction(); |
| 1724 | return; |
| 1725 | } |
| 1726 | } |
| 1727 | style_header("Update Subscription"); |
| 1728 | db_prepare(&q, |
| 1729 | "SELECT" |
| @@ -1709,12 +1738,13 @@ | |
| 1738 | " datetime(sctime,'unixepoch')," /* 8 */ |
| 1739 | " hex(subscriberCode)" /* 9 */ |
| 1740 | " FROM subscriber WHERE subscriberId=%d", sid); |
| 1741 | if( db_step(&q)!=SQLITE_ROW ){ |
| 1742 | db_finalize(&q); |
| 1743 | db_commit_transaction(); |
| 1744 | cgi_redirect("subscribe"); |
| 1745 | /*NOTREACHED*/ |
| 1746 | } |
| 1747 | if( ssub==0 ){ |
| 1748 | semail = db_column_text(&q, 0); |
| 1749 | sdonotcall = db_column_int(&q, 2); |
| 1750 | sdigest = db_column_int(&q, 3); |
| @@ -1733,13 +1763,26 @@ | |
| 1763 | smip = db_column_text(&q, 5); |
| 1764 | mtime = db_column_text(&q, 7); |
| 1765 | sctime = db_column_text(&q, 8); |
| 1766 | if( !g.perm.Admin && !sverified ){ |
| 1767 | if( nName==64 ){ |
| 1768 | db_multi_exec( |
| 1769 | "UPDATE subscriber SET sverified=1" |
| 1770 | " WHERE subscriberCode=hextoblob(%Q)", |
| 1771 | zName); |
| 1772 | if( db_get_boolean("selfreg-verify",0) ){ |
| 1773 | char *zNewCap = db_get("default-perms","u"); |
| 1774 | db_multi_exec( |
| 1775 | "UPDATE user" |
| 1776 | " SET cap=%Q" |
| 1777 | " WHERE cap='7' AND login=(" |
| 1778 | " SELECT suname FROM subscriber" |
| 1779 | " WHERE subscriberCode=hextoblob(%Q))", |
| 1780 | zNewCap, zName |
| 1781 | ); |
| 1782 | login_set_capabilities(zNewCap, 0); |
| 1783 | } |
| 1784 | @ <h1>Your email alert subscription has been verified!</h1> |
| 1785 | @ <p>Use the form below to update your subscription information.</p> |
| 1786 | @ <p>Hint: Bookmark this page so that you can more easily update |
| 1787 | @ your subscription information in the future</p> |
| 1788 | }else{ |
| @@ -1855,10 +1898,12 @@ | |
| 1898 | @ </table> |
| 1899 | @ </form> |
| 1900 | fossil_free(zErr); |
| 1901 | db_finalize(&q); |
| 1902 | style_footer(); |
| 1903 | db_commit_transaction(); |
| 1904 | return; |
| 1905 | } |
| 1906 | |
| 1907 | /* This is the message that gets sent to describe how to change |
| 1908 | ** or modify a subscription |
| 1909 | */ |
| 1910 |
+1
-1
| --- src/capabilities.c | ||
| +++ src/capabilities.c | ||
| @@ -366,11 +366,11 @@ | ||
| 366 | 366 | CapabilityString *pCap; |
| 367 | 367 | char *zSelfCap; |
| 368 | 368 | char *zPubPages = db_get("public-pages",0); |
| 369 | 369 | int hasPubPages = zPubPages && zPubPages[0]; |
| 370 | 370 | |
| 371 | - pCap = capability_add(0, db_get("default-perms",0)); | |
| 371 | + pCap = capability_add(0, db_get("default-perms","u")); | |
| 372 | 372 | capability_expand(pCap); |
| 373 | 373 | zSelfCap = capability_string(pCap); |
| 374 | 374 | capability_free(pCap); |
| 375 | 375 | |
| 376 | 376 | db_prepare(&q, |
| 377 | 377 |
| --- src/capabilities.c | |
| +++ src/capabilities.c | |
| @@ -366,11 +366,11 @@ | |
| 366 | CapabilityString *pCap; |
| 367 | char *zSelfCap; |
| 368 | char *zPubPages = db_get("public-pages",0); |
| 369 | int hasPubPages = zPubPages && zPubPages[0]; |
| 370 | |
| 371 | pCap = capability_add(0, db_get("default-perms",0)); |
| 372 | capability_expand(pCap); |
| 373 | zSelfCap = capability_string(pCap); |
| 374 | capability_free(pCap); |
| 375 | |
| 376 | db_prepare(&q, |
| 377 |
| --- src/capabilities.c | |
| +++ src/capabilities.c | |
| @@ -366,11 +366,11 @@ | |
| 366 | CapabilityString *pCap; |
| 367 | char *zSelfCap; |
| 368 | char *zPubPages = db_get("public-pages",0); |
| 369 | int hasPubPages = zPubPages && zPubPages[0]; |
| 370 | |
| 371 | pCap = capability_add(0, db_get("default-perms","u")); |
| 372 | capability_expand(pCap); |
| 373 | zSelfCap = capability_string(pCap); |
| 374 | capability_free(pCap); |
| 375 | |
| 376 | db_prepare(&q, |
| 377 |
+54
-10
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -481,11 +481,11 @@ | ||
| 481 | 481 | int login_self_register_available(const char *zNeeded){ |
| 482 | 482 | CapabilityString *pCap; |
| 483 | 483 | int rc; |
| 484 | 484 | if( !db_get_boolean("self-register",0) ) return 0; |
| 485 | 485 | if( zNeeded==0 ) return 1; |
| 486 | - pCap = capability_add(0, db_get("default-perms", 0)); | |
| 486 | + pCap = capability_add(0, db_get("default-perms", "u")); | |
| 487 | 487 | capability_expand(pCap); |
| 488 | 488 | rc = capability_has_any(pCap, zNeeded); |
| 489 | 489 | capability_free(pCap); |
| 490 | 490 | return rc; |
| 491 | 491 | } |
| @@ -1128,11 +1128,11 @@ | ||
| 1128 | 1128 | if( zPublicPages!=0 ){ |
| 1129 | 1129 | Glob *pGlob = glob_create(zPublicPages); |
| 1130 | 1130 | const char *zUri = PD("REQUEST_URI",""); |
| 1131 | 1131 | zUri += (int)strlen(g.zTop); |
| 1132 | 1132 | if( glob_match(pGlob, zUri) ){ |
| 1133 | - login_set_capabilities(db_get("default-perms", 0), 0); | |
| 1133 | + login_set_capabilities(db_get("default-perms", "u"), 0); | |
| 1134 | 1134 | } |
| 1135 | 1135 | glob_free(pGlob); |
| 1136 | 1136 | } |
| 1137 | 1137 | } |
| 1138 | 1138 | |
| @@ -1459,10 +1459,38 @@ | ||
| 1459 | 1459 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1460 | 1460 | zUserID, zUserID, zUserID |
| 1461 | 1461 | ); |
| 1462 | 1462 | return rc; |
| 1463 | 1463 | } |
| 1464 | + | |
| 1465 | +/* | |
| 1466 | +** Check an email address and confirm that it is valid for self-registration. | |
| 1467 | +** The email address is known already to be well-formed. Return true | |
| 1468 | +** if the email address is on the allowed list. | |
| 1469 | +** | |
| 1470 | +** The default behavior is that any valid email address is accepted. | |
| 1471 | +** But if the "auth-sub-email" setting exists and is not empty, then | |
| 1472 | +** it is a comma-separated list of GLOB patterns for email addresses | |
| 1473 | +** that are authorized to self-register. | |
| 1474 | +*/ | |
| 1475 | +int authorized_subscription_email(const char *zEAddr){ | |
| 1476 | + char *zGlob = db_get("auth-sub-email",0); | |
| 1477 | + Glob *pGlob; | |
| 1478 | + char *zAddr; | |
| 1479 | + int rc; | |
| 1480 | + | |
| 1481 | + if( zGlob==0 || zGlob[0]==0 ) return 1; | |
| 1482 | + zGlob = fossil_strtolwr(fossil_strdup(zGlob)); | |
| 1483 | + pGlob = glob_create(zGlob); | |
| 1484 | + fossil_free(zGlob); | |
| 1485 | + | |
| 1486 | + zAddr = fossil_strtolwr(fossil_strdup(zEAddr)); | |
| 1487 | + rc = glob_match(pGlob, zAddr); | |
| 1488 | + fossil_free(zAddr); | |
| 1489 | + glob_free(pGlob); | |
| 1490 | + return rc!=0; | |
| 1491 | +} | |
| 1464 | 1492 | |
| 1465 | 1493 | /* |
| 1466 | 1494 | ** WEBPAGE: register |
| 1467 | 1495 | ** |
| 1468 | 1496 | ** Page to allow users to self-register. The "self-register" setting |
| @@ -1471,13 +1499,14 @@ | ||
| 1471 | 1499 | void register_page(void){ |
| 1472 | 1500 | const char *zUserID, *zPasswd, *zConfirm, *zEAddr; |
| 1473 | 1501 | const char *zDName; |
| 1474 | 1502 | unsigned int uSeed; |
| 1475 | 1503 | const char *zDecoded; |
| 1476 | - char *zCaptcha; | |
| 1477 | 1504 | int iErrLine = -1; |
| 1478 | 1505 | const char *zErr = 0; |
| 1506 | + int captchaIsCorrect = 0; /* True on a correct captcha */ | |
| 1507 | + char *zCaptcha = ""; /* Value of the captcha text */ | |
| 1479 | 1508 | char *zPerms; /* Permissions for the default user */ |
| 1480 | 1509 | int canDoAlerts = 0; /* True if receiving email alerts is possible */ |
| 1481 | 1510 | int doAlerts = 0; /* True if subscription is wanted too */ |
| 1482 | 1511 | if( !db_get_boolean("self-register", 0) ){ |
| 1483 | 1512 | style_header("Registration not possible"); |
| @@ -1484,11 +1513,11 @@ | ||
| 1484 | 1513 | @ <p>This project does not allow user self-registration. Please contact the |
| 1485 | 1514 | @ project administrator to obtain an account.</p> |
| 1486 | 1515 | style_footer(); |
| 1487 | 1516 | return; |
| 1488 | 1517 | } |
| 1489 | - zPerms = db_get("default-perms", 0); | |
| 1518 | + zPerms = db_get("default-perms", "u"); | |
| 1490 | 1519 | |
| 1491 | 1520 | /* Prompt the user for email alerts if this repository is configured for |
| 1492 | 1521 | ** email alerts and if the default permissions include "7" */ |
| 1493 | 1522 | canDoAlerts = alert_tables_exist() && db_int(0, |
| 1494 | 1523 | "SELECT fullcap(%Q) GLOB '*7*'", zPerms |
| @@ -1503,11 +1532,11 @@ | ||
| 1503 | 1532 | |
| 1504 | 1533 | /* Verify user imputs */ |
| 1505 | 1534 | if( P("new")==0 || !cgi_csrf_safe(1) ){ |
| 1506 | 1535 | /* This is not a valid form submission. Fall through into |
| 1507 | 1536 | ** the form display */ |
| 1508 | - }else if( !captcha_is_correct(1) ){ | |
| 1537 | + }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ | |
| 1509 | 1538 | iErrLine = 6; |
| 1510 | 1539 | zErr = "Incorrect CAPTCHA"; |
| 1511 | 1540 | }else if( strlen(zUserID)<6 ){ |
| 1512 | 1541 | iErrLine = 1; |
| 1513 | 1542 | zErr = "User ID too short. Must be at least 6 characters."; |
| @@ -1521,10 +1550,13 @@ | ||
| 1521 | 1550 | iErrLine = 3; |
| 1522 | 1551 | zErr = "Required"; |
| 1523 | 1552 | }else if( email_address_is_valid(zEAddr,0)==0 ){ |
| 1524 | 1553 | iErrLine = 3; |
| 1525 | 1554 | zErr = "Not a valid email address"; |
| 1555 | + }else if( authorized_subscription_email(zEAddr)==0 ){ | |
| 1556 | + iErrLine = 3; | |
| 1557 | + zErr = "Not an authorized email address"; | |
| 1526 | 1558 | }else if( strlen(zPasswd)<6 ){ |
| 1527 | 1559 | iErrLine = 4; |
| 1528 | 1560 | zErr = "Password must be at least 6 characters long"; |
| 1529 | 1561 | }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){ |
| 1530 | 1562 | iErrLine = 5; |
| @@ -1548,16 +1580,24 @@ | ||
| 1548 | 1580 | /* If all of the tests above have passed, that means that the submitted |
| 1549 | 1581 | ** form contains valid data and we can proceed to create the new login */ |
| 1550 | 1582 | Blob sql; |
| 1551 | 1583 | int uid; |
| 1552 | 1584 | char *zPass = sha1_shared_secret(zPasswd, zUserID, 0); |
| 1585 | + const char *zStartPerms = zPerms; | |
| 1586 | + if( db_get_boolean("selfreg-verify",0) ){ | |
| 1587 | + /* If email verification is required for self-registration, initalize | |
| 1588 | + ** the new user capabilities to just "7" (Sign up for email). The | |
| 1589 | + ** full "default-perms" permissions will be added when they click | |
| 1590 | + ** the verification link on the email they are sent. */ | |
| 1591 | + zStartPerms = "7"; | |
| 1592 | + } | |
| 1553 | 1593 | blob_init(&sql, 0, 0); |
| 1554 | 1594 | blob_append_sql(&sql, |
| 1555 | 1595 | "INSERT INTO user(login,pw,cap,info,mtime)\n" |
| 1556 | 1596 | "VALUES(%Q,%Q,%Q," |
| 1557 | 1597 | "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", |
| 1558 | - zUserID, zPass, zPerms, zDName, zEAddr, g.zIpAddr); | |
| 1598 | + zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr); | |
| 1559 | 1599 | fossil_free(zPass); |
| 1560 | 1600 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 1561 | 1601 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); |
| 1562 | 1602 | login_set_user_cookie(zUserID, uid, NULL); |
| 1563 | 1603 | if( doAlerts ){ |
| @@ -1620,12 +1660,11 @@ | ||
| 1620 | 1660 | @ <blockquote><pre> |
| 1621 | 1661 | @ %h(pSender->zErr) |
| 1622 | 1662 | @ </pre></blockquote> |
| 1623 | 1663 | }else{ |
| 1624 | 1664 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1625 | - @ hyperlink that you must click on in order to activate your | |
| 1626 | - @ subscription.</p> | |
| 1665 | + @ hyperlink that you must click to activate your account.</p> | |
| 1627 | 1666 | } |
| 1628 | 1667 | alert_sender_free(pSender); |
| 1629 | 1668 | if( zGoto ){ |
| 1630 | 1669 | @ <p><a href='%h(zGoto)'>Continue</a> |
| 1631 | 1670 | } |
| @@ -1634,11 +1673,15 @@ | ||
| 1634 | 1673 | } |
| 1635 | 1674 | redirect_to_g(); |
| 1636 | 1675 | } |
| 1637 | 1676 | |
| 1638 | 1677 | /* Prepare the captcha. */ |
| 1639 | - uSeed = captcha_seed(); | |
| 1678 | + if( captchaIsCorrect ){ | |
| 1679 | + uSeed = strtoul(P("captchaseed"),0,10); | |
| 1680 | + }else{ | |
| 1681 | + uSeed = captcha_seed(); | |
| 1682 | + } | |
| 1640 | 1683 | zDecoded = captcha_decode(uSeed); |
| 1641 | 1684 | zCaptcha = captcha_render(zDecoded); |
| 1642 | 1685 | |
| 1643 | 1686 | style_header("Register"); |
| 1644 | 1687 | /* Print out the registration form. */ |
| @@ -1693,11 +1736,12 @@ | ||
| 1693 | 1736 | if( iErrLine==5 ){ |
| 1694 | 1737 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1695 | 1738 | } |
| 1696 | 1739 | @ <tr> |
| 1697 | 1740 | @ <td class="form_label" align="right">Captcha:</td> |
| 1698 | - @ <td><input type="text" name="captcha" value="" size="30"> | |
| 1741 | + @ <td><input type="text" name="captcha" size="30"\ | |
| 1742 | + @ value="%h(captchaIsCorrect?zDecoded:"")" size="30"> | |
| 1699 | 1743 | captcha_speakit_button(uSeed, "Speak the captcha text"); |
| 1700 | 1744 | @ </td> |
| 1701 | 1745 | @ </tr> |
| 1702 | 1746 | if( iErrLine==6 ){ |
| 1703 | 1747 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1704 | 1748 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -481,11 +481,11 @@ | |
| 481 | int login_self_register_available(const char *zNeeded){ |
| 482 | CapabilityString *pCap; |
| 483 | int rc; |
| 484 | if( !db_get_boolean("self-register",0) ) return 0; |
| 485 | if( zNeeded==0 ) return 1; |
| 486 | pCap = capability_add(0, db_get("default-perms", 0)); |
| 487 | capability_expand(pCap); |
| 488 | rc = capability_has_any(pCap, zNeeded); |
| 489 | capability_free(pCap); |
| 490 | return rc; |
| 491 | } |
| @@ -1128,11 +1128,11 @@ | |
| 1128 | if( zPublicPages!=0 ){ |
| 1129 | Glob *pGlob = glob_create(zPublicPages); |
| 1130 | const char *zUri = PD("REQUEST_URI",""); |
| 1131 | zUri += (int)strlen(g.zTop); |
| 1132 | if( glob_match(pGlob, zUri) ){ |
| 1133 | login_set_capabilities(db_get("default-perms", 0), 0); |
| 1134 | } |
| 1135 | glob_free(pGlob); |
| 1136 | } |
| 1137 | } |
| 1138 | |
| @@ -1459,10 +1459,38 @@ | |
| 1459 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1460 | zUserID, zUserID, zUserID |
| 1461 | ); |
| 1462 | return rc; |
| 1463 | } |
| 1464 | |
| 1465 | /* |
| 1466 | ** WEBPAGE: register |
| 1467 | ** |
| 1468 | ** Page to allow users to self-register. The "self-register" setting |
| @@ -1471,13 +1499,14 @@ | |
| 1471 | void register_page(void){ |
| 1472 | const char *zUserID, *zPasswd, *zConfirm, *zEAddr; |
| 1473 | const char *zDName; |
| 1474 | unsigned int uSeed; |
| 1475 | const char *zDecoded; |
| 1476 | char *zCaptcha; |
| 1477 | int iErrLine = -1; |
| 1478 | const char *zErr = 0; |
| 1479 | char *zPerms; /* Permissions for the default user */ |
| 1480 | int canDoAlerts = 0; /* True if receiving email alerts is possible */ |
| 1481 | int doAlerts = 0; /* True if subscription is wanted too */ |
| 1482 | if( !db_get_boolean("self-register", 0) ){ |
| 1483 | style_header("Registration not possible"); |
| @@ -1484,11 +1513,11 @@ | |
| 1484 | @ <p>This project does not allow user self-registration. Please contact the |
| 1485 | @ project administrator to obtain an account.</p> |
| 1486 | style_footer(); |
| 1487 | return; |
| 1488 | } |
| 1489 | zPerms = db_get("default-perms", 0); |
| 1490 | |
| 1491 | /* Prompt the user for email alerts if this repository is configured for |
| 1492 | ** email alerts and if the default permissions include "7" */ |
| 1493 | canDoAlerts = alert_tables_exist() && db_int(0, |
| 1494 | "SELECT fullcap(%Q) GLOB '*7*'", zPerms |
| @@ -1503,11 +1532,11 @@ | |
| 1503 | |
| 1504 | /* Verify user imputs */ |
| 1505 | if( P("new")==0 || !cgi_csrf_safe(1) ){ |
| 1506 | /* This is not a valid form submission. Fall through into |
| 1507 | ** the form display */ |
| 1508 | }else if( !captcha_is_correct(1) ){ |
| 1509 | iErrLine = 6; |
| 1510 | zErr = "Incorrect CAPTCHA"; |
| 1511 | }else if( strlen(zUserID)<6 ){ |
| 1512 | iErrLine = 1; |
| 1513 | zErr = "User ID too short. Must be at least 6 characters."; |
| @@ -1521,10 +1550,13 @@ | |
| 1521 | iErrLine = 3; |
| 1522 | zErr = "Required"; |
| 1523 | }else if( email_address_is_valid(zEAddr,0)==0 ){ |
| 1524 | iErrLine = 3; |
| 1525 | zErr = "Not a valid email address"; |
| 1526 | }else if( strlen(zPasswd)<6 ){ |
| 1527 | iErrLine = 4; |
| 1528 | zErr = "Password must be at least 6 characters long"; |
| 1529 | }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){ |
| 1530 | iErrLine = 5; |
| @@ -1548,16 +1580,24 @@ | |
| 1548 | /* If all of the tests above have passed, that means that the submitted |
| 1549 | ** form contains valid data and we can proceed to create the new login */ |
| 1550 | Blob sql; |
| 1551 | int uid; |
| 1552 | char *zPass = sha1_shared_secret(zPasswd, zUserID, 0); |
| 1553 | blob_init(&sql, 0, 0); |
| 1554 | blob_append_sql(&sql, |
| 1555 | "INSERT INTO user(login,pw,cap,info,mtime)\n" |
| 1556 | "VALUES(%Q,%Q,%Q," |
| 1557 | "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", |
| 1558 | zUserID, zPass, zPerms, zDName, zEAddr, g.zIpAddr); |
| 1559 | fossil_free(zPass); |
| 1560 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 1561 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); |
| 1562 | login_set_user_cookie(zUserID, uid, NULL); |
| 1563 | if( doAlerts ){ |
| @@ -1620,12 +1660,11 @@ | |
| 1620 | @ <blockquote><pre> |
| 1621 | @ %h(pSender->zErr) |
| 1622 | @ </pre></blockquote> |
| 1623 | }else{ |
| 1624 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1625 | @ hyperlink that you must click on in order to activate your |
| 1626 | @ subscription.</p> |
| 1627 | } |
| 1628 | alert_sender_free(pSender); |
| 1629 | if( zGoto ){ |
| 1630 | @ <p><a href='%h(zGoto)'>Continue</a> |
| 1631 | } |
| @@ -1634,11 +1673,15 @@ | |
| 1634 | } |
| 1635 | redirect_to_g(); |
| 1636 | } |
| 1637 | |
| 1638 | /* Prepare the captcha. */ |
| 1639 | uSeed = captcha_seed(); |
| 1640 | zDecoded = captcha_decode(uSeed); |
| 1641 | zCaptcha = captcha_render(zDecoded); |
| 1642 | |
| 1643 | style_header("Register"); |
| 1644 | /* Print out the registration form. */ |
| @@ -1693,11 +1736,12 @@ | |
| 1693 | if( iErrLine==5 ){ |
| 1694 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1695 | } |
| 1696 | @ <tr> |
| 1697 | @ <td class="form_label" align="right">Captcha:</td> |
| 1698 | @ <td><input type="text" name="captcha" value="" size="30"> |
| 1699 | captcha_speakit_button(uSeed, "Speak the captcha text"); |
| 1700 | @ </td> |
| 1701 | @ </tr> |
| 1702 | if( iErrLine==6 ){ |
| 1703 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1704 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -481,11 +481,11 @@ | |
| 481 | int login_self_register_available(const char *zNeeded){ |
| 482 | CapabilityString *pCap; |
| 483 | int rc; |
| 484 | if( !db_get_boolean("self-register",0) ) return 0; |
| 485 | if( zNeeded==0 ) return 1; |
| 486 | pCap = capability_add(0, db_get("default-perms", "u")); |
| 487 | capability_expand(pCap); |
| 488 | rc = capability_has_any(pCap, zNeeded); |
| 489 | capability_free(pCap); |
| 490 | return rc; |
| 491 | } |
| @@ -1128,11 +1128,11 @@ | |
| 1128 | if( zPublicPages!=0 ){ |
| 1129 | Glob *pGlob = glob_create(zPublicPages); |
| 1130 | const char *zUri = PD("REQUEST_URI",""); |
| 1131 | zUri += (int)strlen(g.zTop); |
| 1132 | if( glob_match(pGlob, zUri) ){ |
| 1133 | login_set_capabilities(db_get("default-perms", "u"), 0); |
| 1134 | } |
| 1135 | glob_free(pGlob); |
| 1136 | } |
| 1137 | } |
| 1138 | |
| @@ -1459,10 +1459,38 @@ | |
| 1459 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1460 | zUserID, zUserID, zUserID |
| 1461 | ); |
| 1462 | return rc; |
| 1463 | } |
| 1464 | |
| 1465 | /* |
| 1466 | ** Check an email address and confirm that it is valid for self-registration. |
| 1467 | ** The email address is known already to be well-formed. Return true |
| 1468 | ** if the email address is on the allowed list. |
| 1469 | ** |
| 1470 | ** The default behavior is that any valid email address is accepted. |
| 1471 | ** But if the "auth-sub-email" setting exists and is not empty, then |
| 1472 | ** it is a comma-separated list of GLOB patterns for email addresses |
| 1473 | ** that are authorized to self-register. |
| 1474 | */ |
| 1475 | int authorized_subscription_email(const char *zEAddr){ |
| 1476 | char *zGlob = db_get("auth-sub-email",0); |
| 1477 | Glob *pGlob; |
| 1478 | char *zAddr; |
| 1479 | int rc; |
| 1480 | |
| 1481 | if( zGlob==0 || zGlob[0]==0 ) return 1; |
| 1482 | zGlob = fossil_strtolwr(fossil_strdup(zGlob)); |
| 1483 | pGlob = glob_create(zGlob); |
| 1484 | fossil_free(zGlob); |
| 1485 | |
| 1486 | zAddr = fossil_strtolwr(fossil_strdup(zEAddr)); |
| 1487 | rc = glob_match(pGlob, zAddr); |
| 1488 | fossil_free(zAddr); |
| 1489 | glob_free(pGlob); |
| 1490 | return rc!=0; |
| 1491 | } |
| 1492 | |
| 1493 | /* |
| 1494 | ** WEBPAGE: register |
| 1495 | ** |
| 1496 | ** Page to allow users to self-register. The "self-register" setting |
| @@ -1471,13 +1499,14 @@ | |
| 1499 | void register_page(void){ |
| 1500 | const char *zUserID, *zPasswd, *zConfirm, *zEAddr; |
| 1501 | const char *zDName; |
| 1502 | unsigned int uSeed; |
| 1503 | const char *zDecoded; |
| 1504 | int iErrLine = -1; |
| 1505 | const char *zErr = 0; |
| 1506 | int captchaIsCorrect = 0; /* True on a correct captcha */ |
| 1507 | char *zCaptcha = ""; /* Value of the captcha text */ |
| 1508 | char *zPerms; /* Permissions for the default user */ |
| 1509 | int canDoAlerts = 0; /* True if receiving email alerts is possible */ |
| 1510 | int doAlerts = 0; /* True if subscription is wanted too */ |
| 1511 | if( !db_get_boolean("self-register", 0) ){ |
| 1512 | style_header("Registration not possible"); |
| @@ -1484,11 +1513,11 @@ | |
| 1513 | @ <p>This project does not allow user self-registration. Please contact the |
| 1514 | @ project administrator to obtain an account.</p> |
| 1515 | style_footer(); |
| 1516 | return; |
| 1517 | } |
| 1518 | zPerms = db_get("default-perms", "u"); |
| 1519 | |
| 1520 | /* Prompt the user for email alerts if this repository is configured for |
| 1521 | ** email alerts and if the default permissions include "7" */ |
| 1522 | canDoAlerts = alert_tables_exist() && db_int(0, |
| 1523 | "SELECT fullcap(%Q) GLOB '*7*'", zPerms |
| @@ -1503,11 +1532,11 @@ | |
| 1532 | |
| 1533 | /* Verify user imputs */ |
| 1534 | if( P("new")==0 || !cgi_csrf_safe(1) ){ |
| 1535 | /* This is not a valid form submission. Fall through into |
| 1536 | ** the form display */ |
| 1537 | }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){ |
| 1538 | iErrLine = 6; |
| 1539 | zErr = "Incorrect CAPTCHA"; |
| 1540 | }else if( strlen(zUserID)<6 ){ |
| 1541 | iErrLine = 1; |
| 1542 | zErr = "User ID too short. Must be at least 6 characters."; |
| @@ -1521,10 +1550,13 @@ | |
| 1550 | iErrLine = 3; |
| 1551 | zErr = "Required"; |
| 1552 | }else if( email_address_is_valid(zEAddr,0)==0 ){ |
| 1553 | iErrLine = 3; |
| 1554 | zErr = "Not a valid email address"; |
| 1555 | }else if( authorized_subscription_email(zEAddr)==0 ){ |
| 1556 | iErrLine = 3; |
| 1557 | zErr = "Not an authorized email address"; |
| 1558 | }else if( strlen(zPasswd)<6 ){ |
| 1559 | iErrLine = 4; |
| 1560 | zErr = "Password must be at least 6 characters long"; |
| 1561 | }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){ |
| 1562 | iErrLine = 5; |
| @@ -1548,16 +1580,24 @@ | |
| 1580 | /* If all of the tests above have passed, that means that the submitted |
| 1581 | ** form contains valid data and we can proceed to create the new login */ |
| 1582 | Blob sql; |
| 1583 | int uid; |
| 1584 | char *zPass = sha1_shared_secret(zPasswd, zUserID, 0); |
| 1585 | const char *zStartPerms = zPerms; |
| 1586 | if( db_get_boolean("selfreg-verify",0) ){ |
| 1587 | /* If email verification is required for self-registration, initalize |
| 1588 | ** the new user capabilities to just "7" (Sign up for email). The |
| 1589 | ** full "default-perms" permissions will be added when they click |
| 1590 | ** the verification link on the email they are sent. */ |
| 1591 | zStartPerms = "7"; |
| 1592 | } |
| 1593 | blob_init(&sql, 0, 0); |
| 1594 | blob_append_sql(&sql, |
| 1595 | "INSERT INTO user(login,pw,cap,info,mtime)\n" |
| 1596 | "VALUES(%Q,%Q,%Q," |
| 1597 | "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())", |
| 1598 | zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr); |
| 1599 | fossil_free(zPass); |
| 1600 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 1601 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID); |
| 1602 | login_set_user_cookie(zUserID, uid, NULL); |
| 1603 | if( doAlerts ){ |
| @@ -1620,12 +1660,11 @@ | |
| 1660 | @ <blockquote><pre> |
| 1661 | @ %h(pSender->zErr) |
| 1662 | @ </pre></blockquote> |
| 1663 | }else{ |
| 1664 | @ <p>An email has been sent to "%h(zEAddr)". That email contains a |
| 1665 | @ hyperlink that you must click to activate your account.</p> |
| 1666 | } |
| 1667 | alert_sender_free(pSender); |
| 1668 | if( zGoto ){ |
| 1669 | @ <p><a href='%h(zGoto)'>Continue</a> |
| 1670 | } |
| @@ -1634,11 +1673,15 @@ | |
| 1673 | } |
| 1674 | redirect_to_g(); |
| 1675 | } |
| 1676 | |
| 1677 | /* Prepare the captcha. */ |
| 1678 | if( captchaIsCorrect ){ |
| 1679 | uSeed = strtoul(P("captchaseed"),0,10); |
| 1680 | }else{ |
| 1681 | uSeed = captcha_seed(); |
| 1682 | } |
| 1683 | zDecoded = captcha_decode(uSeed); |
| 1684 | zCaptcha = captcha_render(zDecoded); |
| 1685 | |
| 1686 | style_header("Register"); |
| 1687 | /* Print out the registration form. */ |
| @@ -1693,11 +1736,12 @@ | |
| 1736 | if( iErrLine==5 ){ |
| 1737 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1738 | } |
| 1739 | @ <tr> |
| 1740 | @ <td class="form_label" align="right">Captcha:</td> |
| 1741 | @ <td><input type="text" name="captcha" size="30"\ |
| 1742 | @ value="%h(captchaIsCorrect?zDecoded:"")" size="30"> |
| 1743 | captcha_speakit_button(uSeed, "Speak the captcha text"); |
| 1744 | @ </td> |
| 1745 | @ </tr> |
| 1746 | if( iErrLine==6 ){ |
| 1747 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr> |
| 1748 |
+8
-2
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -2523,18 +2523,24 @@ | ||
| 2523 | 2523 | ** |
| 2524 | 2524 | ** Works like the http command but gives setup permission to all users. |
| 2525 | 2525 | ** |
| 2526 | 2526 | ** Options: |
| 2527 | 2527 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2528 | +** --usercap CAP user capability string. (Default: "sx") | |
| 2528 | 2529 | ** |
| 2529 | 2530 | */ |
| 2530 | 2531 | void cmd_test_http(void){ |
| 2531 | 2532 | const char *zIpAddr; /* IP address of remote client */ |
| 2533 | + const char *zUserCap; | |
| 2532 | 2534 | |
| 2533 | 2535 | Th_InitTraceLog(); |
| 2534 | - login_set_capabilities("sx", 0); | |
| 2535 | - g.useLocalauth = 1; | |
| 2536 | + zUserCap = find_option("usercap",0,1); | |
| 2537 | + if( zUserCap==0 ){ | |
| 2538 | + g.useLocalauth = 1; | |
| 2539 | + zUserCap = "sx"; | |
| 2540 | + } | |
| 2541 | + login_set_capabilities(zUserCap, 0); | |
| 2536 | 2542 | g.httpIn = stdin; |
| 2537 | 2543 | g.httpOut = stdout; |
| 2538 | 2544 | fossil_binary_mode(g.httpOut); |
| 2539 | 2545 | fossil_binary_mode(g.httpIn); |
| 2540 | 2546 | g.zExtRoot = find_option("extroot",0,1); |
| 2541 | 2547 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -2523,18 +2523,24 @@ | |
| 2523 | ** |
| 2524 | ** Works like the http command but gives setup permission to all users. |
| 2525 | ** |
| 2526 | ** Options: |
| 2527 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2528 | ** |
| 2529 | */ |
| 2530 | void cmd_test_http(void){ |
| 2531 | const char *zIpAddr; /* IP address of remote client */ |
| 2532 | |
| 2533 | Th_InitTraceLog(); |
| 2534 | login_set_capabilities("sx", 0); |
| 2535 | g.useLocalauth = 1; |
| 2536 | g.httpIn = stdin; |
| 2537 | g.httpOut = stdout; |
| 2538 | fossil_binary_mode(g.httpOut); |
| 2539 | fossil_binary_mode(g.httpIn); |
| 2540 | g.zExtRoot = find_option("extroot",0,1); |
| 2541 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -2523,18 +2523,24 @@ | |
| 2523 | ** |
| 2524 | ** Works like the http command but gives setup permission to all users. |
| 2525 | ** |
| 2526 | ** Options: |
| 2527 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 2528 | ** --usercap CAP user capability string. (Default: "sx") |
| 2529 | ** |
| 2530 | */ |
| 2531 | void cmd_test_http(void){ |
| 2532 | const char *zIpAddr; /* IP address of remote client */ |
| 2533 | const char *zUserCap; |
| 2534 | |
| 2535 | Th_InitTraceLog(); |
| 2536 | zUserCap = find_option("usercap",0,1); |
| 2537 | if( zUserCap==0 ){ |
| 2538 | g.useLocalauth = 1; |
| 2539 | zUserCap = "sx"; |
| 2540 | } |
| 2541 | login_set_capabilities(zUserCap, 0); |
| 2542 | g.httpIn = stdin; |
| 2543 | g.httpOut = stdout; |
| 2544 | fossil_binary_mode(g.httpOut); |
| 2545 | fossil_binary_mode(g.httpIn); |
| 2546 | g.zExtRoot = find_option("extroot",0,1); |
| 2547 |
+1
-1
| --- src/security_audit.c | ||
| +++ src/security_audit.c | ||
| @@ -122,11 +122,11 @@ | ||
| 122 | 122 | zAnonCap = db_text("", "SELECT fullcap(NULL)"); |
| 123 | 123 | zDevCap = db_text("", "SELECT fullcap('v')"); |
| 124 | 124 | zReadCap = db_text("", "SELECT fullcap('u')"); |
| 125 | 125 | zPubPages = db_get("public-pages",0); |
| 126 | 126 | hasSelfReg = db_get_boolean("self-register",0); |
| 127 | - pCap = capability_add(0, db_get("default-perms",0)); | |
| 127 | + pCap = capability_add(0, db_get("default-perms","u")); | |
| 128 | 128 | capability_expand(pCap); |
| 129 | 129 | zSelfCap = capability_string(pCap); |
| 130 | 130 | capability_free(pCap); |
| 131 | 131 | if( hasAnyCap(zAnonCap,"as") ){ |
| 132 | 132 | @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because |
| 133 | 133 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -122,11 +122,11 @@ | |
| 122 | zAnonCap = db_text("", "SELECT fullcap(NULL)"); |
| 123 | zDevCap = db_text("", "SELECT fullcap('v')"); |
| 124 | zReadCap = db_text("", "SELECT fullcap('u')"); |
| 125 | zPubPages = db_get("public-pages",0); |
| 126 | hasSelfReg = db_get_boolean("self-register",0); |
| 127 | pCap = capability_add(0, db_get("default-perms",0)); |
| 128 | capability_expand(pCap); |
| 129 | zSelfCap = capability_string(pCap); |
| 130 | capability_free(pCap); |
| 131 | if( hasAnyCap(zAnonCap,"as") ){ |
| 132 | @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because |
| 133 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -122,11 +122,11 @@ | |
| 122 | zAnonCap = db_text("", "SELECT fullcap(NULL)"); |
| 123 | zDevCap = db_text("", "SELECT fullcap('v')"); |
| 124 | zReadCap = db_text("", "SELECT fullcap('u')"); |
| 125 | zPubPages = db_get("public-pages",0); |
| 126 | hasSelfReg = db_get_boolean("self-register",0); |
| 127 | pCap = capability_add(0, db_get("default-perms","u")); |
| 128 | capability_expand(pCap); |
| 129 | zSelfCap = capability_string(pCap); |
| 130 | capability_free(pCap); |
| 131 | if( hasAnyCap(zAnonCap,"as") ){ |
| 132 | @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because |
| 133 |
+34
-7
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -500,17 +500,44 @@ | ||
| 500 | 500 | @ (Property: "public-pages") |
| 501 | 501 | @ </p> |
| 502 | 502 | |
| 503 | 503 | @ <hr /> |
| 504 | 504 | onoff_attribute("Allow users to register themselves", |
| 505 | - "self-register", "selfregister", 0, 0); | |
| 506 | - @ <p>Allow users to register themselves through the HTTP UI. | |
| 507 | - @ The registration form always requires filling in a CAPTCHA | |
| 508 | - @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone | |
| 509 | - @ can register under any user name. This option is useful for public projects | |
| 510 | - @ where you do not want everyone in any ticket discussion to be named | |
| 511 | - @ "Anonymous". (Property: "self-register")</p> | |
| 505 | + "self-register", "selfreg", 0, 0); | |
| 506 | + @ <p>Allow users to register themselves on the /register webpage. | |
| 507 | + @ A self-registration creates a new entry in the USER table and | |
| 508 | + @ perhaps also in the SUBSCRIBER table if email notification is | |
| 509 | + @ enabled. | |
| 510 | + @ (Property: "self-register")</p> | |
| 511 | + | |
| 512 | + @ <hr /> | |
| 513 | + onoff_attribute("Email verification required for self-registration", | |
| 514 | + "selfreg-verify", "sfverify", 0, 0); | |
| 515 | + @ <p>If enabled, self-registration creates a new entry in the USER table | |
| 516 | + @ with only capabilities "7". The default user capabilities are not | |
| 517 | + @ added until the email address associated with the self-registration | |
| 518 | + @ has been verified. This setting only makes sense if | |
| 519 | + @ email notifications are enabled. | |
| 520 | + @ (Property: "selfreg-verify")</p> | |
| 521 | + | |
| 522 | + @ <hr /> | |
| 523 | + onoff_attribute("Allow anonymous subscriptions", | |
| 524 | + "anon-subscribe", "anonsub", 1, 0); | |
| 525 | + @ <p>If disabled, email notification subscriptions are only allowed | |
| 526 | + @ for users with a login. If Nobody or Anonymous visit the /subscribe | |
| 527 | + @ page, they are redirected to /register or /login. | |
| 528 | + @ (Property: "anon-subscribe")</p> | |
| 529 | + | |
| 530 | + @ <hr /> | |
| 531 | + entry_attribute("Authorized subscription email addresses", 35, | |
| 532 | + "auth-sub-email", "asemail", "", 0); | |
| 533 | + @ <p>This is a comma-separated list of GLOB patterns that specify | |
| 534 | + @ email addresses that are authorized to subscriptions. If blank | |
| 535 | + @ (the usual case), then any email address can be used to self-register. | |
| 536 | + @ This setting is used to limit subscriptions to members of a particular | |
| 537 | + @ organization or group based on their email address. | |
| 538 | + @ (Property: "auth-sub-email")</p> | |
| 512 | 539 | |
| 513 | 540 | @ <hr /> |
| 514 | 541 | entry_attribute("Default privileges", 10, "default-perms", |
| 515 | 542 | "defaultperms", "u", 0); |
| 516 | 543 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 517 | 544 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -500,17 +500,44 @@ | |
| 500 | @ (Property: "public-pages") |
| 501 | @ </p> |
| 502 | |
| 503 | @ <hr /> |
| 504 | onoff_attribute("Allow users to register themselves", |
| 505 | "self-register", "selfregister", 0, 0); |
| 506 | @ <p>Allow users to register themselves through the HTTP UI. |
| 507 | @ The registration form always requires filling in a CAPTCHA |
| 508 | @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone |
| 509 | @ can register under any user name. This option is useful for public projects |
| 510 | @ where you do not want everyone in any ticket discussion to be named |
| 511 | @ "Anonymous". (Property: "self-register")</p> |
| 512 | |
| 513 | @ <hr /> |
| 514 | entry_attribute("Default privileges", 10, "default-perms", |
| 515 | "defaultperms", "u", 0); |
| 516 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 517 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -500,17 +500,44 @@ | |
| 500 | @ (Property: "public-pages") |
| 501 | @ </p> |
| 502 | |
| 503 | @ <hr /> |
| 504 | onoff_attribute("Allow users to register themselves", |
| 505 | "self-register", "selfreg", 0, 0); |
| 506 | @ <p>Allow users to register themselves on the /register webpage. |
| 507 | @ A self-registration creates a new entry in the USER table and |
| 508 | @ perhaps also in the SUBSCRIBER table if email notification is |
| 509 | @ enabled. |
| 510 | @ (Property: "self-register")</p> |
| 511 | |
| 512 | @ <hr /> |
| 513 | onoff_attribute("Email verification required for self-registration", |
| 514 | "selfreg-verify", "sfverify", 0, 0); |
| 515 | @ <p>If enabled, self-registration creates a new entry in the USER table |
| 516 | @ with only capabilities "7". The default user capabilities are not |
| 517 | @ added until the email address associated with the self-registration |
| 518 | @ has been verified. This setting only makes sense if |
| 519 | @ email notifications are enabled. |
| 520 | @ (Property: "selfreg-verify")</p> |
| 521 | |
| 522 | @ <hr /> |
| 523 | onoff_attribute("Allow anonymous subscriptions", |
| 524 | "anon-subscribe", "anonsub", 1, 0); |
| 525 | @ <p>If disabled, email notification subscriptions are only allowed |
| 526 | @ for users with a login. If Nobody or Anonymous visit the /subscribe |
| 527 | @ page, they are redirected to /register or /login. |
| 528 | @ (Property: "anon-subscribe")</p> |
| 529 | |
| 530 | @ <hr /> |
| 531 | entry_attribute("Authorized subscription email addresses", 35, |
| 532 | "auth-sub-email", "asemail", "", 0); |
| 533 | @ <p>This is a comma-separated list of GLOB patterns that specify |
| 534 | @ email addresses that are authorized to subscriptions. If blank |
| 535 | @ (the usual case), then any email address can be used to self-register. |
| 536 | @ This setting is used to limit subscriptions to members of a particular |
| 537 | @ organization or group based on their email address. |
| 538 | @ (Property: "auth-sub-email")</p> |
| 539 | |
| 540 | @ <hr /> |
| 541 | entry_attribute("Default privileges", 10, "default-perms", |
| 542 | "defaultperms", "u", 0); |
| 543 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 544 |
+1
-1
| --- src/setupuser.c | ||
| +++ src/setupuser.c | ||
| @@ -553,11 +553,11 @@ | ||
| 553 | 553 | if( alert_tables_exist() ){ |
| 554 | 554 | int sid; |
| 555 | 555 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 556 | 556 | " WHERE suname=%Q", zLogin); |
| 557 | 557 | if( sid>0 ){ |
| 558 | - @ <a href="%R/alerts?sid=%d(sid)>\ | |
| 558 | + @ <a href="%R/alerts?sid=%d(sid)">\ | |
| 559 | 559 | @ (subscription info for %h(zLogin))</a>\ |
| 560 | 560 | } |
| 561 | 561 | } |
| 562 | 562 | @ </td></tr> |
| 563 | 563 | @ <tr> |
| 564 | 564 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -553,11 +553,11 @@ | |
| 553 | if( alert_tables_exist() ){ |
| 554 | int sid; |
| 555 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 556 | " WHERE suname=%Q", zLogin); |
| 557 | if( sid>0 ){ |
| 558 | @ <a href="%R/alerts?sid=%d(sid)>\ |
| 559 | @ (subscription info for %h(zLogin))</a>\ |
| 560 | } |
| 561 | } |
| 562 | @ </td></tr> |
| 563 | @ <tr> |
| 564 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -553,11 +553,11 @@ | |
| 553 | if( alert_tables_exist() ){ |
| 554 | int sid; |
| 555 | sid = db_int(0, "SELECT subscriberId FROM subscriber" |
| 556 | " WHERE suname=%Q", zLogin); |
| 557 | if( sid>0 ){ |
| 558 | @ <a href="%R/alerts?sid=%d(sid)">\ |
| 559 | @ (subscription info for %h(zLogin))</a>\ |
| 560 | } |
| 561 | } |
| 562 | @ </td></tr> |
| 563 | @ <tr> |
| 564 |
+1
-11
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -66,20 +66,10 @@ | ||
| 66 | 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | 67 | }; |
| 68 | 68 | #endif /* INTERFACE */ |
| 69 | 69 | |
| 70 | 70 | |
| 71 | -/* | |
| 72 | -** Convert a string to lower-case. | |
| 73 | -*/ | |
| 74 | -static void url_tolower(char *z){ | |
| 75 | - while( *z ){ | |
| 76 | - *z = fossil_tolower(*z); | |
| 77 | - z++; | |
| 78 | - } | |
| 79 | -} | |
| 80 | - | |
| 81 | 71 | /* |
| 82 | 72 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 83 | 73 | ** as follows: |
| 84 | 74 | ** |
| 85 | 75 | ** isFile True if FILE: |
| @@ -177,11 +167,11 @@ | ||
| 177 | 167 | pUrlData->name++; |
| 178 | 168 | pUrlData->name[n-2] = 0; |
| 179 | 169 | } |
| 180 | 170 | zLogin = mprintf(""); |
| 181 | 171 | } |
| 182 | - url_tolower(pUrlData->name); | |
| 172 | + fossil_strtolwr(pUrlData->name); | |
| 183 | 173 | if( c==':' ){ |
| 184 | 174 | pUrlData->port = 0; |
| 185 | 175 | i++; |
| 186 | 176 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 187 | 177 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| 188 | 178 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -66,20 +66,10 @@ | |
| 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | }; |
| 68 | #endif /* INTERFACE */ |
| 69 | |
| 70 | |
| 71 | /* |
| 72 | ** Convert a string to lower-case. |
| 73 | */ |
| 74 | static void url_tolower(char *z){ |
| 75 | while( *z ){ |
| 76 | *z = fossil_tolower(*z); |
| 77 | z++; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 83 | ** as follows: |
| 84 | ** |
| 85 | ** isFile True if FILE: |
| @@ -177,11 +167,11 @@ | |
| 177 | pUrlData->name++; |
| 178 | pUrlData->name[n-2] = 0; |
| 179 | } |
| 180 | zLogin = mprintf(""); |
| 181 | } |
| 182 | url_tolower(pUrlData->name); |
| 183 | if( c==':' ){ |
| 184 | pUrlData->port = 0; |
| 185 | i++; |
| 186 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 187 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| 188 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -66,20 +66,10 @@ | |
| 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | }; |
| 68 | #endif /* INTERFACE */ |
| 69 | |
| 70 | |
| 71 | /* |
| 72 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 73 | ** as follows: |
| 74 | ** |
| 75 | ** isFile True if FILE: |
| @@ -177,11 +167,11 @@ | |
| 167 | pUrlData->name++; |
| 168 | pUrlData->name[n-2] = 0; |
| 169 | } |
| 170 | zLogin = mprintf(""); |
| 171 | } |
| 172 | fossil_strtolwr(pUrlData->name); |
| 173 | if( c==':' ){ |
| 174 | pUrlData->port = 0; |
| 175 | i++; |
| 176 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 177 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| 178 |
+15
| --- src/util.c | ||
| +++ src/util.c | ||
| @@ -140,10 +140,25 @@ | ||
| 140 | 140 | } |
| 141 | 141 | #else |
| 142 | 142 | fossil_free(p); |
| 143 | 143 | #endif |
| 144 | 144 | } |
| 145 | + | |
| 146 | +/* | |
| 147 | +** Translate every upper-case character in the input string into | |
| 148 | +** its equivalent lower-case. | |
| 149 | +*/ | |
| 150 | +char *fossil_strtolwr(char *zIn){ | |
| 151 | + char *zStart = zIn; | |
| 152 | + if( zIn ){ | |
| 153 | + while( *zIn ){ | |
| 154 | + *zIn = fossil_tolower(*zIn); | |
| 155 | + zIn++; | |
| 156 | + } | |
| 157 | + } | |
| 158 | + return zStart; | |
| 159 | +} | |
| 145 | 160 | |
| 146 | 161 | /* |
| 147 | 162 | ** This function implements a cross-platform "system()" interface. |
| 148 | 163 | */ |
| 149 | 164 | int fossil_system(const char *zOrigCmd){ |
| 150 | 165 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -140,10 +140,25 @@ | |
| 140 | } |
| 141 | #else |
| 142 | fossil_free(p); |
| 143 | #endif |
| 144 | } |
| 145 | |
| 146 | /* |
| 147 | ** This function implements a cross-platform "system()" interface. |
| 148 | */ |
| 149 | int fossil_system(const char *zOrigCmd){ |
| 150 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -140,10 +140,25 @@ | |
| 140 | } |
| 141 | #else |
| 142 | fossil_free(p); |
| 143 | #endif |
| 144 | } |
| 145 | |
| 146 | /* |
| 147 | ** Translate every upper-case character in the input string into |
| 148 | ** its equivalent lower-case. |
| 149 | */ |
| 150 | char *fossil_strtolwr(char *zIn){ |
| 151 | char *zStart = zIn; |
| 152 | if( zIn ){ |
| 153 | while( *zIn ){ |
| 154 | *zIn = fossil_tolower(*zIn); |
| 155 | zIn++; |
| 156 | } |
| 157 | } |
| 158 | return zStart; |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | ** This function implements a cross-platform "system()" interface. |
| 163 | */ |
| 164 | int fossil_system(const char *zOrigCmd){ |
| 165 |