Diff
Not logged in

Differences From Artifact [e1beb31cbd]:

To Artifact [a3ba9fb490]:


45
46
47
48
49
50
51

52
53
54
55
56
57
58
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59







+







@ -- to the USER entry.
@ --
@ -- The ssub field is a string where each character indicates a particular
@ -- type of event to subscribe to.  Choices:
@ --     a - Announcements
@ --     c - Check-ins
@ --     f - Forum posts
@ --     k - ** Special: Unsubscribed using /oneclickunsub
@ --     n - New forum threads
@ --     r - Replies to my own forum posts
@ --     t - Ticket changes
@ --     w - Wiki changes
@ --     x - Edits to forum posts
@ -- Probably different codes will be added in the future.  In the future
@ -- we might also add a separate table that allows subscribing to email
84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
85
86
87
88
89
90
91

92
93
94
95
96
97
98
99







-
+







@ --
@ CREATE TABLE repository.pending_alert(
@   eventid TEXT PRIMARY KEY,         -- Object that changed
@   sentSep BOOLEAN DEFAULT false,    -- individual alert sent
@   sentDigest BOOLEAN DEFAULT false, -- digest alert sent
@   sentMod BOOLEAN DEFAULT false     -- pending moderation alert sent
@ ) WITHOUT ROWID;
@
@ 
@ -- Obsolete table.  No longer used.
@ DROP TABLE IF EXISTS repository.alert_bounce;
;

/*
** Return true if the email notification tables exist.
*/
873
874
875
876
877
878
879
880

881
882
883
884
885
886
887
888
889
890
891
892
893
894

895
896
897
898
899
900
901
874
875
876
877
878
879
880

881
882
883
884
885
886
887
888
889
890
891
892
893
894

895
896
897
898
899
900
901
902







-
+













-
+







*/
void email_header_to(Blob *pMsg, int *pnTo, char ***pazTo){
  int nTo = 0;
  char **azTo = 0;
  Blob v;
  char *z, *zAddr;
  int i;

  
  email_header_value(pMsg, "to", &v);
  z = blob_str(&v);
  for(i=0; z[i]; i++){
    if( z[i]=='<' && (zAddr = email_copy_addr(&z[i+1],'>'))!=0 ){
      azTo = fossil_realloc(azTo, sizeof(azTo[0])*(nTo+1) );
      azTo[nTo++] = zAddr;
    }
  }
  *pnTo = nTo;
  *pazTo = azTo;
}

/*
** Free a list of To addresses obtained from a prior call to
** Free a list of To addresses obtained from a prior call to 
** email_header_to()
*/
void email_header_to_free(int nTo, char **azTo){
  int i;
  for(i=0; i<nTo; i++) fossil_free(azTo[i]);
  fossil_free(azTo);
}
913
914
915
916
917
918
919
920

921
922
923
924
925
926
927
928
929
930
931
932
933

934
935
936
937
938
939
940
914
915
916
917
918
919
920

921
922
923
924
925
926
927
928
929
930
931
932
933

934
935
936
937
938
939
940
941







-
+












-
+







