Fossil SCM

For non-digest subscribers, alerts for new forum posts are now in separate emails with each email having a subject which is the title of the forum thread, and with appropriate Message-Id and In-Reply-To header fields so that email clients can string together the email thread. Digest subscribes still get just the synopsis.

drh 2018-08-07 00:22 UTC forum-v2
Commit 4d6591736e92ec5f4ca328818131c222b22d2f5f6587e8fe567f8de6d80c151d
1 file changed +151 -37
+151 -37
--- src/email.c
+++ src/email.c
@@ -694,16 +694,18 @@
694694
pOut = &all;
695695
}
696696
blob_append(pOut, blob_buffer(pHdr), blob_size(pHdr));
697697
blob_appendf(pOut, "From: <%s>\r\n", p->zFrom);
698698
blob_appendf(pOut, "Date: %z\r\n", cgi_rfc822_datestamp(time(0)));
699
- /* Message-id format: "<$(date)x$(random).$(from)>" where $(date) is
700
- ** the current unix-time in hex, $(random) is a 64-bit random number,
701
- ** and $(from) is the sender. */
702
- sqlite3_randomness(sizeof(r1), &r1);
703
- r2 = time(0);
704
- blob_appendf(pOut, "Message-Id: <%llxx%016llx.%s>\r\n", r2, r1, p->zFrom);
699
+ if( strstr(blob_str(pHdr), "\r\nMessage-Id:")==0 ){
700
+ /* Message-id format: "<$(date)x$(random).$(from)>" where $(date) is
701
+ ** the current unix-time in hex, $(random) is a 64-bit random number,
702
+ ** and $(from) is the sender. */
703
+ sqlite3_randomness(sizeof(r1), &r1);
704
+ r2 = time(0);
705
+ blob_appendf(pOut, "Message-Id: <%llxx%016llx.%s>\r\n", r2, r1, p->zFrom);
706
+ }
705707
blob_add_final_newline(pBody);
706708
blob_appendf(pOut,"Content-Type: text/plain\r\n");
707709
#if 0
708710
blob_appendf(pOut, "Content-Transfer-Encoding: base64\r\n\r\n");
709711
append_base64(pOut, pBody);
@@ -986,13 +988,12 @@
986988
for(i=3; i<g.argc; i++){
987989
if( i>3 ) blob_append(&hdr, ", ", 2);
988990
blob_appendf(&hdr, "<%s>", g.argv[i]);
989991
}
990992
blob_append(&hdr,"\r\n",2);
991
- if( zSubject ){
992
- blob_appendf(&hdr, "Subject: %s\r\n", zSubject);
993
- }
993
+ if( zSubject==0 ) zSubject = "fossil alerts test-message";
994
+ blob_appendf(&hdr, "Subject: %s\r\n", zSubject);
994995
if( zSource ){
995996
blob_read_from_file(&body, zSource, ExtFILE);
996997
}else{
997998
prompt_for_user_comment(&body, &prompt);
998999
}
@@ -1793,10 +1794,11 @@
17931794
** instance of the following object.
17941795
*/
17951796
struct EmailEvent {
17961797
int type; /* 'c', 'f', 'm', 't', 'w' */
17971798
int needMod; /* Pending moderator approval */
1799
+ Blob hdr; /* Header content, for forum entries */
17981800
Blob txt; /* Text description to appear in an alert */
17991801
EmailEvent *pNext; /* Next in chronological order */
18001802
};
18011803
#endif
18021804
@@ -1805,10 +1807,11 @@
18051807
*/
18061808
void email_free_eventlist(EmailEvent *p){
18071809
while( p ){
18081810
EmailEvent *pNext = p->pNext;
18091811
blob_reset(&p->txt);
1812
+ blob_reset(&p->hdr);
18101813
fossil_free(p);
18111814
p = pNext;
18121815
}
18131816
}
18141817
@@ -1823,11 +1826,15 @@
18231826
Stmt q;
18241827
EmailEvent *p;
18251828
EmailEvent anchor;
18261829
EmailEvent *pLast;
18271830
const char *zUrl = db_get("email-url","http://localhost:8080");
1831
+ const char *zFrom;
1832
+ const char *zSub;
1833
+
18281834
1835
+ /* First do non-forum post events */
18291836
db_prepare(&q,
18301837
"SELECT"
18311838
" blob.uuid," /* 0 */
18321839
" datetime(event.mtime)," /* 1 */
18331840
" coalesce(ecomment,comment)"
@@ -1836,39 +1843,37 @@
18361843
" FROM (SELECT group_concat(substr(tagname,5), ', ') AS x"
18371844
" FROM tag, tagxref"
18381845
" WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
18391846
" AND tagxref.rid=blob.rid AND tagxref.tagtype>0))"
18401847
" || ')' as comment," /* 2 */
1841
- " tagxref.value AS branch," /* 3 */
1842
- " wantalert.eventId," /* 4 */
1843
- " wantalert.needMod" /* 5 */
1844
- " FROM temp.wantalert JOIN tag CROSS JOIN event CROSS JOIN blob"
1845
- " LEFT JOIN tagxref ON tagxref.tagid=tag.tagid"
1846
- " AND tagxref.tagtype>0"
1847
- " AND tagxref.rid=blob.rid"
1848
+ " wantalert.eventId," /* 3 */
1849
+ " wantalert.needMod" /* 4 */
1850
+ " FROM temp.wantalert CROSS JOIN event CROSS JOIN blob"
18481851
" WHERE blob.rid=event.objid"
1849
- " AND tag.tagname='branch'"
18501852
" AND event.objid=substr(wantalert.eventId,2)+0"
1851
- " ORDER BY event.mtime"
1853
+ " AND (%d OR eventId NOT GLOB 'f*')"
1854
+ " ORDER BY event.mtime",
1855
+ doDigest
18521856
);
18531857
memset(&anchor, 0, sizeof(anchor));
18541858
pLast = &anchor;
18551859
*pnEvent = 0;
18561860
while( db_step(&q)==SQLITE_ROW ){
18571861
const char *zType = "";
18581862
p = fossil_malloc( sizeof(EmailEvent) );
18591863
pLast->pNext = p;
18601864
pLast = p;
1861
- p->type = db_column_text(&q, 4)[0];
1862
- p->needMod = db_column_int(&q, 5);
1865
+ p->type = db_column_text(&q, 3)[0];
1866
+ p->needMod = db_column_int(&q, 4);
18631867
p->pNext = 0;
18641868
switch( p->type ){
18651869
case 'c': zType = "Check-In"; break;
18661870
case 'f': zType = "Forum post"; break;
18671871
case 't': zType = "Wiki Edit"; break;
18681872
case 'w': zType = "Ticket Change"; break;
18691873
}
1874
+ blob_init(&p->hdr, 0, 0);
18701875
blob_init(&p->txt, 0, 0);
18711876
blob_appendf(&p->txt,"== %s %s ==\n%s\n%s/info/%.20s\n",
18721877
db_column_text(&q,1),
18731878
zType,
18741879
db_column_text(&q,2),
@@ -1882,10 +1887,83 @@
18821887
);
18831888
}
18841889
(*pnEvent)++;
18851890
}
18861891
db_finalize(&q);
1892
+
1893
+ /* Early-out if forumpost is not a table in this repository */
1894
+ if( !db_table_exists("repository","forumpost") ){
1895
+ return anchor.pNext;
1896
+ }
1897
+
1898
+ /* For digests, the previous loop also handled forumposts already */
1899
+ if( doDigest ){
1900
+ return anchor.pNext;
1901
+ }
1902
+
1903
+ /* If we reach this point, it means that forumposts exist and this
1904
+ ** is a normal email alert. Construct full-text forum post alerts
1905
+ ** using a format that enables them to be sent as separate emails.
1906
+ */
1907
+ db_prepare(&q,
1908
+ "SELECT"
1909
+ " forumpost.fpid," /* 0 */
1910
+ " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1 */
1911
+ " datetime(event.mtime)," /* 2 */
1912
+ " substr(comment,instr(comment,':')+2)," /* 3 */
1913
+ " (SELECT uuid FROM blob WHERE rid=forumpost.firt)," /* 4 */
1914
+ " wantalert.needMod" /* 5 */
1915
+ " FROM temp.wantalert, event, forumpost"
1916
+ " WHERE event.objid=substr(wantalert.eventId,2)+0"
1917
+ " AND eventId GLOB 'f*'"
1918
+ " AND forumpost.fpid=event.objid"
1919
+ );
1920
+ zFrom = db_get("email-self",0);
1921
+ zSub = db_get("email-subname","");
1922
+ while( db_step(&q)==SQLITE_ROW ){
1923
+ Manifest *pPost = manifest_get(db_column_int(&q,0), CFTYPE_FORUM, 0);
1924
+ const char *zIrt;
1925
+ const char *zUuid;
1926
+ const char *zTitle;
1927
+ if( pPost==0 ) continue;
1928
+ p = fossil_malloc( sizeof(EmailEvent) );
1929
+ pLast->pNext = p;
1930
+ pLast = p;
1931
+ p->type = 'f';
1932
+ p->needMod = db_column_int(&q, 5);
1933
+ p->pNext = 0;
1934
+ blob_init(&p->hdr, 0, 0);
1935
+ zUuid = db_column_text(&q, 1);
1936
+ zTitle = db_column_text(&q, 3);
1937
+ if( p->needMod ){
1938
+ blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n",
1939
+ zSub, zTitle);
1940
+ }else{
1941
+ blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle);
1942
+ blob_appendf(&p->hdr, "Message-Id: <%s.%s>\r\n", zUuid, zFrom);
1943
+ zIrt = db_column_text(&q, 4);
1944
+ if( zIrt && zIrt[0] ){
1945
+ blob_appendf(&p->hdr, "In-Reply-To: <%s.%s>\r\n", zIrt, zFrom);
1946
+ }
1947
+ }
1948
+ blob_init(&p->txt, 0, 0);
1949
+ if( p->needMod ){
1950
+ blob_appendf(&p->txt,
1951
+ "** Pending moderator approval (%s/modreq) **\n",
1952
+ zUrl
1953
+ );
1954
+ }
1955
+ blob_appendf(&p->txt,
1956
+ "Forum post by %s on %s\n",
1957
+ pPost->zUser, db_column_text(&q, 2));
1958
+ blob_appendf(&p->txt, "%s/forumpost/%S\n\n", zUrl, zUuid);
1959
+ blob_append(&p->txt, pPost->zWiki, -1);
1960
+ manifest_destroy(pPost);
1961
+ (*pnEvent)++;
1962
+ }
1963
+ db_finalize(&q);
1964
+
18871965
return anchor.pNext;
18881966
}
18891967
18901968
/*
18911969
** Put a header on an alert email
@@ -1953,10 +2031,14 @@
19532031
blob_init(&out, 0, 0);
19542032
email_header(&out);
19552033
pEvent = email_compute_event_text(&nEvent, doDigest);
19562034
for(p=pEvent; p; p=p->pNext){
19572035
blob_append(&out, "\n", 1);
2036
+ if( blob_size(&p->hdr) ){
2037
+ blob_append(&out, blob_buffer(&p->hdr), blob_size(&p->hdr));
2038
+ blob_append(&out, "\n", 1);
2039
+ }
19582040
blob_append(&out, blob_buffer(&p->txt), blob_size(&p->txt));
19592041
}
19602042
email_free_eventlist(pEvent);
19612043
email_footer(&out);
19622044
fossil_print("%s", blob_str(&out));
@@ -1965,11 +2047,11 @@
19652047
}
19662048
19672049
/*
19682050
** COMMAND: test-add-alerts
19692051
**
1970
-** Usage: %fossil test-add-alerts [--backoffice] EVENTID ...
2052
+** Usage: %fossil test-add-alerts [OPTIONS] EVENTID ...
19712053
**
19722054
** Add one or more events to the pending_alert queue. Use this
19732055
** command during testing to force email notifications for specific
19742056
** events.
19752057
**
@@ -1976,27 +2058,43 @@
19762058
** EVENTIDs are text. The first character is 'c', 'f', 't', or 'w'
19772059
** for check-in, forum, ticket, or wiki. The remaining text is a
19782060
** integer that references the EVENT.OBJID value for the event.
19792061
** Run /timeline?showid to see these OBJID values.
19802062
**
1981
-** If the --backoffice option is included, then email_backoffice() is run
1982
-** after all alerts have been added. This will cause the alerts to
1983
-** be sent out with the SENDALERT_TRACE option.
2063
+** Options:
2064
+**
2065
+** --backoffice Run email_backoffice() after all alerts have
2066
+** been added. This will cause the alerts to be
2067
+** sent out with the SENDALERT_TRACE option.
2068
+**
2069
+** --debug Like --backoffice, but add the SENDALERT_STDOUT
2070
+** so that emails are printed to standard output
2071
+** rather than being sent.
2072
+**
2073
+** --digest Process emails using SENDALERT_DIGEST
19842074
*/
19852075
void test_add_alert_cmd(void){
19862076
int i;
19872077
int doAuto = find_option("backoffice",0,0)!=0;
2078
+ unsigned mFlags = 0;
2079
+ if( find_option("debug",0,0)!=0 ){
2080
+ doAuto = 1;
2081
+ mFlags = SENDALERT_STDOUT;
2082
+ }
2083
+ if( find_option("digest",0,0)!=0 ){
2084
+ mFlags |= SENDALERT_DIGEST;
2085
+ }
19882086
db_find_and_open_repository(0, 0);
19892087
verify_all_options();
19902088
db_begin_write();
19912089
email_schema(0);
19922090
for(i=2; i<g.argc; i++){
19932091
db_multi_exec("REPLACE INTO pending_alert(eventId) VALUES(%Q)", g.argv[i]);
19942092
}
19952093
db_end_transaction(0);
19962094
if( doAuto ){
1997
- email_backoffice(SENDALERT_TRACE);
2095
+ email_backoffice(SENDALERT_TRACE|mFlags);
19982096
}
19992097
}
20002098
20012099
#if INTERFACE
20022100
/*
@@ -2136,22 +2234,38 @@
21362234
case 't': xType = 'r'; break;
21372235
case 'w': xType = 'j'; break;
21382236
}
21392237
if( strchr(zCap,xType)==0 ) continue;
21402238
}
2141
- if( nHit==0 ){
2142
- blob_appendf(&hdr,"To: <%s>\r\n", zEmail);
2143
- blob_appendf(&hdr,"Subject: %s activity alert\r\n", zRepoName);
2144
- blob_appendf(&body,
2145
- "This is an automated email sent by the Fossil repository "
2146
- "at %s to report changes.\n",
2147
- zUrl
2148
- );
2149
- }
2150
- nHit++;
2151
- blob_append(&body, "\n", 1);
2152
- blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
2239
+ if( blob_size(&p->hdr)>0 ){
2240
+ /* This alert should be sent as a separate email */
2241
+ Blob fhdr, fbody;
2242
+ blob_init(&fhdr, 0, 0);
2243
+ blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
2244
+ blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
2245
+ blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
2246
+ blob_appendf(&fbody, "\n-- \nSubscription info: %s/alerts/%s\n",
2247
+ zUrl, zCode);
2248
+ email_send(pSender,&fhdr,&fbody);
2249
+ blob_reset(&fhdr);
2250
+ blob_reset(&fbody);
2251
+ }else{
2252
+ /* Events other than forum posts are gathered together into
2253
+ ** a single email message */
2254
+ if( nHit==0 ){
2255
+ blob_appendf(&hdr,"To: <%s>\r\n", zEmail);
2256
+ blob_appendf(&hdr,"Subject: %s activity alert\r\n", zRepoName);
2257
+ blob_appendf(&body,
2258
+ "This is an automated email sent by the Fossil repository "
2259
+ "at %s to report changes.\n",
2260
+ zUrl
2261
+ );
2262
+ }
2263
+ nHit++;
2264
+ blob_append(&body, "\n", 1);
2265
+ blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
2266
+ }
21532267
}
21542268
if( nHit==0 ) continue;
21552269
blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
21562270
zUrl, zCode);
21572271
email_send(pSender,&hdr,&body);
21582272
--- src/email.c
+++ src/email.c
@@ -694,16 +694,18 @@
694 pOut = &all;
695 }
696 blob_append(pOut, blob_buffer(pHdr), blob_size(pHdr));
697 blob_appendf(pOut, "From: <%s>\r\n", p->zFrom);
698 blob_appendf(pOut, "Date: %z\r\n", cgi_rfc822_datestamp(time(0)));
699 /* Message-id format: "<$(date)x$(random).$(from)>" where $(date) is
700 ** the current unix-time in hex, $(random) is a 64-bit random number,
701 ** and $(from) is the sender. */
702 sqlite3_randomness(sizeof(r1), &r1);
703 r2 = time(0);
704 blob_appendf(pOut, "Message-Id: <%llxx%016llx.%s>\r\n", r2, r1, p->zFrom);
 
 
705 blob_add_final_newline(pBody);
706 blob_appendf(pOut,"Content-Type: text/plain\r\n");
707 #if 0
708 blob_appendf(pOut, "Content-Transfer-Encoding: base64\r\n\r\n");
709 append_base64(pOut, pBody);
@@ -986,13 +988,12 @@
986 for(i=3; i<g.argc; i++){
987 if( i>3 ) blob_append(&hdr, ", ", 2);
988 blob_appendf(&hdr, "<%s>", g.argv[i]);
989 }
990 blob_append(&hdr,"\r\n",2);
991 if( zSubject ){
992 blob_appendf(&hdr, "Subject: %s\r\n", zSubject);
993 }
994 if( zSource ){
995 blob_read_from_file(&body, zSource, ExtFILE);
996 }else{
997 prompt_for_user_comment(&body, &prompt);
998 }
@@ -1793,10 +1794,11 @@
1793 ** instance of the following object.
1794 */
1795 struct EmailEvent {
1796 int type; /* 'c', 'f', 'm', 't', 'w' */
1797 int needMod; /* Pending moderator approval */
 
1798 Blob txt; /* Text description to appear in an alert */
1799 EmailEvent *pNext; /* Next in chronological order */
1800 };
1801 #endif
1802
@@ -1805,10 +1807,11 @@
1805 */
1806 void email_free_eventlist(EmailEvent *p){
1807 while( p ){
1808 EmailEvent *pNext = p->pNext;
1809 blob_reset(&p->txt);
 
1810 fossil_free(p);
1811 p = pNext;
1812 }
1813 }
1814
@@ -1823,11 +1826,15 @@
1823 Stmt q;
1824 EmailEvent *p;
1825 EmailEvent anchor;
1826 EmailEvent *pLast;
1827 const char *zUrl = db_get("email-url","http://localhost:8080");
 
 
 
