| | @@ -337,10 +337,11 @@ |
| 337 | 337 | const char *zDest; /* How to send email. */ |
| 338 | 338 | const char *zDb; /* Name of database file */ |
| 339 | 339 | const char *zDir; /* Directory in which to store as email files */ |
| 340 | 340 | const char *zCmd; /* Command to run for each email */ |
| 341 | 341 | const char *zFrom; /* Emails come from here */ |
| 342 | + Blob out; /* For zDest=="blob" */ |
| 342 | 343 | char *zErr; /* Error message */ |
| 343 | 344 | int bImmediateFail; /* On any error, call fossil_fatal() */ |
| 344 | 345 | }; |
| 345 | 346 | #endif /* INTERFACE */ |
| 346 | 347 | |
| | @@ -354,10 +355,11 @@ |
| 354 | 355 | p->db = 0; |
| 355 | 356 | p->zDb = 0; |
| 356 | 357 | p->zDir = 0; |
| 357 | 358 | p->zCmd = 0; |
| 358 | 359 | p->zDest = "off"; |
| 360 | + blob_zero(&p->out); |
| 359 | 361 | } |
| 360 | 362 | |
| 361 | 363 | /* |
| 362 | 364 | ** Put the EmailSender into an error state. |
| 363 | 365 | */ |
| | @@ -454,10 +456,12 @@ |
| 454 | 456 | } |
| 455 | 457 | }else if( fossil_strcmp(p->zDest, "pipe")==0 ){ |
| 456 | 458 | emailerGetSetting(p, &p->zCmd, "email-send-command"); |
| 457 | 459 | }else if( fossil_strcmp(p->zDest, "dir")==0 ){ |
| 458 | 460 | emailerGetSetting(p, &p->zDir, "email-send-dir"); |
| 461 | + }else if( fossil_strcmp(p->zDest, "blob")==0 ){ |
| 462 | + blob_init(&p->out, 0, 0); |
| 459 | 463 | } |
| 460 | 464 | return p; |
| 461 | 465 | } |
| 462 | 466 | |
| 463 | 467 | /* |
| | @@ -477,21 +481,29 @@ |
| 477 | 481 | ** The caller maintains ownership of the input Blobs. This routine will |
| 478 | 482 | ** read the Blobs and send them onward to the email system, but it will |
| 479 | 483 | ** not free them. |
| 480 | 484 | */ |
| 481 | 485 | void email_send(EmailSender *p, Blob *pHdr, Blob *pBody){ |
| 482 | | - Blob all; |
| 486 | + Blob all, *pOut; |
| 483 | 487 | if( fossil_strcmp(p->zDest, "off")==0 ){ |
| 484 | 488 | return; |
| 485 | 489 | } |
| 486 | | - blob_init(&all, 0, 0); |
| 487 | | - blob_append(&all, blob_buffer(pHdr), blob_size(pHdr)); |
| 488 | | - blob_appendf(&all, "From: %s\r\n", p->zFrom); |
| 490 | + if( fossil_strcmp(p->zDest, "blob")==0 ){ |
| 491 | + pOut = &p->out; |
| 492 | + if( blob_size(pOut) ){ |
| 493 | + blob_appendf(pOut, "%.72c\n", '='); |
| 494 | + } |
| 495 | + }else{ |
| 496 | + blob_init(&all, 0, 0); |
| 497 | + pOut = &all; |
| 498 | + } |
| 499 | + blob_append(pOut, blob_buffer(pHdr), blob_size(pHdr)); |
| 500 | + blob_appendf(pOut, "From: %s\r\n", p->zFrom); |
| 489 | 501 | blob_add_final_newline(pBody); |
| 490 | | - blob_appendf(&all,"Content-Type: text/plain\r\n"); |
| 491 | | - blob_appendf(&all, "Content-Transfer-Encoding: base64\r\n\r\n"); |
| 492 | | - append_base64(&all, pBody); |
| 502 | + blob_appendf(pOut,"Content-Type: text/plain\r\n"); |
| 503 | + blob_appendf(pOut, "Content-Transfer-Encoding: base64\r\n\r\n"); |
| 504 | + append_base64(pOut, pBody); |
| 493 | 505 | if( p->pStmt ){ |
| 494 | 506 | int i, rc; |
| 495 | 507 | sqlite3_bind_text(p->pStmt, 1, blob_str(&all), -1, SQLITE_TRANSIENT); |
| 496 | 508 | for(i=0; i<100 && sqlite3_step(p->pStmt)==SQLITE_BUSY; i++){ |
| 497 | 509 | sqlite3_sleep(10); |
| | @@ -1762,12 +1774,12 @@ |
| 1762 | 1774 | } |
| 1763 | 1775 | if( nHit==0 ) continue; |
| 1764 | 1776 | blob_appendf(&body,"\n%.72c\nSubscription info: %s/alerts/%s\n", |
| 1765 | 1777 | '-', zUrl, zCode); |
| 1766 | 1778 | email_send(pSender,&hdr,&body); |
| 1767 | | - blob_truncate(&hdr); |
| 1768 | | - blob_truncate(&body); |
| 1779 | + blob_truncate(&hdr, 0); |
| 1780 | + blob_truncate(&body, 0); |
| 1769 | 1781 | } |
| 1770 | 1782 | blob_zero(&hdr); |
| 1771 | 1783 | blob_zero(&body); |
| 1772 | 1784 | db_finalize(&q); |
| 1773 | 1785 | email_free_eventlist(pEvents); |
| | @@ -1816,15 +1828,15 @@ |
| 1816 | 1828 | autoexec_done: |
| 1817 | 1829 | db_end_transaction(0); |
| 1818 | 1830 | } |
| 1819 | 1831 | |
| 1820 | 1832 | /* |
| 1821 | | -** WEBPAGE: msgadmin |
| 1833 | +** WEBPAGE: msgtoadmin |
| 1822 | 1834 | ** |
| 1823 | 1835 | ** A web-form to send a message to the repository administrator. |
| 1824 | 1836 | */ |
| 1825 | | -void msgadmin_page(void){ |
| 1837 | +void msgtoadmin_page(void){ |
| 1826 | 1838 | const char *zAdminEmail = db_get("email-admin",0); |
| 1827 | 1839 | unsigned int uSeed; |
| 1828 | 1840 | const char *zDecoded; |
| 1829 | 1841 | char *zCaptcha = 0; |
| 1830 | 1842 | |
| | @@ -1871,11 +1883,11 @@ |
| 1871 | 1883 | uSeed = captcha_seed(); |
| 1872 | 1884 | zDecoded = captcha_decode(uSeed); |
| 1873 | 1885 | zCaptcha = captcha_render(zDecoded); |
| 1874 | 1886 | } |
| 1875 | 1887 | style_header("Message To Administrator"); |
| 1876 | | - form_begin(0, "%R/msgadmin"); |
| 1888 | + form_begin(0, "%R/msgtoadmin"); |
| 1877 | 1889 | @ <p>Enter a message to the repository administrator below:</p> |
| 1878 | 1890 | @ <table class="subscribe"> |
| 1879 | 1891 | if( zCaptcha ){ |
| 1880 | 1892 | @ <tr> |
| 1881 | 1893 | @ <td class="form_label">Security Code:</td> |
| | @@ -1907,8 +1919,140 @@ |
| 1907 | 1919 | @ %h(zCaptcha) |
| 1908 | 1920 | @ </pre> |
| 1909 | 1921 | @ Enter the 8 characters above in the "Security Code" box |
| 1910 | 1922 | @ </td></tr></table></div> |
| 1911 | 1923 | } |
| 1924 | + @ </form> |
| 1925 | + style_footer(); |
| 1926 | +} |
| 1927 | + |
| 1928 | +/* |
| 1929 | +** Send an annoucement message described by query parameter. |
| 1930 | +** Permission to do this has already been verified. |
| 1931 | +*/ |
| 1932 | +static char *email_send_announcement(void){ |
| 1933 | + EmailSender *pSender; |
| 1934 | + char *zErr; |
| 1935 | + const char *zTo = PT("to"); |
| 1936 | + char *zSubject = PT("subject"); |
| 1937 | + int bAll = PB("all"); |
| 1938 | + int bAA = PB("aa"); |
| 1939 | + const char *zSub = db_get("email-subname", "[Fossil Repo]"); |
| 1940 | + int bTest2 = fossil_strcmp(P("name"),"test2")==0; |
| 1941 | + Blob hdr, body; |
| 1942 | + blob_init(&body, 0, 0); |
| 1943 | + blob_init(&hdr, 0, 0); |
| 1944 | + blob_appendf(&body, "%s", PT("msg")/*safe-for-%s*/); |
| 1945 | + pSender = email_sender_new(bTest2 ? "blob" : 0, 0); |
| 1946 | + if( zTo[0] ){ |
| 1947 | + blob_appendf(&hdr, "To: %s\nSubject: %s %s\n", zTo, zSub, zSubject); |
| 1948 | + email_send(pSender, &hdr, &body); |
| 1949 | + } |
| 1950 | + if( bAll || bAA ){ |
| 1951 | + Stmt q; |
| 1952 | + int nUsed = blob_size(&body); |
| 1953 | + const char *zURL = db_get("email-url",0); |
| 1954 | + db_prepare(&q, "SELECT semail, subscriberCode FROM subscriber " |
| 1955 | + " WHERE sverified AND NOT sdonotcall %s", |
| 1956 | + bAll ? "" : " AND ssub LIKE '%a%'"); |
| 1957 | + while( db_step(&q)==SQLITE_ROW ){ |
| 1958 | + const char *zCode = db_column_text(&q, 1); |
| 1959 | + zTo = db_column_text(&q, 0); |
| 1960 | + blob_truncate(&hdr, 0); |
| 1961 | + blob_appendf(&hdr, "To: %s\nSubject: %s %s\n", zTo, zSub, zSubject); |
| 1962 | + if( zURL ){ |
| 1963 | + blob_truncate(&body, nUsed); |
| 1964 | + blob_appendf(&body,"\n%.72c\nSubscription info: %s/alerts/%s\n", |
| 1965 | + '-', zURL, zCode); |
| 1966 | + } |
| 1967 | + email_send(pSender, &hdr, &body); |
| 1968 | + } |
| 1969 | + db_finalize(&q); |
| 1970 | + } |
| 1971 | + if( bTest2 ){ |
| 1972 | + /* If the URL is /announce/test2 instead of just /announce, then no |
| 1973 | + ** email is actually sent. Instead, the text of the email that would |
| 1974 | + ** have been sent is displayed in the result window. */ |
| 1975 | + @ <pre style='border: 2px solid blue; padding: 1ex'> |
| 1976 | + @ %h(blob_str(&pSender->out)) |
| 1977 | + @ </pre> |
| 1978 | + } |
| 1979 | + zErr = pSender->zErr; |
| 1980 | + pSender->zErr = 0; |
| 1981 | + email_sender_free(pSender); |
| 1982 | + return zErr; |
| 1983 | +} |
| 1984 | + |
| 1985 | + |
| 1986 | +/* |
| 1987 | +** WEBPAGE: announce |
| 1988 | +** |
| 1989 | +** A web-form, available to users with the "Send-Announcement" or "A" |
| 1990 | +** capability, that allows one to to send an announcements to whomever |
| 1991 | +** has subscribed to them. The administrator can also send an announcement |
| 1992 | +** to the entire mailing list (including people who have elected to |
| 1993 | +** receive no announcements or notifications of any kind, or to |
| 1994 | +** individual email to anyone. |
| 1995 | +*/ |
| 1996 | +void announce_page(void){ |
| 1997 | + const char *zTo = PT("to"); |
| 1998 | + login_check_credentials(); |
| 1999 | + if( !g.perm.Announce ){ |
| 2000 | + login_needed(0); |
| 2001 | + return; |
| 2002 | + } |
| 2003 | + if( fossil_strcmp(P("name"),"test1")==0 ){ |
| 2004 | + /* Visit the /announce/test1 page to see the CGI variables */ |
| 2005 | + @ <p style='border: 1px solid black; padding: 1ex;'> |
| 2006 | + cgi_print_all(0, 0); |
| 2007 | + @ </p> |
| 2008 | + }else |
| 2009 | + if( P("submit")!=0 && cgi_csrf_safe(1) ){ |
| 2010 | + char *zErr = email_send_announcement(); |
| 2011 | + style_header("Announcement Sent"); |
| 2012 | + if( zErr ){ |
| 2013 | + @ <h1>Internal Error</h1> |
| 2014 | + @ <p>The following error was reported by the system: |
| 2015 | + @ <blockquote><pre> |
| 2016 | + @ %h(zErr) |
| 2017 | + @ </pre></blockquote> |
| 2018 | + }else{ |
| 2019 | + @ <p>The announcement has been sent.</p> |
| 2020 | + } |
| 2021 | + style_footer(); |
| 2022 | + return; |
| 2023 | + } |
| 2024 | + style_header("Send Announcement"); |
| 2025 | + @ <form method="POST"> |
| 2026 | + @ <table class="subscribe"> |
| 2027 | + if( g.perm.Admin ){ |
| 2028 | + int aa = PB("aa"); |
| 2029 | + int all = PB("all"); |
| 2030 | + const char *aack = aa ? "checked" : ""; |
| 2031 | + const char *allck = all ? "checked" : ""; |
| 2032 | + @ <tr> |
| 2033 | + @ <td class="form_label">To:</td> |
| 2034 | + @ <td><input type="text" name="to" value="%h(PT("to"))" size="30"><br> |
| 2035 | + @ <label><input type="checkbox" name="aa" %s(aack)> \ |
| 2036 | + @ All "announcement" subscribers</label><br> |
| 2037 | + @ <label><input type="checkbox" name="all" %s(allck)> \ |
| 2038 | + @ All subscribers</label></td> |
| 2039 | + @ </tr> |
| 2040 | + } |
| 2041 | + @ <tr> |
| 2042 | + @ <td class="form_label">Subject:</td> |
| 2043 | + @ <td><input type="text" name="subject" value="%h(PT("subject"))"\ |
| 2044 | + @ size="80"></td> |
| 2045 | + @ </tr> |
| 2046 | + @ <tr> |
| 2047 | + @ <td class="form_label">Message:</td> |
| 2048 | + @ <td><textarea name="msg" cols="80" rows="10" wrap="virtual">\ |
| 2049 | + @ %h(PT("msg"))</textarea> |
| 2050 | + @ </tr> |
| 2051 | + @ <tr> |
| 2052 | + @ <td></td> |
| 2053 | + @ <td><input type="submit" name="submit" value="Send Message"> |
| 2054 | + @ </tr> |
| 2055 | + @ </table> |
| 1912 | 2056 | @ </form> |
| 1913 | 2057 | style_footer(); |
| 1914 | 2058 | } |
| 1915 | 2059 | |