**     From:
**     Date:
**     Message-Id:
**     Content-Type:
**     Content-Transfer-Encoding:
**     MIME-Version:
**     Sender:
**
**     
** 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.
**
** The Message-Id: field is added if there is not already a Message-Id
** in the pHdr parameter.
**
** If the zFromName argument is not NULL, then it should be a human-readable
** name or handle for the sender.  In that case, "From:" becomes a made-up
** email address based on a hash of zFromName and the domain of email-self,
** and an additional "Sender:" field is inserted with the email-self
** address.  Downstream software might use the Sender header to set
** the envelope-from address of the email.  If zFromName is a NULL pointer,
** the envelope-from address of the email.  If zFromName is a NULL pointer, 
** then the "From:" is set to the email-self value and Sender is
** omitted.
*/
void alert_send(
  AlertSender *p,           /* Emailer context */
  Blob *pHdr,               /* Email header (incomplete) */
  Blob *pBody,              /* Email body */
1043
1044
1045
1046
1047
1048
1049
1050

1051
1052
1053
1054
1055
1056
1057
1044
1045
1046
1047
1048
1049
1050

1051
1052
1053
1054
1055
1056
1057
1058







-
+







** the basename for hyperlinks included in email alert text.
** Omit the trailing "/".  If the repository is not intended to be
** a long-running server and will not be sending email notifications,
** then leave this setting blank.
*/
/*
** SETTING: email-admin               width=40
** This is the email address for the human administrator for the system.
** This is the email address for the human administrator for the system. 
** Abuse and trouble reports and password reset requests are send here.
*/
/*
** SETTING: email-subname             width=16
** This is a short name used to identifies the repository in the Subject:
** line of email alerts. Traditionally this name is included in square
** brackets. Examples: "[fossil-src]", "[sqlite-src]".
1078
1079
1080
1081
1082
1083
1084
1085

1086
1087
1088
1089
1090
1091
1092
1093

1094
1095
1096
1097
1098
1099
1100
1079
1080
1081
1082
1083
1084
1085

1086
1087
1088
1089
1090
1091
1092
1093

1094
1095
1096
1097
1098
1099
1100
1101







-
+







-
+







** a subscription is less than email-renew-cutoff, then now new emails
** are sent to the subscriber.
**
** email-renew-warning is the time (in days since 1970-01-01) when the
** last batch of "your subscription is about to expire" emails were
** sent out.
**
** email-renew-cutoff is normally 7 days behind email-renew-warning.
** email-renew-cutoff is normally 7 days behind email-renew-warning.  
*/
/*
** SETTING: email-send-method         width=5 default=off sensitive
** Determine the method used to send email.  Allowed values are
** "off", "relay", "pipe", "dir", "db", and "stdout".  The "off" value
** means no email is ever sent.  The "relay" value means emails are sent
** to an Mail Sending Agent using SMTP located at email-send-relayhost.
** The "pipe" value means email messages are piped into a command
** The "pipe" value means email messages are piped into a command 
** determined by the email-send-command setting. The "dir" value means
** emails are written to individual files in a directory determined
** by the email-send-dir setting.  The "db" value means that emails
** are added to an SQLite database named by the* email-send-db setting.
** The "stdout" value writes email text to standard output, for debugging.
*/
/*
1131
1132
1133
1134
1135
1136
1137
1138

1139
1140
1141
1142
1143
1144
1145
1132
1133
1134
1135
1136
1137
1138

1139
1140
1141
1142
1143
1144
1145
1146







-
+







** SMTP server configured as a Mail Submission Agent listening on the
** designated host and port and all times.
*/


/*
** COMMAND: alerts*
**
** 
** Usage: %fossil alerts SUBCOMMAND ARGS...
**
** Subcommands:
**
**    pending                 Show all pending alerts.  Useful for debugging.
**
**    reset                   Hard reset of all email notification tables
1740
1741
1742
1743
1744
1745
1746
1747

1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763



1764
1765
1766











1767
1768
1769
1770
1771
1772
1773
1741
1742
1743
1744
1745
1746
1747

1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767



1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785







-
+
















+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+







}

/*
** Either shutdown or completely delete a subscription entry given
** by the hex value zName.  Then paint a webpage that explains that
** the entry has been removed.
*/
static void alert_unsubscribe(int sid){
static void alert_unsubscribe(int sid, int bTotal){
  const char *zEmail = 0;
  const char *zLogin = 0;
  int uid = 0;
  Stmt q;
  db_prepare(&q, "SELECT semail, suname FROM subscriber"
                 " WHERE subscriberId=%d", sid);
  if( db_step(&q)==SQLITE_ROW ){
    zEmail = db_column_text(&q, 0);
    zLogin = db_column_text(&q, 1);
    uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
  }
  style_set_current_feature("alerts");
  if( zEmail==0 ){
    style_header("Unsubscribe Fail");
    @ <p>Unable to locate a subscriber with the requested key</p>
  }else{
    db_unprotect(PROTECT_READONLY);
    if( bTotal ){
      /* Completely delete the subscriber */
    db_multi_exec(
      "DELETE FROM subscriber WHERE subscriberId=%d", sid
    );
      db_multi_exec(
        "DELETE FROM subscriber WHERE subscriberId=%d", sid
      );
    }else{
      /* Keep the subscriber, but turn off all notifications */
      db_multi_exec(
        "UPDATE subscriber SET ssub='k', mtime=now() WHERE subscriberId=%d",
        sid
      );
    }
    db_protect_pop();
    style_header("Unsubscribed");
    @ <p>The "%h(zEmail)" email address has been unsubscribed from all
    @ notifications.  All subscription records for "%h(zEmail)" have
    @ been purged.  No further emails will be sent to "%h(zEmail)".</p>
    if( uid && g.perm.Admin ){
       @ <p>You may also want to
       @ <a href="%R/setup_uedit?id=%d(uid)">edit or delete
1791
1792
1793
1794
1795
1796
1797
1798

1799
1800
1801
1802
1803
1804
1805
1803
1804
1805
1806
1807
1808
1809

1810
1811
1812
1813
1814
1815
1816
1817







-
+







**         email and clicks on the link in the email.  When a
**         compilete subscriberCode is seen on the name= query parameter,
**         that constitutes verification of the email address.
**
**    *    The sid= query parameter contains an integer subscriberId.
**         This only works for the administrator.  It allows the
**         administrator to edit any subscription.
**
**         
**    *    The user is logged into an account other than "nobody" or
**         "anonymous".  In that case the notification settings
**         associated with that account can be edited without needing
**         to know the subscriber code.
**
**    *    The name= query parameter contains a 32-digit prefix of
**         subscriber code.  (Subscriber codes are normally 64 hex digits
1921
1922
1923
1924
1925
1926
1927
1928

1929
1930

1931
1932
1933
1934
1935
1936
1937
1933
1934
1935
1936
1937
1938
1939

1940
1941

1942
1943
1944
1945
1946
1947
1948
1949







-
+

-
+







  }
  if( P("delete")!=0 && cgi_csrf_safe(2) ){
    if( !PB("dodelete") ){
      eErr = 9;
      zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
                     " unsubscribe");
    }else{
      alert_unsubscribe(sid);
      alert_unsubscribe(sid, 1);
      db_commit_transaction();
      return;
      return; 
    }
  }
  style_set_current_feature("alerts");
  style_header("Update Subscription");
  db_prepare(&q,
    "SELECT"
    "  semail,"                       /* 0 */
2086
2087
2088
2089
2090
2091
2092




2093
2094
2095
2096
2097
2098
2099
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115







+
+
+
+







    @  Ticket changes</label><br>
  }
  if( g.perm.RdWiki ){
    @  <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
    @  Wiki</label>
  }
  @ </td></tr>
  if( strchr(ssub,'k')!=0 ){
    @ <tr><td></td><td>&nbsp;&uarr;&nbsp;
    @ Note: User did a one-click unsubscribe</td></tr>
  }
  @ <tr>
  @  <td class="form_label">Delivery:</td>
  @  <td><select size="1" name="sdigest">
  @     <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
  @     <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
  @     </select></td>
  @ </tr>
2180
2181
2182
2183
2184
2185
2186
2187

2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198

2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210



2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228

2229
2230

2231
2232
2233
2234
2235
2236



2237
2238
2239
2240
2241
2242
2243
2196
2197
2198
2199
2200
2201
2202

2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247

2248
2249

2250
2251
2252
2253
2254
2255

2256
2257
2258
2259
2260
2261
2262
2263
2264
2265







-
+











+












+
+
+

















-
+

-
+





-
+
+
+







  style_finish_page();
}


/* This is the message that gets sent to describe how to change
** or modify a subscription
*/
static const char zUnsubMsg[] =
static const char zUnsubMsg[] = 
@ To changes your subscription settings at %s visit this link:
@
@    %s/alerts/%s
@
@ To completely unsubscribe from %s, visit the following link:
@
@    %s/unsubscribe/%s
;

/*
** WEBPAGE: unsubscribe
** WEBPAGE: oneclickunsub
**
** Users visit this page to be delisted from email alerts.
**
** If a valid subscriber code is supplied in the name= query parameter,
** then that subscriber is delisted.
**
** Otherwise, If the users is logged in, then they are redirected
** to the /alerts page where they have an unsubscribe button.
**
** Non-logged-in users with no name= query parameter are invited to enter
** an email address to which will be sent the unsubscribe link that
** contains the correct subscriber code.
**
** The /unsubscribe page requires comfirmation.  The /oneclickunsub
** page unsubscribes immediately without any need to confirm.
*/
void unsubscribe_page(void){
  const char *zName = P("name");
  char *zErr = 0;
  int eErr = 0;
  unsigned int uSeed = 0;
  const char *zDecoded;
  char *zCaptcha = 0;
  int dx;
  int bSubmit;
  const char *zEAddr;
  char *zCode = 0;
  int sid = 0;

  if( zName==0 ) zName = P("scode");

  /* If a valid subscriber code is supplied, then either present the user
  ** with a confirmation, or if already confirmed, unsubscribe immediately.
  ** with a comformation, or if already confirmed, unsubscribe immediately.
  */
  if( zName
  if( zName 
   && (sid = db_int(0, "SELECT subscriberId FROM subscriber"
                       " WHERE subscriberCode=hextoblob(%Q)", zName))!=0
  ){
    char *zUnsubName = mprintf("confirm%04x", sid);
    if( P(zUnsubName)!=0 ){
      alert_unsubscribe(sid);
      alert_unsubscribe(sid, 1);
    }else if( sqlite3_strglob("*oneclick*",g.zPath)==0 ){
      alert_unsubscribe(sid, 0);
    }else if( P("manage")!=0 ){
      cgi_redirectf("%R/alerts/%s", zName);
    }else{
      style_header("Unsubscribe");
      form_begin(0, "%R/unsubscribe");
      @ <input type="hidden" name="scode" value="%h(zName)">
      @ <table border="0" cellpadding="10" width="100%%">
2310
2311
2312
2313
2314
2315
2316
2317

2318
2319
2320
2321
2322
2323
2324
2332
2333
2334
2335
2336
2337
2338

2339
2340
2341
2342
2343
2344
2345
2346







-
+







    }else{
      @ <p>An email has been sent to "%h(zEAddr)" that explains how to
      @ unsubscribe and/or modify your subscription settings</p>
    }
    alert_sender_free(pSender);
    style_finish_page();
    return;
  }
  }  

  /* Non-logged-in users have to enter an email address to which is
  ** sent a message containing the unsubscribe link.
  */
  style_header("Unsubscribe Request");
  @ <p>Fill out the form below to request an email message that will
  @ explain how to unsubscribe and/or change your subscription settings.</p>
2719
2720
2721
2722
2723
2724
2725
2726

2727
2728
2729
2730
2731
2732
2733
2741
2742
2743
2744
2745
2746
2747

2748
2749
2750
2751
2752
2753
2754
2755







-
+







    zUuid = db_column_text(&q, 1);
    zTitle = db_column_text(&q, 3);
    if( p->needMod ){
      blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n",
                   zSub, zTitle);
    }else{
      blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle);
      blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n",
      blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n", 
                   zUuid, alert_hostname(zFrom));
      zIrt = db_column_text(&q, 4);
      if( zIrt && zIrt[0] ){
        blob_appendf(&p->hdr, "In-Reply-To: <%.32s@%s>\r\n",
                     zIrt, alert_hostname(zFrom));
      }
    }
3146
3147
3148
3149
3150
3151
3152
3153

3154
3155
3156
3157
3158
3159
3160
3168
3169
3170
3171
3172
3173
3174

3175
3176
3177
3178
3179
3180
3181
3182







-
+







      if( blob_size(&p->hdr)>0 ){
        /* This alert should be sent as a separate email */
        Blob fhdr, fbody;
        blob_init(&fhdr, 0, 0);
        blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
        blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
        blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
        blob_appendf(&fhdr, "List-Unsubscribe: <%s/unsubscribe/%s>\r\n",
        blob_appendf(&fhdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
                     zUrl, zCode);
        blob_appendf(&fhdr,
                   "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
        blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
           zUrl, zCode);
        /* blob_appendf(&fbody, "Subscription settings: %s/alerts/%s\n",
        **   zUrl, zCode); */
3176
3177
3178
3179
3180
3181
3182
3183

3184
3185
3186
3187
3188
3189
3190
3198
3199
3200
3201
3202
3203
3204

3205
3206
3207
3208
3209
3210
3211
3212







-
+







        }
        nHit++;
        blob_append(&body, "\n", 1);
        blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
      }
    }
    if( nHit==0 ) continue;
    blob_appendf(&hdr, "List-Unsubscribe: <%s/unsubscribe/%s>\r\n",
    blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
         zUrl, zCode);
    blob_appendf(&hdr, "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
    blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
         zUrl, zCode);
    alert_send(pSender,&hdr,&body,0);
    nSent++;
    blob_truncate(&hdr, 0);