1828
 
1829 db_prepare(&q,
1830 "SELECT"
1831 " blob.uuid," /* 0 */
1832 " datetime(event.mtime)," /* 1 */
1833 " coalesce(ecomment,comment)"
@@ -1836,39 +1843,37 @@
1836 " FROM (SELECT group_concat(substr(tagname,5), ', ') AS x"
1837 " FROM tag, tagxref"
1838 " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
1839 " AND tagxref.rid=blob.rid AND tagxref.tagtype>0))"
1840 " || ')' as comment," /* 2 */
1841 " tagxref.value AS branch," /* 3 */
1842 " wantalert.eventId," /* 4 */
1843 " wantalert.needMod" /* 5 */
1844 " FROM temp.wantalert JOIN tag CROSS JOIN event CROSS JOIN blob"
1845 " LEFT JOIN tagxref ON tagxref.tagid=tag.tagid"
1846 " AND tagxref.tagtype>0"
1847 " AND tagxref.rid=blob.rid"
1848 " WHERE blob.rid=event.objid"
1849 " AND tag.tagname='branch'"
1850 " AND event.objid=substr(wantalert.eventId,2)+0"
1851 " ORDER BY event.mtime"
 
 
1852 );
1853 memset(&anchor, 0, sizeof(anchor));
1854 pLast = &anchor;
1855 *pnEvent = 0;
1856 while( db_step(&q)==SQLITE_ROW ){
1857 const char *zType = "";
1858 p = fossil_malloc( sizeof(EmailEvent) );
1859 pLast->pNext = p;
1860 pLast = p;
1861 p->type = db_column_text(&q, 4)[0];
1862 p->needMod = db_column_int(&q, 5);
1863 p->pNext = 0;
1864 switch( p->type ){
1865 case 'c': zType = "Check-In"; break;
1866 case 'f': zType = "Forum post"; break;
1867 case 't': zType = "Wiki Edit"; break;
1868 case 'w': zType = "Ticket Change"; break;
1869 }
 
1870 blob_init(&p->txt, 0, 0);
1871 blob_appendf(&p->txt,"== %s %s ==\n%s\n%s/info/%.20s\n",
1872 db_column_text(&q,1),
1873 zType,
1874 db_column_text(&q,2),
@@ -1882,10 +1887,83 @@
1882 );
1883 }
1884 (*pnEvent)++;
1885 }
1886 db_finalize(&q);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1887 return anchor.pNext;
1888 }
1889
1890 /*
1891 ** Put a header on an alert email
@@ -1953,10 +2031,14 @@
1953 blob_init(&out, 0, 0);
1954 email_header(&out);
1955 pEvent = email_compute_event_text(&nEvent, doDigest);
1956 for(p=pEvent; p; p=p->pNext){
1957 blob_append(&out, "\n", 1);
 
 
 
 
1958 blob_append(&out, blob_buffer(&p->txt), blob_size(&p->txt));
1959 }
1960 email_free_eventlist(pEvent);
1961 email_footer(&out);
1962 fossil_print("%s", blob_str(&out));
@@ -1965,11 +2047,11 @@
1965 }
1966
1967 /*
1968 ** COMMAND: test-add-alerts
1969 **
1970 ** Usage: %fossil test-add-alerts [--backoffice] EVENTID ...
1971 **
1972 ** Add one or more events to the pending_alert queue. Use this
1973 ** command during testing to force email notifications for specific
1974 ** events.
1975 **
@@ -1976,27 +2058,43 @@
1976 ** EVENTIDs are text. The first character is 'c', 'f', 't', or 'w'
1977 ** for check-in, forum, ticket, or wiki. The remaining text is a
1978 ** integer that references the EVENT.OBJID value for the event.
1979 ** Run /timeline?showid to see these OBJID values.
1980 **
1981 ** If the --backoffice option is included, then email_backoffice() is run
1982 ** after all alerts have been added. This will cause the alerts to
1983 ** be sent out with the SENDALERT_TRACE option.
 
 
 
 
 
 
 
 
1984 */
1985 void test_add_alert_cmd(void){
1986 int i;
1987 int doAuto = find_option("backoffice",0,0)!=0;
 
 
 
 
 
 
 
 
1988 db_find_and_open_repository(0, 0);
1989 verify_all_options();
1990 db_begin_write();
1991 email_schema(0);
1992 for(i=2; i<g.argc; i++){
1993 db_multi_exec("REPLACE INTO pending_alert(eventId) VALUES(%Q)", g.argv[i]);
1994 }
1995 db_end_transaction(0);
1996 if( doAuto ){
1997 email_backoffice(SENDALERT_TRACE);
1998 }
1999 }
2000
2001 #if INTERFACE
2002 /*
@@ -2136,22 +2234,38 @@
2136 case 't': xType = 'r'; break;
2137 case 'w': xType = 'j'; break;
2138 }
2139 if( strchr(zCap,xType)==0 ) continue;
2140 }
2141 if( nHit==0 ){
2142 blob_appendf(&hdr,"To: <%s>\r\n", zEmail);
2143 blob_appendf(&hdr,"Subject: %s activity alert\r\n", zRepoName);
2144 blob_appendf(&body,
2145 "This is an automated email sent by the Fossil repository "
2146 "at %s to report changes.\n",
2147 zUrl
2148 );
2149 }
2150 nHit++;
2151 blob_append(&body, "\n", 1);
2152 blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2153 }
2154 if( nHit==0 ) continue;
2155 blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
2156 zUrl, zCode);
2157 email_send(pSender,&hdr,&body);
2158
--- src/email.c
+++ src/email.c
@@ -694,16 +694,18 @@
694 pOut = &all;
695 }
696 blob_append(pOut, blob_buffer(pHdr), blob_size(pHdr));
697 blob_appendf(pOut, "From: <%s>\r\n", p->zFrom);
698 blob_appendf(pOut, "Date: %z\r\n", cgi_rfc822_datestamp(time(0)));
699 if( strstr(blob_str(pHdr), "\r\nMessage-Id:")==0 ){
700 /* Message-id format: "<$(date)x$(random).$(from)>" where $(date) is
701 ** the current unix-time in hex, $(random) is a 64-bit random number,
702 ** and $(from) is the sender. */
703 sqlite3_randomness(sizeof(r1), &r1);
704 r2 = time(0);
705 blob_appendf(pOut, "Message-Id: <%llxx%016llx.%s>\r\n", r2, r1, p->zFrom);
706 }
707 blob_add_final_newline(pBody);
708 blob_appendf(pOut,"Content-Type: text/plain\r\n");
709 #if 0
710 blob_appendf(pOut, "Content-Transfer-Encoding: base64\r\n\r\n");
711 append_base64(pOut, pBody);
@@ -986,13 +988,12 @@
988 for(i=3; i<g.argc; i++){
989 if( i>3 ) blob_append(&hdr, ", ", 2);
990 blob_appendf(&hdr, "<%s>", g.argv[i]);
991 }
992 blob_append(&hdr,"\r\n",2);
993 if( zSubject==0 ) zSubject = "fossil alerts test-message";
994 blob_appendf(&hdr, "Subject: %s\r\n", zSubject);
 
