Fossil SCM

Improved support for one-click unsubscribe for notifications.

drh 2024-01-21 18:03 trunk
Commit bca95cbf9c2bf230efe14793f0389db34c6636d3b44b0863161a6997a6d84dcb
1 file changed +48 -26
+48 -26
--- src/alerts.c
+++ src/alerts.c
@@ -47,10 +47,11 @@
4747
@ -- The ssub field is a string where each character indicates a particular
4848
@ -- type of event to subscribe to. Choices:
4949
@ -- a - Announcements
5050
@ -- c - Check-ins
5151
@ -- f - Forum posts
52
+@ -- k - ** Special: Unsubscribed using /oneclickunsub
5253
@ -- n - New forum threads
5354
@ -- r - Replies to my own forum posts
5455
@ -- t - Ticket changes
5556
@ -- w - Wiki changes
5657
@ -- x - Edits to forum posts
@@ -86,11 +87,11 @@
8687
@ eventid TEXT PRIMARY KEY, -- Object that changed
8788
@ sentSep BOOLEAN DEFAULT false, -- individual alert sent
8889
@ sentDigest BOOLEAN DEFAULT false, -- digest alert sent
8990
@ sentMod BOOLEAN DEFAULT false -- pending moderation alert sent
9091
@ ) WITHOUT ROWID;
91
-@
92
+@
9293
@ -- Obsolete table. No longer used.
9394
@ DROP TABLE IF EXISTS repository.alert_bounce;
9495
;
9596
9697
/*
@@ -875,11 +876,11 @@
875876
int nTo = 0;
876877
char **azTo = 0;
877878
Blob v;
878879
char *z, *zAddr;
879880
int i;
880
-
881
+
881882
email_header_value(pMsg, "to", &v);
882883
z = blob_str(&v);
883884
for(i=0; z[i]; i++){
884885
if( z[i]=='<' && (zAddr = email_copy_addr(&z[i+1],'>'))!=0 ){
885886
azTo = fossil_realloc(azTo, sizeof(azTo[0])*(nTo+1) );
@@ -889,11 +890,11 @@
889890
*pnTo = nTo;
890891
*pazTo = azTo;
891892
}
892893
893894
/*
894
-** Free a list of To addresses obtained from a prior call to
895
+** Free a list of To addresses obtained from a prior call to
895896
** email_header_to()
896897
*/
897898
void email_header_to_free(int nTo, char **azTo){
898899
int i;
899900
for(i=0; i<nTo; i++) fossil_free(azTo[i]);
@@ -915,11 +916,11 @@
915916
** Message-Id:
916917
** Content-Type:
917918
** Content-Transfer-Encoding:
918919
** MIME-Version:
919920
** Sender:
920
-**
921
+**
921922
** The caller maintains ownership of the input Blobs. This routine will
922923
** read the Blobs and send them onward to the email system, but it will
923924
** not free them.
924925
**
925926
** The Message-Id: field is added if there is not already a Message-Id
@@ -928,11 +929,11 @@
928929
** If the zFromName argument is not NULL, then it should be a human-readable
929930
** name or handle for the sender. In that case, "From:" becomes a made-up
930931
** email address based on a hash of zFromName and the domain of email-self,
931932
** and an additional "Sender:" field is inserted with the email-self
932933
** address. Downstream software might use the Sender header to set
933
-** the envelope-from address of the email. If zFromName is a NULL pointer,
934
+** the envelope-from address of the email. If zFromName is a NULL pointer,
934935
** then the "From:" is set to the email-self value and Sender is
935936
** omitted.
936937
*/
937938
void alert_send(
938939
AlertSender *p, /* Emailer context */
@@ -1045,11 +1046,11 @@
10451046
** a long-running server and will not be sending email notifications,
10461047
** then leave this setting blank.
10471048
*/
10481049
/*
10491050
** SETTING: email-admin width=40
1050
-** This is the email address for the human administrator for the system.
1051
+** This is the email address for the human administrator for the system.
10511052
** Abuse and trouble reports and password reset requests are send here.
10521053
*/
10531054
/*
10541055
** SETTING: email-subname width=16
10551056
** This is a short name used to identifies the repository in the Subject:
@@ -1080,19 +1081,19 @@
10801081
**
10811082
** email-renew-warning is the time (in days since 1970-01-01) when the
10821083
** last batch of "your subscription is about to expire" emails were
10831084
** sent out.
10841085
**
1085
-** email-renew-cutoff is normally 7 days behind email-renew-warning.
1086
+** email-renew-cutoff is normally 7 days behind email-renew-warning.
10861087
*/
10871088
/*
10881089
** SETTING: email-send-method width=5 default=off sensitive
10891090
** Determine the method used to send email. Allowed values are
10901091
** "off", "relay", "pipe", "dir", "db", and "stdout". The "off" value
10911092
** means no email is ever sent. The "relay" value means emails are sent
10921093
** to an Mail Sending Agent using SMTP located at email-send-relayhost.
1093
-** The "pipe" value means email messages are piped into a command
1094
+** The "pipe" value means email messages are piped into a command
10941095
** determined by the email-send-command setting. The "dir" value means
10951096
** emails are written to individual files in a directory determined
10961097
** by the email-send-dir setting. The "db" value means that emails
10971098
** are added to an SQLite database named by the* email-send-db setting.
10981099
** The "stdout" value writes email text to standard output, for debugging.
@@ -1133,11 +1134,11 @@
11331134
*/
11341135
11351136
11361137
/*
11371138
** COMMAND: alerts*
1138
-**
1139
+**
11391140
** Usage: %fossil alerts SUBCOMMAND ARGS...
11401141
**
11411142
** Subcommands:
11421143
**
11431144
** pending Show all pending alerts. Useful for debugging.
@@ -1742,11 +1743,11 @@
17421743
/*
17431744
** Either shutdown or completely delete a subscription entry given
17441745
** by the hex value zName. Then paint a webpage that explains that
17451746
** the entry has been removed.
17461747
*/
1747
-static void alert_unsubscribe(int sid){
1748
+static void alert_unsubscribe(int sid, int bTotal){
17481749
const char *zEmail = 0;
17491750
const char *zLogin = 0;
17501751
int uid = 0;
17511752
Stmt q;
17521753
db_prepare(&q, "SELECT semail, suname FROM subscriber"
@@ -1759,13 +1760,24 @@
17591760
style_set_current_feature("alerts");
17601761
if( zEmail==0 ){
17611762
style_header("Unsubscribe Fail");
17621763
@ <p>Unable to locate a subscriber with the requested key</p>
17631764
}else{
1764
- db_multi_exec(
1765
- "DELETE FROM subscriber WHERE subscriberId=%d", sid
1766
- );
1765
+ db_unprotect(PROTECT_READONLY);
1766
+ if( bTotal ){
1767
+ /* Completely delete the subscriber */
1768
+ db_multi_exec(
1769
+ "DELETE FROM subscriber WHERE subscriberId=%d", sid
1770
+ );
1771
+ }else{
1772
+ /* Keep the subscriber, but turn off all notifications */
1773
+ db_multi_exec(
1774
+ "UPDATE subscriber SET ssub='k', mtime=now() WHERE subscriberId=%d",
1775
+ sid
1776
+ );
1777
+ }
1778
+ db_protect_pop();
17671779
style_header("Unsubscribed");
17681780
@ <p>The "%h(zEmail)" email address has been unsubscribed from all
17691781
@ notifications. All subscription records for "%h(zEmail)" have
17701782
@ been purged. No further emails will be sent to "%h(zEmail)".</p>
17711783
if( uid && g.perm.Admin ){
@@ -1793,11 +1805,11 @@
17931805
** that constitutes verification of the email address.
17941806
**
17951807
** * The sid= query parameter contains an integer subscriberId.
17961808
** This only works for the administrator. It allows the
17971809
** administrator to edit any subscription.
1798
-**
1810
+**
17991811
** * The user is logged into an account other than "nobody" or
18001812
** "anonymous". In that case the notification settings
18011813
** associated with that account can be edited without needing
18021814
** to know the subscriber code.
18031815
**
@@ -1923,13 +1935,13 @@
19231935
if( !PB("dodelete") ){
19241936
eErr = 9;
19251937
zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
19261938
" unsubscribe");
19271939
}else{
1928
- alert_unsubscribe(sid);
1940
+ alert_unsubscribe(sid, 1);
19291941
db_commit_transaction();
1930
- return;
1942
+ return;
19311943
}
19321944
}
19331945
style_set_current_feature("alerts");
19341946
style_header("Update Subscription");
19351947
db_prepare(&q,
@@ -2088,10 +2100,14 @@
20882100
if( g.perm.RdWiki ){
20892101
@ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
20902102
@ Wiki</label>
20912103
}
20922104
@ </td></tr>
2105
+ if( strchr(ssub,'k')!=0 ){
2106
+ @ <tr><td></td><td>&nbsp;&uarr;&nbsp;
2107
+ @ Note: User did a one-click unsubscribe</td></tr>
2108
+ }
20932109
@ <tr>
20942110
@ <td class="form_label">Delivery:</td>
20952111
@ <td><select size="1" name="sdigest">
20962112
@ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
20972113
@ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
@@ -2182,11 +2198,11 @@
21822198
21832199
21842200
/* This is the message that gets sent to describe how to change
21852201
** or modify a subscription
21862202
*/
2187
-static const char zUnsubMsg[] =
2203
+static const char zUnsubMsg[] =
21882204
@ To changes your subscription settings at %s visit this link:
21892205
@
21902206
@ %s/alerts/%s
21912207
@
21922208
@ To completely unsubscribe from %s, visit the following link:
@@ -2194,10 +2210,11 @@
21942210
@ %s/unsubscribe/%s
21952211
;
21962212
21972213
/*
21982214
** WEBPAGE: unsubscribe
2215
+** WEBPAGE: oneclickunsub
21992216
**
22002217
** Users visit this page to be delisted from email alerts.
22012218
**
22022219
** If a valid subscriber code is supplied in the name= query parameter,
22032220
** then that subscriber is delisted.
@@ -2206,10 +2223,13 @@
22062223
** to the /alerts page where they have an unsubscribe button.
22072224
**
22082225
** Non-logged-in users with no name= query parameter are invited to enter
22092226
** an email address to which will be sent the unsubscribe link that
22102227
** contains the correct subscriber code.
2228
+**
2229
+** The /unsubscribe page requires comfirmation. The /oneclickunsub
2230
+** page unsubscribes immediately without any need to confirm.
22112231
*/
22122232
void unsubscribe_page(void){
22132233
const char *zName = P("name");
22142234
char *zErr = 0;
22152235
int eErr = 0;
@@ -2223,19 +2243,21 @@
22232243
int sid = 0;
22242244
22252245
if( zName==0 ) zName = P("scode");
22262246
22272247
/* If a valid subscriber code is supplied, then either present the user
2228
- ** with a confirmation, or if already confirmed, unsubscribe immediately.
2248
+ ** with a comformation, or if already confirmed, unsubscribe immediately.
22292249
*/
2230
- if( zName
2250
+ if( zName
22312251
&& (sid = db_int(0, "SELECT subscriberId FROM subscriber"
22322252
" WHERE subscriberCode=hextoblob(%Q)", zName))!=0
22332253
){
22342254
char *zUnsubName = mprintf("confirm%04x", sid);
22352255
if( P(zUnsubName)!=0 ){
2236
- alert_unsubscribe(sid);
2256
+ alert_unsubscribe(sid, 1);
2257
+ }else if( sqlite3_strglob("*oneclick*",g.zPath)==0 ){
2258
+ alert_unsubscribe(sid, 0);
22372259
}else if( P("manage")!=0 ){
22382260
cgi_redirectf("%R/alerts/%s", zName);
22392261
}else{
22402262
style_header("Unsubscribe");
22412263
form_begin(0, "%R/unsubscribe");
@@ -2312,11 +2334,11 @@
23122334
@ unsubscribe and/or modify your subscription settings</p>
23132335
}
23142336
alert_sender_free(pSender);
23152337
style_finish_page();
23162338
return;
2317
- }
2339
+ }
23182340
23192341
/* Non-logged-in users have to enter an email address to which is
23202342
** sent a message containing the unsubscribe link.
23212343
*/
23222344
style_header("Unsubscribe Request");
@@ -2721,11 +2743,11 @@
27212743
if( p->needMod ){
27222744
blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n",
27232745
zSub, zTitle);
27242746
}else{
27252747
blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle);
2726
- blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n",
2748
+ blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n",
27272749
zUuid, alert_hostname(zFrom));
27282750
zIrt = db_column_text(&q, 4);
27292751
if( zIrt && zIrt[0] ){
27302752
blob_appendf(&p->hdr, "In-Reply-To: <%.32s@%s>\r\n",
27312753
zIrt, alert_hostname(zFrom));
@@ -3148,11 +3170,11 @@
31483170
Blob fhdr, fbody;
31493171
blob_init(&fhdr, 0, 0);
31503172
blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
31513173
blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
31523174
blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
3153
- blob_appendf(&fhdr, "List-Unsubscribe: <%s/unsubscribe/%s>\r\n",
3175
+ blob_appendf(&fhdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
31543176
zUrl, zCode);
31553177
blob_appendf(&fhdr,
31563178
"List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
31573179
blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
31583180
zUrl, zCode);
@@ -3178,11 +3200,11 @@
31783200
blob_append(&body, "\n", 1);
31793201
blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
31803202
}
31813203
}
31823204
if( nHit==0 ) continue;
3183
- blob_appendf(&hdr, "List-Unsubscribe: <%s/unsubscribe/%s>\r\n",
3205
+ blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
31843206
zUrl, zCode);
31853207
blob_appendf(&hdr, "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
31863208
blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
31873209
zUrl, zCode);
31883210
alert_send(pSender,&hdr,&body,0);
@@ -3230,11 +3252,11 @@
32303252
);
32313253
while( db_step(&q)==SQLITE_ROW ){
32323254
Blob hdr, body;
32333255
blob_init(&hdr, 0, 0);
32343256
blob_init(&body, 0, 0);
3235
- alert_renewal_msg(&hdr, &body,
3257
+ alert_renewal_msg(&hdr, &body,
32363258
db_column_text(&q,0),
32373259
db_column_int(&q,1),
32383260
db_column_text(&q,2),
32393261
db_column_text(&q,3),
32403262
zRepoName, zUrl);
@@ -3300,11 +3322,11 @@
33003322
style_header("Outbound Email Disabled");
33013323
@ <p>Outbound email is disabled on this repository
33023324
style_finish_page();
33033325
return;
33043326
}
3305
- if( P("submit")!=0
3327
+ if( P("submit")!=0
33063328
&& P("subject")!=0
33073329
&& P("msg")!=0
33083330
&& P("from")!=0
33093331
&& cgi_csrf_safe(2)
33103332
&& captcha_is_correct(0)
33113333
--- src/alerts.c
+++ src/alerts.c
@@ -47,10 +47,11 @@
47 @ -- The ssub field is a string where each character indicates a particular
48 @ -- type of event to subscribe to. Choices:
49 @ -- a - Announcements
50 @ -- c - Check-ins
51 @ -- f - Forum posts
 
52 @ -- n - New forum threads
53 @ -- r - Replies to my own forum posts
54 @ -- t - Ticket changes
55 @ -- w - Wiki changes
56 @ -- x - Edits to forum posts
@@ -86,11 +87,11 @@
86 @ eventid TEXT PRIMARY KEY, -- Object that changed
87 @ sentSep BOOLEAN DEFAULT false, -- individual alert sent
88 @ sentDigest BOOLEAN DEFAULT false, -- digest alert sent
89 @ sentMod BOOLEAN DEFAULT false -- pending moderation alert sent
90 @ ) WITHOUT ROWID;
91 @
92 @ -- Obsolete table. No longer used.
93 @ DROP TABLE IF EXISTS repository.alert_bounce;
94 ;
95
96 /*
@@ -875,11 +876,11 @@
875 int nTo = 0;
876 char **azTo = 0;
877 Blob v;
878 char *z, *zAddr;
879 int i;
880
881 email_header_value(pMsg, "to", &v);
882 z = blob_str(&v);
883 for(i=0; z[i]; i++){
884 if( z[i]=='<' && (zAddr = email_copy_addr(&z[i+1],'>'))!=0 ){
885 azTo = fossil_realloc(azTo, sizeof(azTo[0])*(nTo+1) );
@@ -889,11 +890,11 @@
889 *pnTo = nTo;
890 *pazTo = azTo;
891 }
892
893 /*
894 ** Free a list of To addresses obtained from a prior call to
895 ** email_header_to()
896 */
897 void email_header_to_free(int nTo, char **azTo){
898 int i;
899 for(i=0; i<nTo; i++) fossil_free(azTo[i]);
@@ -915,11 +916,11 @@
915 ** Message-Id:
916 ** Content-Type:
917 ** Content-Transfer-Encoding:
918 ** MIME-Version:
919 ** Sender:
920 **
921 ** The caller maintains ownership of the input Blobs. This routine will
922 ** read the Blobs and send them onward to the email system, but it will
923 ** not free them.
924 **
925 ** The Message-Id: field is added if there is not already a Message-Id
@@ -928,11 +929,11 @@
928 ** If the zFromName argument is not NULL, then it should be a human-readable
929 ** name or handle for the sender. In that case, "From:" becomes a made-up
930 ** email address based on a hash of zFromName and the domain of email-self,
931 ** and an additional "Sender:" field is inserted with the email-self
932 ** address. Downstream software might use the Sender header to set
933 ** the envelope-from address of the email. If zFromName is a NULL pointer,
934 ** then the "From:" is set to the email-self value and Sender is
935 ** omitted.
936 */
937 void alert_send(
938 AlertSender *p, /* Emailer context */
@@ -1045,11 +1046,11 @@
1045 ** a long-running server and will not be sending email notifications,
1046 ** then leave this setting blank.
1047 */
1048 /*
1049 ** SETTING: email-admin width=40
1050 ** This is the email address for the human administrator for the system.
1051 ** Abuse and trouble reports and password reset requests are send here.
1052 */
1053 /*
1054 ** SETTING: email-subname width=16
1055 ** This is a short name used to identifies the repository in the Subject:
@@ -1080,19 +1081,19 @@
1080 **
1081 ** email-renew-warning is the time (in days since 1970-01-01) when the
1082 ** last batch of "your subscription is about to expire" emails were
1083 ** sent out.
1084 **
1085 ** email-renew-cutoff is normally 7 days behind email-renew-warning.
1086 */
1087 /*
1088 ** SETTING: email-send-method width=5 default=off sensitive
1089 ** Determine the method used to send email. Allowed values are
1090 ** "off", "relay", "pipe", "dir", "db", and "stdout". The "off" value
1091 ** means no email is ever sent. The "relay" value means emails are sent
1092 ** to an Mail Sending Agent using SMTP located at email-send-relayhost.
1093 ** The "pipe" value means email messages are piped into a command
1094 ** determined by the email-send-command setting. The "dir" value means
1095 ** emails are written to individual files in a directory determined
1096 ** by the email-send-dir setting. The "db" value means that emails
1097 ** are added to an SQLite database named by the* email-send-db setting.
1098 ** The "stdout" value writes email text to standard output, for debugging.
@@ -1133,11 +1134,11 @@
1133 */
1134
1135
1136 /*
1137 ** COMMAND: alerts*
1138 **
1139 ** Usage: %fossil alerts SUBCOMMAND ARGS...
1140 **
1141 ** Subcommands:
1142 **
1143 ** pending Show all pending alerts. Useful for debugging.
@@ -1742,11 +1743,11 @@
1742 /*
1743 ** Either shutdown or completely delete a subscription entry given
1744 ** by the hex value zName. Then paint a webpage that explains that
1745 ** the entry has been removed.
1746 */
1747 static void alert_unsubscribe(int sid){
1748 const char *zEmail = 0;
1749 const char *zLogin = 0;
1750 int uid = 0;
1751 Stmt q;
1752 db_prepare(&q, "SELECT semail, suname FROM subscriber"
@@ -1759,13 +1760,24 @@
1759 style_set_current_feature("alerts");
1760 if( zEmail==0 ){
1761 style_header("Unsubscribe Fail");
1762 @ <p>Unable to locate a subscriber with the requested key</p>
1763 }else{
1764 db_multi_exec(
1765 "DELETE FROM subscriber WHERE subscriberId=%d", sid
1766 );
 
 
 
 
 
 
 
 
 
 
 
1767 style_header("Unsubscribed");
1768 @ <p>The "%h(zEmail)" email address has been unsubscribed from all
1769 @ notifications. All subscription records for "%h(zEmail)" have
1770 @ been purged. No further emails will be sent to "%h(zEmail)".</p>
1771 if( uid && g.perm.Admin ){
@@ -1793,11 +1805,11 @@
1793 ** that constitutes verification of the email address.
1794 **
1795 ** * The sid= query parameter contains an integer subscriberId.
1796 ** This only works for the administrator. It allows the
1797 ** administrator to edit any subscription.
1798 **
1799 ** * The user is logged into an account other than "nobody" or
1800 ** "anonymous". In that case the notification settings
1801 ** associated with that account can be edited without needing
1802 ** to know the subscriber code.
1803 **
@@ -1923,13 +1935,13 @@
1923 if( !PB("dodelete") ){
1924 eErr = 9;
1925 zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
1926 " unsubscribe");
1927 }else{
1928 alert_unsubscribe(sid);
1929 db_commit_transaction();
1930 return;
1931 }
1932 }
1933 style_set_current_feature("alerts");
1934 style_header("Update Subscription");
1935 db_prepare(&q,
@@ -2088,10 +2100,14 @@
2088 if( g.perm.RdWiki ){
2089 @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
2090 @ Wiki</label>
2091 }
2092 @ </td></tr>
 
 
 
 
2093 @ <tr>
2094 @ <td class="form_label">Delivery:</td>
2095 @ <td><select size="1" name="sdigest">
2096 @ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
2097 @ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
@@ -2182,11 +2198,11 @@
2182
2183
2184 /* This is the message that gets sent to describe how to change
2185 ** or modify a subscription
2186 */
2187 static const char zUnsubMsg[] =
2188 @ To changes your subscription settings at %s visit this link:
2189 @
2190 @ %s/alerts/%s
2191 @
2192 @ To completely unsubscribe from %s, visit the following link:
@@ -2194,10 +2210,11 @@
2194 @ %s/unsubscribe/%s
2195 ;
2196
2197 /*
2198 ** WEBPAGE: unsubscribe
 
2199 **
2200 ** Users visit this page to be delisted from email alerts.
2201 **
2202 ** If a valid subscriber code is supplied in the name= query parameter,
2203 ** then that subscriber is delisted.
@@ -2206,10 +2223,13 @@
2206 ** to the /alerts page where they have an unsubscribe button.
2207 **
2208 ** Non-logged-in users with no name= query parameter are invited to enter
2209 ** an email address to which will be sent the unsubscribe link that
2210 ** contains the correct subscriber code.
 
 
 
2211 */
2212 void unsubscribe_page(void){
2213 const char *zName = P("name");
2214 char *zErr = 0;
2215 int eErr = 0;
@@ -2223,19 +2243,21 @@
2223 int sid = 0;
2224
2225 if( zName==0 ) zName = P("scode");
2226
2227 /* If a valid subscriber code is supplied, then either present the user
2228 ** with a confirmation, or if already confirmed, unsubscribe immediately.
2229 */
2230 if( zName
2231 && (sid = db_int(0, "SELECT subscriberId FROM subscriber"
2232 " WHERE subscriberCode=hextoblob(%Q)", zName))!=0
2233 ){
2234 char *zUnsubName = mprintf("confirm%04x", sid);
2235 if( P(zUnsubName)!=0 ){
2236 alert_unsubscribe(sid);
 
 
2237 }else if( P("manage")!=0 ){
2238 cgi_redirectf("%R/alerts/%s", zName);
2239 }else{
2240 style_header("Unsubscribe");
2241 form_begin(0, "%R/unsubscribe");
@@ -2312,11 +2334,11 @@
2312 @ unsubscribe and/or modify your subscription settings</p>
2313 }
2314 alert_sender_free(pSender);
2315 style_finish_page();
2316 return;
2317 }
2318
2319 /* Non-logged-in users have to enter an email address to which is
2320 ** sent a message containing the unsubscribe link.
2321 */
2322 style_header("Unsubscribe Request");
@@ -2721,11 +2743,11 @@
2721 if( p->needMod ){
2722 blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n",
2723 zSub, zTitle);
2724 }else{
2725 blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle);
2726 blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n",
2727 zUuid, alert_hostname(zFrom));
2728 zIrt = db_column_text(&q, 4);
2729 if( zIrt && zIrt[0] ){
2730 blob_appendf(&p->hdr, "In-Reply-To: <%.32s@%s>\r\n",
2731 zIrt, alert_hostname(zFrom));
@@ -3148,11 +3170,11 @@
3148 Blob fhdr, fbody;
3149 blob_init(&fhdr, 0, 0);
3150 blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
3151 blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
3152 blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
3153 blob_appendf(&fhdr, "List-Unsubscribe: <%s/unsubscribe/%s>\r\n",
3154 zUrl, zCode);
3155 blob_appendf(&fhdr,
3156 "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3157 blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
3158 zUrl, zCode);
@@ -3178,11 +3200,11 @@
3178 blob_append(&body, "\n", 1);
3179 blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
3180 }
3181 }
3182 if( nHit==0 ) continue;
3183 blob_appendf(&hdr, "List-Unsubscribe: <%s/unsubscribe/%s>\r\n",
3184 zUrl, zCode);
3185 blob_appendf(&hdr, "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3186 blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
3187 zUrl, zCode);
3188 alert_send(pSender,&hdr,&body,0);
@@ -3230,11 +3252,11 @@
3230 );
3231 while( db_step(&q)==SQLITE_ROW ){
3232 Blob hdr, body;
3233 blob_init(&hdr, 0, 0);
3234 blob_init(&body, 0, 0);
3235 alert_renewal_msg(&hdr, &body,
3236 db_column_text(&q,0),
3237 db_column_int(&q,1),
3238 db_column_text(&q,2),
3239 db_column_text(&q,3),
3240 zRepoName, zUrl);
@@ -3300,11 +3322,11 @@
3300 style_header("Outbound Email Disabled");
3301 @ <p>Outbound email is disabled on this repository
3302 style_finish_page();
3303 return;
3304 }
3305 if( P("submit")!=0
3306 && P("subject")!=0
3307 && P("msg")!=0
3308 && P("from")!=0
3309 && cgi_csrf_safe(2)
3310 && captcha_is_correct(0)
3311
--- src/alerts.c
+++ src/alerts.c
@@ -47,10 +47,11 @@
47 @ -- The ssub field is a string where each character indicates a particular
48 @ -- type of event to subscribe to. Choices:
49 @ -- a - Announcements
50 @ -- c - Check-ins
51 @ -- f - Forum posts
52 @ -- k - ** Special: Unsubscribed using /oneclickunsub
53 @ -- n - New forum threads
54 @ -- r - Replies to my own forum posts
55 @ -- t - Ticket changes
56 @ -- w - Wiki changes
57 @ -- x - Edits to forum posts
@@ -86,11 +87,11 @@
87 @ eventid TEXT PRIMARY KEY, -- Object that changed
88 @ sentSep BOOLEAN DEFAULT false, -- individual alert sent
89 @ sentDigest BOOLEAN DEFAULT false, -- digest alert sent
90 @ sentMod BOOLEAN DEFAULT false -- pending moderation alert sent
91 @ ) WITHOUT ROWID;
92 @
93 @ -- Obsolete table. No longer used.
94 @ DROP TABLE IF EXISTS repository.alert_bounce;
95 ;
96
97 /*
@@ -875,11 +876,11 @@
876 int nTo = 0;
877 char **azTo = 0;
878 Blob v;
879 char *z, *zAddr;
880 int i;
881
882 email_header_value(pMsg, "to", &v);
883 z = blob_str(&v);
884 for(i=0; z[i]; i++){
885 if( z[i]=='<' && (zAddr = email_copy_addr(&z[i+1],'>'))!=0 ){
886 azTo = fossil_realloc(azTo, sizeof(azTo[0])*(nTo+1) );
@@ -889,11 +890,11 @@
890 *pnTo = nTo;
891 *pazTo = azTo;
892 }
893
894 /*
895 ** Free a list of To addresses obtained from a prior call to
896 ** email_header_to()
897 */
898 void email_header_to_free(int nTo, char **azTo){
899 int i;
900 for(i=0; i<nTo; i++) fossil_free(azTo[i]);
@@ -915,11 +916,11 @@
916 ** Message-Id:
917 ** Content-Type:
918 ** Content-Transfer-Encoding:
919 ** MIME-Version:
920 ** Sender:
921 **
922 ** The caller maintains ownership of the input Blobs. This routine will
923 ** read the Blobs and send them onward to the email system, but it will
924 ** not free them.
925 **
926 ** The Message-Id: field is added if there is not already a Message-Id
@@ -928,11 +929,11 @@
929 ** If the zFromName argument is not NULL, then it should be a human-readable
930 ** name or handle for the sender. In that case, "From:" becomes a made-up
931 ** email address based on a hash of zFromName and the domain of email-self,
932 ** and an additional "Sender:" field is inserted with the email-self
933 ** address. Downstream software might use the Sender header to set
934 ** the envelope-from address of the email. If zFromName is a NULL pointer,
935 ** then the "From:" is set to the email-self value and Sender is
936 ** omitted.
937 */
938 void alert_send(
939 AlertSender *p, /* Emailer context */
@@ -1045,11 +1046,11 @@
1046 ** a long-running server and will not be sending email notifications,
1047 ** then leave this setting blank.
1048 */
1049 /*
1050 ** SETTING: email-admin width=40
1051 ** This is the email address for the human administrator for the system.
1052 ** Abuse and trouble reports and password reset requests are send here.
1053 */
1054 /*
1055 ** SETTING: email-subname width=16
1056 ** This is a short name used to identifies the repository in the Subject:
@@ -1080,19 +1081,19 @@
1081 **
1082 ** email-renew-warning is the time (in days since 1970-01-01) when the
1083 ** last batch of "your subscription is about to expire" emails were
1084 ** sent out.
1085 **
1086 ** email-renew-cutoff is normally 7 days behind email-renew-warning.
1087 */
1088 /*
1089 ** SETTING: email-send-method width=5 default=off sensitive
1090 ** Determine the method used to send email. Allowed values are
1091 ** "off", "relay", "pipe", "dir", "db", and "stdout". The "off" value
1092 ** means no email is ever sent. The "relay" value means emails are sent
1093 ** to an Mail Sending Agent using SMTP located at email-send-relayhost.
1094 ** The "pipe" value means email messages are piped into a command
1095 ** determined by the email-send-command setting. The "dir" value means
1096 ** emails are written to individual files in a directory determined
1097 ** by the email-send-dir setting. The "db" value means that emails
1098 ** are added to an SQLite database named by the* email-send-db setting.
1099 ** The "stdout" value writes email text to standard output, for debugging.
@@ -1133,11 +1134,11 @@
1134 */
1135
1136
1137 /*
1138 ** COMMAND: alerts*
1139 **
1140 ** Usage: %fossil alerts SUBCOMMAND ARGS...
1141 **
1142 ** Subcommands:
1143 **
1144 ** pending Show all pending alerts. Useful for debugging.
@@ -1742,11 +1743,11 @@
1743 /*
1744 ** Either shutdown or completely delete a subscription entry given
1745 ** by the hex value zName. Then paint a webpage that explains that
1746 ** the entry has been removed.
1747 */
1748 static void alert_unsubscribe(int sid, int bTotal){
1749 const char *zEmail = 0;
1750 const char *zLogin = 0;
1751 int uid = 0;
1752 Stmt q;
1753 db_prepare(&q, "SELECT semail, suname FROM subscriber"
@@ -1759,13 +1760,24 @@
1760 style_set_current_feature("alerts");
1761 if( zEmail==0 ){
1762 style_header("Unsubscribe Fail");
1763 @ <p>Unable to locate a subscriber with the requested key</p>
1764 }else{
1765 db_unprotect(PROTECT_READONLY);
1766 if( bTotal ){
1767 /* Completely delete the subscriber */
1768 db_multi_exec(
1769 "DELETE FROM subscriber WHERE subscriberId=%d", sid
1770 );
1771 }else{
1772 /* Keep the subscriber, but turn off all notifications */
1773 db_multi_exec(
1774 "UPDATE subscriber SET ssub='k', mtime=now() WHERE subscriberId=%d",
1775 sid
1776 );
1777 }
1778 db_protect_pop();
1779 style_header("Unsubscribed");
1780 @ <p>The "%h(zEmail)" email address has been unsubscribed from all
1781 @ notifications. All subscription records for "%h(zEmail)" have
1782 @ been purged. No further emails will be sent to "%h(zEmail)".</p>
1783 if( uid && g.perm.Admin ){
@@ -1793,11 +1805,11 @@
1805 ** that constitutes verification of the email address.
1806 **
1807 ** * The sid= query parameter contains an integer subscriberId.
1808 ** This only works for the administrator. It allows the
1809 ** administrator to edit any subscription.
1810 **
1811 ** * The user is logged into an account other than "nobody" or
1812 ** "anonymous". In that case the notification settings
1813 ** associated with that account can be edited without needing
1814 ** to know the subscriber code.
1815 **
@@ -1923,13 +1935,13 @@
1935 if( !PB("dodelete") ){
1936 eErr = 9;
1937 zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
1938 " unsubscribe");
1939 }else{
1940 alert_unsubscribe(sid, 1);
1941 db_commit_transaction();
1942 return;
1943 }
1944 }
1945 style_set_current_feature("alerts");
1946 style_header("Update Subscription");
1947 db_prepare(&q,
@@ -2088,10 +2100,14 @@
2100 if( g.perm.RdWiki ){
2101 @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
2102 @ Wiki</label>
2103 }
2104 @ </td></tr>
2105 if( strchr(ssub,'k')!=0 ){
2106 @ <tr><td></td><td>&nbsp;&uarr;&nbsp;
2107 @ Note: User did a one-click unsubscribe</td></tr>
2108 }
2109 @ <tr>
2110 @ <td class="form_label">Delivery:</td>
2111 @ <td><select size="1" name="sdigest">
2112 @ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
2113 @ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
@@ -2182,11 +2198,11 @@
2198
2199
2200 /* This is the message that gets sent to describe how to change
2201 ** or modify a subscription
2202 */
2203 static const char zUnsubMsg[] =
2204 @ To changes your subscription settings at %s visit this link:
2205 @
2206 @ %s/alerts/%s
2207 @
2208 @ To completely unsubscribe from %s, visit the following link:
@@ -2194,10 +2210,11 @@
2210 @ %s/unsubscribe/%s
2211 ;
2212
2213 /*
2214 ** WEBPAGE: unsubscribe
2215 ** WEBPAGE: oneclickunsub
2216 **
2217 ** Users visit this page to be delisted from email alerts.
2218 **
2219 ** If a valid subscriber code is supplied in the name= query parameter,
2220 ** then that subscriber is delisted.
@@ -2206,10 +2223,13 @@
2223 ** to the /alerts page where they have an unsubscribe button.
2224 **
2225 ** Non-logged-in users with no name= query parameter are invited to enter
2226 ** an email address to which will be sent the unsubscribe link that
2227 ** contains the correct subscriber code.
2228 **
2229 ** The /unsubscribe page requires comfirmation. The /oneclickunsub
2230 ** page unsubscribes immediately without any need to confirm.
2231 */
2232 void unsubscribe_page(void){
2233 const char *zName = P("name");
2234 char *zErr = 0;
2235 int eErr = 0;
@@ -2223,19 +2243,21 @@
2243 int sid = 0;
2244
2245 if( zName==0 ) zName = P("scode");
2246
2247 /* If a valid subscriber code is supplied, then either present the user
2248 ** with a comformation, or if already confirmed, unsubscribe immediately.
2249 */
2250 if( zName
2251 && (sid = db_int(0, "SELECT subscriberId FROM subscriber"
2252 " WHERE subscriberCode=hextoblob(%Q)", zName))!=0
2253 ){
2254 char *zUnsubName = mprintf("confirm%04x", sid);
2255 if( P(zUnsubName)!=0 ){
2256 alert_unsubscribe(sid, 1);
2257 }else if( sqlite3_strglob("*oneclick*",g.zPath)==0 ){
2258 alert_unsubscribe(sid, 0);
2259 }else if( P("manage")!=0 ){
2260 cgi_redirectf("%R/alerts/%s", zName);
2261 }else{
2262 style_header("Unsubscribe");
2263 form_begin(0, "%R/unsubscribe");
@@ -2312,11 +2334,11 @@
2334 @ unsubscribe and/or modify your subscription settings</p>
2335 }
2336 alert_sender_free(pSender);
2337 style_finish_page();
2338 return;
2339 }
2340
2341 /* Non-logged-in users have to enter an email address to which is
2342 ** sent a message containing the unsubscribe link.
2343 */
2344 style_header("Unsubscribe Request");
@@ -2721,11 +2743,11 @@
2743 if( p->needMod ){
2744 blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n",
2745 zSub, zTitle);
2746 }else{
2747 blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle);
2748 blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n",
2749 zUuid, alert_hostname(zFrom));
2750 zIrt = db_column_text(&q, 4);
2751 if( zIrt && zIrt[0] ){
2752 blob_appendf(&p->hdr, "In-Reply-To: <%.32s@%s>\r\n",
2753 zIrt, alert_hostname(zFrom));
@@ -3148,11 +3170,11 @@
3170 Blob fhdr, fbody;
3171 blob_init(&fhdr, 0, 0);
3172 blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
3173 blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
3174 blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
3175 blob_appendf(&fhdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3176 zUrl, zCode);
3177 blob_appendf(&fhdr,
3178 "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3179 blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
3180 zUrl, zCode);
@@ -3178,11 +3200,11 @@
3200 blob_append(&body, "\n", 1);
3201 blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
3202 }
3203 }
3204 if( nHit==0 ) continue;
3205 blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3206 zUrl, zCode);
3207 blob_appendf(&hdr, "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3208 blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
3209 zUrl, zCode);
3210 alert_send(pSender,&hdr,&body,0);
@@ -3230,11 +3252,11 @@
3252 );
3253 while( db_step(&q)==SQLITE_ROW ){
3254 Blob hdr, body;
3255 blob_init(&hdr, 0, 0);
3256 blob_init(&body, 0, 0);
3257 alert_renewal_msg(&hdr, &body,
3258 db_column_text(&q,0),
3259 db_column_int(&q,1),
3260 db_column_text(&q,2),
3261 db_column_text(&q,3),
3262 zRepoName, zUrl);
@@ -3300,11 +3322,11 @@
3322 style_header("Outbound Email Disabled");
3323 @ <p>Outbound email is disabled on this repository
3324 style_finish_page();
3325 return;
3326 }
3327 if( P("submit")!=0
3328 && P("subject")!=0
3329 && P("msg")!=0
3330 && P("from")!=0
3331 && cgi_csrf_safe(2)
3332 && captcha_is_correct(0)
3333

Keyboard Shortcuts

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