3228
3229
3230
3231
3232
3233
3234
3235

3236
3237
3238
3239
3240
3241
3242
3250
3251
3252
3253
3254
3255
3256

3257
3258
3259
3260
3261
3262
3263
3264







-
+







         "   AND length(sdigest)>0",
         iNewWarn, iOldWarn
      );
      while( db_step(&q)==SQLITE_ROW ){
        Blob hdr, body;
        blob_init(&hdr, 0, 0);
        blob_init(&body, 0, 0);
        alert_renewal_msg(&hdr, &body,
        alert_renewal_msg(&hdr, &body, 
           db_column_text(&q,0),
           db_column_int(&q,1),
           db_column_text(&q,2),
           db_column_text(&q,3),
           zRepoName, zUrl);
        alert_send(pSender,&hdr,&body,0);
        blob_reset(&hdr);
3298
3299
3300
3301
3302
3303
3304
3305

3306
3307
3308
3309
3310
3311
3312
3320
3321
3322
3323
3324
3325
3326

3327
3328
3329
3330
3331
3332
3333
3334







-
+







  style_set_current_feature("alerts");
  if( zAdminEmail==0 || zAdminEmail[0]==0 ){
    style_header("Outbound Email Disabled");
    @ <p>Outbound email is disabled on this repository
    style_finish_page();
    return;
  }
  if( P("submit")!=0
  if( P("submit")!=0 
   && P("subject")!=0
   && P("msg")!=0
   && P("from")!=0
   && cgi_csrf_safe(2)
   && captcha_is_correct(0)
  ){
    Blob hdr, body;