995 if( zSource ){
996 blob_read_from_file(&body, zSource, ExtFILE);
997 }else{
998 prompt_for_user_comment(&body, &prompt);
999 }
@@ -1793,10 +1794,11 @@
1794 ** instance of the following object.
1795 */
1796 struct EmailEvent {
1797 int type; /* 'c', 'f', 'm', 't', 'w' */
1798 int needMod; /* Pending moderator approval */
1799 Blob hdr; /* Header content, for forum entries */
1800 Blob txt; /* Text description to appear in an alert */
1801 EmailEvent *pNext; /* Next in chronological order */
1802 };
1803 #endif
1804
@@ -1805,10 +1807,11 @@
1807 */
1808 void email_free_eventlist(EmailEvent *p){
1809 while( p ){
1810 EmailEvent *pNext = p->pNext;
1811 blob_reset(&p->txt);
1812 blob_reset(&p->hdr);
1813 fossil_free(p);
1814 p = pNext;
1815 }
1816 }
1817
@@ -1823,11 +1826,15 @@
1826 Stmt q;
1827 EmailEvent *p;
1828 EmailEvent anchor;
1829 EmailEvent *pLast;
1830 const char *zUrl = db_get("email-url","http://localhost:8080");
1831 const char *zFrom;
1832 const char *zSub;
1833
1834
1835 /* First do non-forum post events */
1836 db_prepare(&q,
1837 "SELECT"
1838 " blob.uuid," /* 0 */
1839 " datetime(event.mtime)," /* 1 */
1840 " coalesce(ecomment,comment)"
@@ -1836,39 +1843,37 @@
1843 " FROM (SELECT group_concat(substr(tagname,5), ', ') AS x"
1844 " FROM tag, tagxref"
1845 " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
1846 " AND tagxref.rid=blob.rid AND tagxref.tagtype>0))"
1847 " || ')' as comment," /* 2 */
1848 " wantalert.eventId," /* 3 */
1849 " wantalert.needMod" /* 4 */
1850 " FROM temp.wantalert CROSS JOIN event CROSS JOIN blob"
 
 
 
 
1851 " WHERE blob.rid=event.objid"
 
1852 " AND event.objid=substr(wantalert.eventId,2)+0"
1853 " AND (%d OR eventId NOT GLOB 'f*')"
1854 " ORDER BY event.mtime",
1855 doDigest
1856 );
1857 memset(&anchor, 0, sizeof(anchor));
1858 pLast = &anchor;
1859 *pnEvent = 0;
1860 while( db_step(&q)==SQLITE_ROW ){
1861 const char *zType = "";
1862 p = fossil_malloc( sizeof(EmailEvent) );
1863 pLast->pNext = p;
1864 pLast = p;
1865 p->type = db_column_text(&q, 3)[0];
1866 p->needMod = db_column_int(&q, 4);
1867 p->pNext = 0;
1868 switch( p->type ){
1869 case 'c': zType = "Check-In"; break;
1870 case 'f': zType = "Forum post"; break;
1871 case 't': zType = "Wiki Edit"; break;
1872 case 'w': zType = "Ticket Change"; break;
1873 }
1874 blob_init(&p->hdr, 0, 0);
1875 blob_init(&p->txt, 0, 0);
1876 blob_appendf(&p->txt,"== %s %s ==\n%s\n%s/info/%.20s\n",
1877 db_column_text(&q,1),
1878 zType,
1879 db_column_text(&q,2),
@@ -1882,10 +1887,83 @@
1887 );
1888 }
1889 (*pnEvent)++;
1890 }
1891 db_finalize(&q);
1892
1893 /* Early-out if forumpost is not a table in this repository */
1894 if( !db_table_exists("repository","forumpost") ){
1895 return anchor.pNext;
1896 }
1897
1898 /* For digests, the previous loop also handled forumposts already */
1899 if( doDigest ){
1900 return anchor.pNext;
1901 }
1902
1903 /* If we reach this point, it means that forumposts exist and this
1904 ** is a normal email alert. Construct full-text forum post alerts
1905 ** using a format that enables them to be sent as separate emails.
1906 */
1907 db_prepare(&q,
1908 "SELECT"
1909 " forumpost.fpid," /* 0 */
1910 " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1 */
1911 " datetime(event.mtime)," /* 2 */
1912 " substr(comment,instr(comment,':')+2)," /* 3 */
1913 " (SELECT uuid FROM blob WHERE rid=forumpost.firt)," /* 4 */
1914 " wantalert.needMod" /* 5 */
1915 " FROM temp.wantalert, event, forumpost"
1916 " WHERE event.objid=substr(wantalert.eventId,2)+0"
1917 " AND eventId GLOB 'f*'"
1918 " AND forumpost.fpid=event.objid"
1919 );
1920 zFrom = db_get("email-self",0);
1921 zSub = db_get("email-subname","");
1922 while( db_step(&q)==SQLITE_ROW ){
1923 Manifest *pPost = manifest_get(db_column_int(&q,0), CFTYPE_FORUM, 0);
1924 const char *zIrt;
1925 const char *zUuid;
1926 const char *zTitle;
1927 if( pPost==0 ) continue;
1928 p = fossil_malloc( sizeof(EmailEvent) );
1929 pLast->pNext = p;
1930 pLast = p;
1931 p->type = 'f';
1932 p->needMod = db_column_int(&q, 5);
1933 p->pNext = 0;
1934 blob_init(&p->hdr, 0, 0);
1935 zUuid = db_column_text(&q, 1);
1936 zTitle = db_column_text(&q, 3);
1937 if( p->needMod ){
1938 blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n",
1939 zSub, zTitle);
1940 }else{
1941 blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle);
1942 blob_appendf(&p->hdr, "Message-Id: <%s.%s>\r\n", zUuid, zFrom);
1943 zIrt = db_column_text(&q, 4);
1944 if( zIrt && zIrt[0] ){
1945 blob_appendf(&p->hdr, "In-Reply-To: <%s.%s>\r\n", zIrt, zFrom);
1946 }
1947 }
1948 blob_init(&p->txt, 0, 0);
1949 if( p->needMod ){
1950 blob_appendf(&p->txt,
1951 "** Pending moderator approval (%s/modreq) **\n",
1952 zUrl
1953 );
1954 }
1955 blob_appendf(&p->txt,
1956 "Forum post by %s on %s\n",
1957 pPost->zUser, db_column_text(&q, 2));
1958 blob_appendf(&p->txt, "%s/forumpost/%S\n\n", zUrl, zUuid);
1959 blob_append(&p->txt, pPost->zWiki, -1);
1960 manifest_destroy(pPost);
1961 (*pnEvent)++;
1962 }
1963 db_finalize(&q);
1964
1965 return anchor.pNext;
1966 }
1967
1968 /*
1969 ** Put a header on an alert email
@@ -1953,10 +2031,14 @@
2031 blob_init(&out, 0, 0);
2032 email_header(&out);
2033 pEvent = email_compute_event_text(&nEvent, doDigest);
2034 for(p=pEvent; p; p=p->pNext){
2035 blob_append(&out, "\n", 1);
2036 if( blob_size(&p->hdr) ){
2037 blob_append(&out, blob_buffer(&p->hdr), blob_size(&p->hdr));
2038 blob_append(&out, "\n", 1);
2039 }
2040 blob_append(&out, blob_buffer(&p->txt), blob_size(&p->txt));
2041 }
2042 email_free_eventlist(pEvent);
2043 email_footer(&out);
2044 fossil_print("%s", blob_str(&out));
@@ -1965,11 +2047,11 @@
2047 }
2048
2049 /*
2050 ** COMMAND: test-add-alerts
2051 **
2052 ** Usage: %fossil test-add-alerts [OPTIONS] EVENTID ...
2053 **
2054 ** Add one or more events to the pending_alert queue. Use this
2055 ** command during testing to force email notifications for specific
2056 ** events.
2057 **
@@ -1976,27 +2058,43 @@
2058 ** EVENTIDs are text. The first character is 'c', 'f', 't', or 'w'
2059 ** for check-in, forum, ticket, or wiki. The remaining text is a
2060 ** integer that references the EVENT.OBJID value for the event.
2061 ** Run /timeline?showid to see these OBJID values.
2062 **
2063 ** Options:
2064 **
2065 ** --backoffice Run email_backoffice() after all alerts have
2066 ** been added. This will cause the alerts to be
2067 ** sent out with the SENDALERT_TRACE option.
2068 **
2069 ** --debug Like --backoffice, but add the SENDALERT_STDOUT
2070 ** so that emails are printed to standard output
2071 ** rather than being sent.
2072 **
2073 ** --digest Process emails using SENDALERT_DIGEST
2074 */
2075 void test_add_alert_cmd(void){
2076 int i;
2077 int doAuto = find_option("backoffice",0,0)!=0;
2078 unsigned mFlags = 0;
2079 if( find_option("debug",0,0)!=0 ){
2080 doAuto = 1;
2081 mFlags = SENDALERT_STDOUT;
2082 }
2083 if( find_option("digest",0,0)!=0 ){
2084 mFlags |= SENDALERT_DIGEST;
2085 }
2086 db_find_and_open_repository(0, 0);
2087 verify_all_options();
2088 db_begin_write();
2089 email_schema(0);
2090 for(i=2; i<g.argc; i++){
2091 db_multi_exec("REPLACE INTO pending_alert(eventId) VALUES(%Q)", g.argv[i]);
2092 }
2093 db_end_transaction(0);
2094 if( doAuto ){
2095 email_backoffice(SENDALERT_TRACE|mFlags);
2096 }
2097 }
2098
2099 #if INTERFACE
2100 /*
@@ -2136,22 +2234,38 @@
2234 case 't': xType = 'r'; break;
2235 case 'w': xType = 'j'; break;
2236 }
2237 if( strchr(zCap,xType)==0 ) continue;
2238 }
2239 if( blob_size(&p->hdr)>0 ){
2240 /* This alert should be sent as a separate email */
2241 Blob fhdr, fbody;
2242 blob_init(&fhdr, 0, 0);
2243 blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
2244 blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
2245 blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
2246 blob_appendf(&fbody, "\n-- \nSubscription info: %s/alerts/%s\n",
2247 zUrl, zCode);
2248 email_send(pSender,&fhdr,&fbody);
2249 blob_reset(&fhdr);
2250 blob_reset(&fbody);
2251 }else{
2252 /* Events other than forum posts are gathered together into
2253 ** a single email message */
2254 if( nHit==0 ){
2255 blob_appendf(&hdr,"To: <%s>\r\n", zEmail);
2256 blob_appendf(&hdr,"Subject: %s activity alert\r\n", zRepoName);
2257 blob_appendf(&body,
2258 "This is an automated email sent by the Fossil repository "
2259 "at %s to report changes.\n",
2260 zUrl
2261 );
2262 }
2263 nHit++;
2264 blob_append(&body, "\n", 1);
2265 blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
2266 }
2267 }
2268 if( nHit==0 ) continue;
2269 blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
2270 zUrl, zCode);
2271 email_send(pSender,&hdr,&body);
2272

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button