Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Initial implementation of the /announce webpage. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
65f57546110b186d016ff1567d460a71 |
User & Date: | drh 2018-06-26 01:24:25.880 |
Context
2018-06-26
| ||
02:01 | Change the email transfer encoding to quoted-printable. ... (check-in: b6a13c45 user: drh tags: trunk) | |
01:24 | Initial implementation of the /announce webpage. ... (check-in: 65f57546 user: drh tags: trunk) | |
2018-06-25
| ||
22:44 | Fix a typo in the processing of Forum record syncs. ... (check-in: b71ab777 user: drh tags: trunk) | |
Changes
Changes to src/blob.c.
︙ | ︙ | |||
482 483 484 485 486 487 488 | void blob_rewind(Blob *p){ p->iCursor = 0; } /* ** Truncate a blob back to zero length */ | | | | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 | void blob_rewind(Blob *p){ p->iCursor = 0; } /* ** Truncate a blob back to zero length */ void blob_truncate(Blob *p, int sz){ if( sz>=0 && sz<p->nUsed ) p->nUsed = sz; } /* ** Seek the cursor in a blob to the indicated offset. */ int blob_seek(Blob *p, int offset, int whence){ if( whence==BLOB_SEEK_SET ){ |
︙ | ︙ |
Changes to src/email.c.
︙ | ︙ | |||
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | sqlite3 *db; /* Database emails are sent to */ sqlite3_stmt *pStmt; /* Stmt to insert into the database */ const char *zDest; /* How to send email. */ const char *zDb; /* Name of database file */ const char *zDir; /* Directory in which to store as email files */ const char *zCmd; /* Command to run for each email */ const char *zFrom; /* Emails come from here */ char *zErr; /* Error message */ int bImmediateFail; /* On any error, call fossil_fatal() */ }; #endif /* INTERFACE */ /* ** Shutdown an emailer. Clear all information other than the error message. */ static void emailerShutdown(EmailSender *p){ sqlite3_finalize(p->pStmt); p->pStmt = 0; sqlite3_close(p->db); p->db = 0; p->zDb = 0; p->zDir = 0; p->zCmd = 0; p->zDest = "off"; } /* ** Put the EmailSender into an error state. */ static void emailerError(EmailSender *p, const char *zFormat, ...){ va_list ap; | > > | 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | sqlite3 *db; /* Database emails are sent to */ sqlite3_stmt *pStmt; /* Stmt to insert into the database */ const char *zDest; /* How to send email. */ const char *zDb; /* Name of database file */ const char *zDir; /* Directory in which to store as email files */ const char *zCmd; /* Command to run for each email */ const char *zFrom; /* Emails come from here */ Blob out; /* For zDest=="blob" */ char *zErr; /* Error message */ int bImmediateFail; /* On any error, call fossil_fatal() */ }; #endif /* INTERFACE */ /* ** Shutdown an emailer. Clear all information other than the error message. */ static void emailerShutdown(EmailSender *p){ sqlite3_finalize(p->pStmt); p->pStmt = 0; sqlite3_close(p->db); p->db = 0; p->zDb = 0; p->zDir = 0; p->zCmd = 0; p->zDest = "off"; blob_zero(&p->out); } /* ** Put the EmailSender into an error state. */ static void emailerError(EmailSender *p, const char *zFormat, ...){ va_list ap; |
︙ | ︙ | |||
452 453 454 455 456 457 458 459 460 461 462 463 464 465 | sqlite3_errmsg(p->db)); return p; } }else if( fossil_strcmp(p->zDest, "pipe")==0 ){ emailerGetSetting(p, &p->zCmd, "email-send-command"); }else if( fossil_strcmp(p->zDest, "dir")==0 ){ emailerGetSetting(p, &p->zDir, "email-send-dir"); } return p; } /* ** Send a single email message. ** | > > | 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 | sqlite3_errmsg(p->db)); return p; } }else if( fossil_strcmp(p->zDest, "pipe")==0 ){ emailerGetSetting(p, &p->zCmd, "email-send-command"); }else if( fossil_strcmp(p->zDest, "dir")==0 ){ emailerGetSetting(p, &p->zDir, "email-send-dir"); }else if( fossil_strcmp(p->zDest, "blob")==0 ){ blob_init(&p->out, 0, 0); } return p; } /* ** Send a single email message. ** |
︙ | ︙ | |||
475 476 477 478 479 480 481 | ** Content-Transfer-Encoding: ** ** The caller maintains ownership of the input Blobs. This routine will ** read the Blobs and send them onward to the email system, but it will ** not free them. */ void email_send(EmailSender *p, Blob *pHdr, Blob *pBody){ | | > > > > > > | > > | | | | | | 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 | ** Content-Transfer-Encoding: ** ** The caller maintains ownership of the input Blobs. This routine will ** read the Blobs and send them onward to the email system, but it will ** not free them. */ void email_send(EmailSender *p, Blob *pHdr, Blob *pBody){ Blob all, *pOut; if( fossil_strcmp(p->zDest, "off")==0 ){ return; } if( fossil_strcmp(p->zDest, "blob")==0 ){ pOut = &p->out; if( blob_size(pOut) ){ blob_appendf(pOut, "%.72c\n", '='); } }else{ blob_init(&all, 0, 0); pOut = &all; } blob_append(pOut, blob_buffer(pHdr), blob_size(pHdr)); blob_appendf(pOut, "From: %s\r\n", p->zFrom); blob_add_final_newline(pBody); blob_appendf(pOut,"Content-Type: text/plain\r\n"); blob_appendf(pOut, "Content-Transfer-Encoding: base64\r\n\r\n"); append_base64(pOut, pBody); if( p->pStmt ){ int i, rc; sqlite3_bind_text(p->pStmt, 1, blob_str(&all), -1, SQLITE_TRANSIENT); for(i=0; i<100 && sqlite3_step(p->pStmt)==SQLITE_BUSY; i++){ sqlite3_sleep(10); } rc = sqlite3_reset(p->pStmt); |
︙ | ︙ | |||
1760 1761 1762 1763 1764 1765 1766 | blob_append(&body, "\n", 1); blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt)); } if( nHit==0 ) continue; blob_appendf(&body,"\n%.72c\nSubscription info: %s/alerts/%s\n", '-', zUrl, zCode); email_send(pSender,&hdr,&body); | | | | 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 | blob_append(&body, "\n", 1); blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt)); } if( nHit==0 ) continue; blob_appendf(&body,"\n%.72c\nSubscription info: %s/alerts/%s\n", '-', zUrl, zCode); email_send(pSender,&hdr,&body); blob_truncate(&hdr, 0); blob_truncate(&body, 0); } blob_zero(&hdr); blob_zero(&body); db_finalize(&q); email_free_eventlist(pEvents); if( (flags & SENDALERT_PRESERVE)==0 ){ if( flags & SENDALERT_DIGEST ){ |
︙ | ︙ | |||
1814 1815 1816 1817 1818 1819 1820 | } autoexec_done: db_end_transaction(0); } /* | | | | 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 | } autoexec_done: db_end_transaction(0); } /* ** WEBPAGE: msgtoadmin ** ** A web-form to send a message to the repository administrator. */ void msgtoadmin_page(void){ const char *zAdminEmail = db_get("email-admin",0); unsigned int uSeed; const char *zDecoded; char *zCaptcha = 0; login_check_credentials(); if( zAdminEmail==0 || zAdminEmail[0]==0 ){ |
︙ | ︙ | |||
1869 1870 1871 1872 1873 1874 1875 | } if( captcha_needed() ){ uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); } style_header("Message To Administrator"); | | | 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 | } if( captcha_needed() ){ uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); } style_header("Message To Administrator"); form_begin(0, "%R/msgtoadmin"); @ <p>Enter a message to the repository administrator below:</p> @ <table class="subscribe"> if( zCaptcha ){ @ <tr> @ <td class="form_label">Security Code:</td> @ <td><input type="text" name="captcha" value="" size="10"> @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> |
︙ | ︙ | |||
1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 | if( zCaptcha ){ @ <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(); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 | if( zCaptcha ){ @ <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(); } /* ** Send an annoucement message described by query parameter. ** Permission to do this has already been verified. */ static char *email_send_announcement(void){ EmailSender *pSender; char *zErr; const char *zTo = PT("to"); char *zSubject = PT("subject"); int bAll = PB("all"); int bAA = PB("aa"); const char *zSub = db_get("email-subname", "[Fossil Repo]"); int bTest2 = fossil_strcmp(P("name"),"test2")==0; Blob hdr, body; blob_init(&body, 0, 0); blob_init(&hdr, 0, 0); blob_appendf(&body, "%s", PT("msg")/*safe-for-%s*/); pSender = email_sender_new(bTest2 ? "blob" : 0, 0); if( zTo[0] ){ blob_appendf(&hdr, "To: %s\nSubject: %s %s\n", zTo, zSub, zSubject); email_send(pSender, &hdr, &body); } if( bAll || bAA ){ Stmt q; int nUsed = blob_size(&body); const char *zURL = db_get("email-url",0); db_prepare(&q, "SELECT semail, subscriberCode FROM subscriber " " WHERE sverified AND NOT sdonotcall %s", bAll ? "" : " AND ssub LIKE '%a%'"); while( db_step(&q)==SQLITE_ROW ){ const char *zCode = db_column_text(&q, 1); zTo = db_column_text(&q, 0); blob_truncate(&hdr, 0); blob_appendf(&hdr, "To: %s\nSubject: %s %s\n", zTo, zSub, zSubject); if( zURL ){ blob_truncate(&body, nUsed); blob_appendf(&body,"\n%.72c\nSubscription info: %s/alerts/%s\n", '-', zURL, zCode); } email_send(pSender, &hdr, &body); } db_finalize(&q); } if( bTest2 ){ /* If the URL is /announce/test2 instead of just /announce, then no ** email is actually sent. Instead, the text of the email that would ** have been sent is displayed in the result window. */ @ <pre style='border: 2px solid blue; padding: 1ex'> @ %h(blob_str(&pSender->out)) @ </pre> } zErr = pSender->zErr; pSender->zErr = 0; email_sender_free(pSender); return zErr; } /* ** WEBPAGE: announce ** ** A web-form, available to users with the "Send-Announcement" or "A" ** capability, that allows one to to send an announcements to whomever ** has subscribed to them. The administrator can also send an announcement ** to the entire mailing list (including people who have elected to ** receive no announcements or notifications of any kind, or to ** individual email to anyone. */ void announce_page(void){ const char *zTo = PT("to"); login_check_credentials(); if( !g.perm.Announce ){ login_needed(0); return; } if( fossil_strcmp(P("name"),"test1")==0 ){ /* Visit the /announce/test1 page to see the CGI variables */ @ <p style='border: 1px solid black; padding: 1ex;'> cgi_print_all(0, 0); @ </p> }else if( P("submit")!=0 && cgi_csrf_safe(1) ){ char *zErr = email_send_announcement(); style_header("Announcement Sent"); if( zErr ){ @ <h1>Internal Error</h1> @ <p>The following error was reported by the system: @ <blockquote><pre> @ %h(zErr) @ </pre></blockquote> }else{ @ <p>The announcement has been sent.</p> } style_footer(); return; } style_header("Send Announcement"); @ <form method="POST"> @ <table class="subscribe"> if( g.perm.Admin ){ int aa = PB("aa"); int all = PB("all"); const char *aack = aa ? "checked" : ""; const char *allck = all ? "checked" : ""; @ <tr> @ <td class="form_label">To:</td> @ <td><input type="text" name="to" value="%h(PT("to"))" size="30"><br> @ <label><input type="checkbox" name="aa" %s(aack)> \ @ All "announcement" subscribers</label><br> @ <label><input type="checkbox" name="all" %s(allck)> \ @ All subscribers</label></td> @ </tr> } @ <tr> @ <td class="form_label">Subject:</td> @ <td><input type="text" name="subject" value="%h(PT("subject"))"\ @ size="80"></td> @ </tr> @ <tr> @ <td class="form_label">Message:</td> @ <td><textarea name="msg" cols="80" rows="10" wrap="virtual">\ @ %h(PT("msg"))</textarea> @ </tr> @ <tr> @ <td></td> @ <td><input type="submit" name="submit" value="Send Message"> @ </tr> @ </table> @ </form> style_footer(); } |
Changes to src/login.c.
︙ | ︙ | |||
1200 1201 1202 1203 1204 1205 1206 | p->RdWiki = p->WrWiki = p->NewWiki = p->ApndWiki = p->Hyperlink = p->Clone = p->NewTkt = p->Password = p->RdAddr = p->TktFmt = p->Attach = p->ApndTkt = p->ModWiki = p->ModTkt = p->Delete = p->RdForum = p->WrForum = p->ModForum = p->WrTForum = p->AdminForum = | | | 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 | p->RdWiki = p->WrWiki = p->NewWiki = p->ApndWiki = p->Hyperlink = p->Clone = p->NewTkt = p->Password = p->RdAddr = p->TktFmt = p->Attach = p->ApndTkt = p->ModWiki = p->ModTkt = p->Delete = p->RdForum = p->WrForum = p->ModForum = p->WrTForum = p->AdminForum = p->EmailAlert = p->Announce = p->WrUnver = p->Private = 1; /* Fall thru into Read/Write */ case 'i': p->Read = p->Write = 1; break; case 'o': p->Read = 1; break; case 'z': p->Zip = 1; break; case 'd': p->Delete = 1; break; |
︙ | ︙ | |||
1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 | case '6': p->AdminForum = 1; case '5': p->ModForum = 1; case '4': p->WrTForum = 1; case '3': p->WrForum = 1; case '2': p->RdForum = 1; break; case '7': p->EmailAlert = 1; break; /* The "u" privileges is a little different. It recursively ** inherits all privileges of the user named "reader" */ case 'u': { if( (flags & LOGIN_IGNORE_UV)==0 ){ const char *zUser; zUser = db_text("", "SELECT cap FROM user WHERE login='reader'"); | > | 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 | case '6': p->AdminForum = 1; case '5': p->ModForum = 1; case '4': p->WrTForum = 1; case '3': p->WrForum = 1; case '2': p->RdForum = 1; break; case '7': p->EmailAlert = 1; break; case 'A': p->Announce = 1; break; /* The "u" privileges is a little different. It recursively ** inherits all privileges of the user named "reader" */ case 'u': { if( (flags & LOGIN_IGNORE_UV)==0 ){ const char *zUser; zUser = db_text("", "SELECT cap FROM user WHERE login='reader'"); |
︙ | ︙ | |||
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 | case 't': rc = p->TktFmt; break; /* case 'u': READER */ /* case 'v': DEVELOPER */ case 'w': rc = p->WrTkt; break; case 'x': rc = p->Private; break; case 'y': rc = p->WrUnver; break; case 'z': rc = p->Zip; break; default: rc = 0; break; } } return rc; } /* | > > > > > > > | 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 | case 't': rc = p->TktFmt; break; /* case 'u': READER */ /* case 'v': DEVELOPER */ case 'w': rc = p->WrTkt; break; case 'x': rc = p->Private; break; case 'y': rc = p->WrUnver; break; case 'z': rc = p->Zip; break; case '2': rc = p->RdForum; break; case '3': rc = p->WrForum; break; case '4': rc = p->WrTForum; break; case '5': rc = p->ModForum; break; case '6': rc = p->AdminForum;break; case '7': rc = p->EmailAlert;break; case 'A': rc = p->Announce; break; default: rc = 0; break; } } return rc; } /* |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
87 88 89 90 91 92 93 94 95 96 97 98 99 100 | char WrUnver; /* y: can push unversioned content */ char RdForum; /* 2: Read forum posts */ char WrForum; /* 3: Create new forum posts */ char WrTForum; /* 4: Post to forums not subject to moderation */ char ModForum; /* 5: Moderate (approve or reject) forum posts */ char AdminForum; /* 6: Edit forum posts by other users */ char EmailAlert; /* 7: Sign up for email notifications */ }; #ifdef FOSSIL_ENABLE_TCL /* ** All Tcl related context information is in this structure. This structure ** definition has been copied from and should be kept in sync with the one in ** "th_tcl.c". | > | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | char WrUnver; /* y: can push unversioned content */ char RdForum; /* 2: Read forum posts */ char WrForum; /* 3: Create new forum posts */ char WrTForum; /* 4: Post to forums not subject to moderation */ char ModForum; /* 5: Moderate (approve or reject) forum posts */ char AdminForum; /* 6: Edit forum posts by other users */ char EmailAlert; /* 7: Sign up for email notifications */ char Announce; /* A: Send announcements */ }; #ifdef FOSSIL_ENABLE_TCL /* ** All Tcl related context information is in this structure. This structure ** definition has been copied from and should be kept in sync with the one in ** "th_tcl.c". |
︙ | ︙ |
Changes to src/setup.c.
︙ | ︙ | |||
362 363 364 365 366 367 368 | @ <td><i>Forum-Trusted:</i> Add pre-approved forum posts </td></tr> @ <tr><th valign="top">5</th> @ <td><i>Forum-Moderator:</i> Approve or disapprove forum posts</td></tr> @ <tr><th valign="top">6</th> @ <td><i>Forum-Supervisor:</i> \ @ Forum administrator @ <tr><th valign="top">7</th> | | > | | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | @ <td><i>Forum-Trusted:</i> Add pre-approved forum posts </td></tr> @ <tr><th valign="top">5</th> @ <td><i>Forum-Moderator:</i> Approve or disapprove forum posts</td></tr> @ <tr><th valign="top">6</th> @ <td><i>Forum-Supervisor:</i> \ @ Forum administrator @ <tr><th valign="top">7</th> @ <td><i>Email-Alerts:</i> Sign up for email nofications</td></tr> @ <tr><th valign="top">A</th> @ <td><i>Announce:</i> Send announcements</td></tr> @ </table> } /* ** WEBPAGE: setup_ulist_notes ** ** A documentation page showing notes about user configuration. This |
︙ | ︙ | |||
762 763 764 765 766 767 768 | @ <label><input type="checkbox" name="a4"%s(oa['4']) /> @ WriteTrusted Forum%s(B('4'))</label><br> @ <label><input type="checkbox" name="a5"%s(oa['5']) /> @ Moderate Forum%s(B('5'))</label><br> @ <label><input type="checkbox" name="a6"%s(oa['6']) /> @ Supervise Forum%s(B('6'))</label><br> @ <label><input type="checkbox" name="a7"%s(oa['7']) /> | | > > | 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 | @ <label><input type="checkbox" name="a4"%s(oa['4']) /> @ WriteTrusted Forum%s(B('4'))</label><br> @ <label><input type="checkbox" name="a5"%s(oa['5']) /> @ Moderate Forum%s(B('5'))</label><br> @ <label><input type="checkbox" name="a6"%s(oa['6']) /> @ Supervise Forum%s(B('6'))</label><br> @ <label><input type="checkbox" name="a7"%s(oa['7']) /> @ Email Alerts%s(B('7'))</label><br> @ <label><input type="checkbox" name="aA"%s(oa['A']) /> @ Send Announcements%s(B('A'))</label> @ </td></tr> @ </table> @ </td> @ </tr> @ <tr> @ <td class="usetupEditLabel">Selected Cap.:</td> @ <td> |
︙ | ︙ |
Changes to www/emaildesign.md.
︙ | ︙ | |||
120 121 122 123 124 125 126 | * The [email](/help?cmd=email) command Web pages: * The [/subscribe](/help?cmd=/subscribe) page * The [/alerts](/help?cmd=/alerts) page * The [/unsubscribe](/help?cmd=/unsubscribe) page | | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | * The [email](/help?cmd=email) command Web pages: * The [/subscribe](/help?cmd=/subscribe) page * The [/alerts](/help?cmd=/alerts) page * The [/unsubscribe](/help?cmd=/unsubscribe) page * The [/msgtoadmin](/help?cmd=/msgtoadmin) page Web pages for administrators only: * The [/setup_email](/help?cmd=/setup_email) page * The [/subscribers](/help?cmd=/subscribers) page Test command: |
︙ | ︙ |