Fossil SCM
Change the email transfer encoding to quoted-printable.
Commit
b6a13c45bd0e59cc584c8f8408dd53bc3690d7225b6f7cff42042fcc29126717
Parent
65f57546110b186…
2 files changed
+19
-6
+50
-7
+19
-6
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -293,10 +293,23 @@ | ||
| 293 | 293 | } |
| 294 | 294 | memcpy(&pBlob->aData[pBlob->nUsed], aData, nData); |
| 295 | 295 | pBlob->nUsed += nData; |
| 296 | 296 | pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */ |
| 297 | 297 | } |
| 298 | + | |
| 299 | +/* | |
| 300 | +** Append a single character to the blob | |
| 301 | +*/ | |
| 302 | +void blob_append_char(Blob *pBlob, char c){ | |
| 303 | + if( pBlob->nUsed+1>pBlob->nAlloc ){ | |
| 304 | + pBlob->xRealloc(pBlob, pBlob->nUsed + pBlob->nAlloc + 100); | |
| 305 | + if( pBlob->nUsed + 1 >= pBlob->nAlloc ){ | |
| 306 | + blob_panic(); | |
| 307 | + } | |
| 308 | + } | |
| 309 | + pBlob->aData[pBlob->nUsed++] = c; | |
| 310 | +} | |
| 298 | 311 | |
| 299 | 312 | /* |
| 300 | 313 | ** Copy a blob |
| 301 | 314 | */ |
| 302 | 315 | void blob_copy(Blob *pTo, Blob *pFrom){ |
| @@ -309,11 +322,11 @@ | ||
| 309 | 322 | ** Return a pointer to a null-terminated string for a blob. |
| 310 | 323 | */ |
| 311 | 324 | char *blob_str(Blob *p){ |
| 312 | 325 | blob_is_init(p); |
| 313 | 326 | if( p->nUsed==0 ){ |
| 314 | - blob_append(p, "", 1); /* NOTE: Changes nUsed. */ | |
| 327 | + blob_append_char(p, 0); /* NOTE: Changes nUsed. */ | |
| 315 | 328 | p->nUsed = 0; |
| 316 | 329 | } |
| 317 | 330 | if( p->aData[p->nUsed]!=0 ){ |
| 318 | 331 | blob_materialize(p); |
| 319 | 332 | } |
| @@ -667,11 +680,11 @@ | ||
| 667 | 680 | ** Ensure that the text in pBlob ends with '\n' |
| 668 | 681 | */ |
| 669 | 682 | void blob_add_final_newline(Blob *pBlob){ |
| 670 | 683 | if( pBlob->nUsed<=0 ) return; |
| 671 | 684 | if( pBlob->aData[pBlob->nUsed-1]!='\n' ){ |
| 672 | - blob_append(pBlob, "\n", 1); | |
| 685 | + blob_append_char(pBlob, '\n'); | |
| 673 | 686 | } |
| 674 | 687 | } |
| 675 | 688 | |
| 676 | 689 | /* |
| 677 | 690 | ** Return true if the blob contains a valid base16 identifier artifact hash. |
| @@ -1254,16 +1267,16 @@ | ||
| 1254 | 1267 | if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){ |
| 1255 | 1268 | needEscape = 1; |
| 1256 | 1269 | } |
| 1257 | 1270 | } |
| 1258 | 1271 | if( n>0 && !fossil_isspace(z[n-1]) ){ |
| 1259 | - blob_append(pBlob, " ", 1); | |
| 1272 | + blob_append_char(pBlob, ' '); | |
| 1260 | 1273 | } |
| 1261 | - if( needEscape ) blob_append(pBlob, &cQuote, 1); | |
| 1274 | + if( needEscape ) blob_append_char(pBlob, cQuote); | |
| 1262 | 1275 | if( zIn[0]=='-' ) blob_append(pBlob, "./", 2); |
| 1263 | 1276 | blob_append(pBlob, zIn, -1); |
| 1264 | - if( needEscape ) blob_append(pBlob, &cQuote, 1); | |
| 1277 | + if( needEscape ) blob_append_char(pBlob, cQuote); | |
| 1265 | 1278 | } |
| 1266 | 1279 | |
| 1267 | 1280 | /* |
| 1268 | 1281 | ** A read(2)-like impl for the Blob class. Reads (copies) up to nLen |
| 1269 | 1282 | ** bytes from pIn, starting at position pIn->iCursor, and copies them |
| @@ -1330,11 +1343,11 @@ | ||
| 1330 | 1343 | zUtf8[i] = zUtf8[i-1]; |
| 1331 | 1344 | zUtf8[--i] = zTemp; |
| 1332 | 1345 | } |
| 1333 | 1346 | } |
| 1334 | 1347 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1335 | - blob_append(pBlob, "", 1); | |
| 1348 | + blob_append_char(pBlob, 0); | |
| 1336 | 1349 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1337 | 1350 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1338 | 1351 | blob_set_dynamic(pBlob, zUtf8); |
| 1339 | 1352 | }else if( useMbcs && invalid_utf8(pBlob) ){ |
| 1340 | 1353 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1341 | 1354 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -293,10 +293,23 @@ | |
| 293 | } |
| 294 | memcpy(&pBlob->aData[pBlob->nUsed], aData, nData); |
| 295 | pBlob->nUsed += nData; |
| 296 | pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */ |
| 297 | } |
| 298 | |
| 299 | /* |
| 300 | ** Copy a blob |
| 301 | */ |
| 302 | void blob_copy(Blob *pTo, Blob *pFrom){ |
| @@ -309,11 +322,11 @@ | |
| 309 | ** Return a pointer to a null-terminated string for a blob. |
| 310 | */ |
| 311 | char *blob_str(Blob *p){ |
| 312 | blob_is_init(p); |
| 313 | if( p->nUsed==0 ){ |
| 314 | blob_append(p, "", 1); /* NOTE: Changes nUsed. */ |
| 315 | p->nUsed = 0; |
| 316 | } |
| 317 | if( p->aData[p->nUsed]!=0 ){ |
| 318 | blob_materialize(p); |
| 319 | } |
| @@ -667,11 +680,11 @@ | |
| 667 | ** Ensure that the text in pBlob ends with '\n' |
| 668 | */ |
| 669 | void blob_add_final_newline(Blob *pBlob){ |
| 670 | if( pBlob->nUsed<=0 ) return; |
| 671 | if( pBlob->aData[pBlob->nUsed-1]!='\n' ){ |
| 672 | blob_append(pBlob, "\n", 1); |
| 673 | } |
| 674 | } |
| 675 | |
| 676 | /* |
| 677 | ** Return true if the blob contains a valid base16 identifier artifact hash. |
| @@ -1254,16 +1267,16 @@ | |
| 1254 | if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){ |
| 1255 | needEscape = 1; |
| 1256 | } |
| 1257 | } |
| 1258 | if( n>0 && !fossil_isspace(z[n-1]) ){ |
| 1259 | blob_append(pBlob, " ", 1); |
| 1260 | } |
| 1261 | if( needEscape ) blob_append(pBlob, &cQuote, 1); |
| 1262 | if( zIn[0]=='-' ) blob_append(pBlob, "./", 2); |
| 1263 | blob_append(pBlob, zIn, -1); |
| 1264 | if( needEscape ) blob_append(pBlob, &cQuote, 1); |
| 1265 | } |
| 1266 | |
| 1267 | /* |
| 1268 | ** A read(2)-like impl for the Blob class. Reads (copies) up to nLen |
| 1269 | ** bytes from pIn, starting at position pIn->iCursor, and copies them |
| @@ -1330,11 +1343,11 @@ | |
| 1330 | zUtf8[i] = zUtf8[i-1]; |
| 1331 | zUtf8[--i] = zTemp; |
| 1332 | } |
| 1333 | } |
| 1334 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1335 | blob_append(pBlob, "", 1); |
| 1336 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1337 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1338 | blob_set_dynamic(pBlob, zUtf8); |
| 1339 | }else if( useMbcs && invalid_utf8(pBlob) ){ |
| 1340 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1341 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -293,10 +293,23 @@ | |
| 293 | } |
| 294 | memcpy(&pBlob->aData[pBlob->nUsed], aData, nData); |
| 295 | pBlob->nUsed += nData; |
| 296 | pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */ |
| 297 | } |
| 298 | |
| 299 | /* |
| 300 | ** Append a single character to the blob |
| 301 | */ |
| 302 | void blob_append_char(Blob *pBlob, char c){ |
| 303 | if( pBlob->nUsed+1>pBlob->nAlloc ){ |
| 304 | pBlob->xRealloc(pBlob, pBlob->nUsed + pBlob->nAlloc + 100); |
| 305 | if( pBlob->nUsed + 1 >= pBlob->nAlloc ){ |
| 306 | blob_panic(); |
| 307 | } |
| 308 | } |
| 309 | pBlob->aData[pBlob->nUsed++] = c; |
| 310 | } |
| 311 | |
| 312 | /* |
| 313 | ** Copy a blob |
| 314 | */ |
| 315 | void blob_copy(Blob *pTo, Blob *pFrom){ |
| @@ -309,11 +322,11 @@ | |
| 322 | ** Return a pointer to a null-terminated string for a blob. |
| 323 | */ |
| 324 | char *blob_str(Blob *p){ |
| 325 | blob_is_init(p); |
| 326 | if( p->nUsed==0 ){ |
| 327 | blob_append_char(p, 0); /* NOTE: Changes nUsed. */ |
| 328 | p->nUsed = 0; |
| 329 | } |
| 330 | if( p->aData[p->nUsed]!=0 ){ |
| 331 | blob_materialize(p); |
| 332 | } |
| @@ -667,11 +680,11 @@ | |
| 680 | ** Ensure that the text in pBlob ends with '\n' |
| 681 | */ |
| 682 | void blob_add_final_newline(Blob *pBlob){ |
| 683 | if( pBlob->nUsed<=0 ) return; |
| 684 | if( pBlob->aData[pBlob->nUsed-1]!='\n' ){ |
| 685 | blob_append_char(pBlob, '\n'); |
| 686 | } |
| 687 | } |
| 688 | |
| 689 | /* |
| 690 | ** Return true if the blob contains a valid base16 identifier artifact hash. |
| @@ -1254,16 +1267,16 @@ | |
| 1267 | if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){ |
| 1268 | needEscape = 1; |
| 1269 | } |
| 1270 | } |
| 1271 | if( n>0 && !fossil_isspace(z[n-1]) ){ |
| 1272 | blob_append_char(pBlob, ' '); |
| 1273 | } |
| 1274 | if( needEscape ) blob_append_char(pBlob, cQuote); |
| 1275 | if( zIn[0]=='-' ) blob_append(pBlob, "./", 2); |
| 1276 | blob_append(pBlob, zIn, -1); |
| 1277 | if( needEscape ) blob_append_char(pBlob, cQuote); |
| 1278 | } |
| 1279 | |
| 1280 | /* |
| 1281 | ** A read(2)-like impl for the Blob class. Reads (copies) up to nLen |
| 1282 | ** bytes from pIn, starting at position pIn->iCursor, and copies them |
| @@ -1330,11 +1343,11 @@ | |
| 1343 | zUtf8[i] = zUtf8[i-1]; |
| 1344 | zUtf8[--i] = zTemp; |
| 1345 | } |
| 1346 | } |
| 1347 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1348 | blob_append_char(pBlob, 0); |
| 1349 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1350 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1351 | blob_set_dynamic(pBlob, zUtf8); |
| 1352 | }else if( useMbcs && invalid_utf8(pBlob) ){ |
| 1353 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1354 |
+50
-7
| --- src/email.c | ||
| +++ src/email.c | ||
| @@ -291,10 +291,11 @@ | ||
| 291 | 291 | @ </div></form> |
| 292 | 292 | db_end_transaction(0); |
| 293 | 293 | style_footer(); |
| 294 | 294 | } |
| 295 | 295 | |
| 296 | +#if 0 | |
| 296 | 297 | /* |
| 297 | 298 | ** Encode pMsg as MIME base64 and append it to pOut |
| 298 | 299 | */ |
| 299 | 300 | static void append_base64(Blob *pOut, Blob *pMsg){ |
| 300 | 301 | int n, i, k; |
| @@ -303,10 +304,47 @@ | ||
| 303 | 304 | for(i=0; i<n; i+=54){ |
| 304 | 305 | k = translateBase64(blob_buffer(pMsg)+i, i+54<n ? 54 : n-i, zBuf); |
| 305 | 306 | blob_append(pOut, zBuf, k); |
| 306 | 307 | blob_append(pOut, "\r\n", 2); |
| 307 | 308 | } |
| 309 | +} | |
| 310 | +#endif | |
| 311 | + | |
| 312 | +/* | |
| 313 | +** Encode pMsg using the quoted-printable email encoding and | |
| 314 | +** append it onto pOut | |
| 315 | +*/ | |
| 316 | +static void append_quoted(Blob *pOut, Blob *pMsg){ | |
| 317 | + char *zIn = blob_str(pMsg); | |
| 318 | + char c; | |
| 319 | + int iCol = 0; | |
| 320 | + while( (c = *(zIn++))!=0 ){ | |
| 321 | + if( (c>='!' && c<='~' && c!='=') | |
| 322 | + || (c==' ' && zIn[0]!='\r' && zIn[0]!='\n') | |
| 323 | + ){ | |
| 324 | + blob_append_char(pOut, c); | |
| 325 | + iCol++; | |
| 326 | + if( iCol>=70 ){ | |
| 327 | + blob_append(pOut, "=\n", 2); | |
| 328 | + iCol = 0; | |
| 329 | + } | |
| 330 | + }else if( c=='\r' && zIn[0]=='\n' ){ | |
| 331 | + zIn++; | |
| 332 | + blob_append(pOut, "\r\n", 2); | |
| 333 | + iCol = 0; | |
| 334 | + }else if( c=='\n' ){ | |
| 335 | + blob_append(pOut, "\r\n", 2); | |
| 336 | + iCol = 0; | |
| 337 | + }else{ | |
| 338 | + char x[3]; | |
| 339 | + x[0] = '='; | |
| 340 | + x[1] = "0123456789ABCDEF"[(c>>4)&0xf]; | |
| 341 | + x[2] = "0123456789ABCDEF"[c&0xf]; | |
| 342 | + blob_append(pOut, x, 3); | |
| 343 | + iCol += 3; | |
| 344 | + } | |
| 345 | + } | |
| 308 | 346 | } |
| 309 | 347 | |
| 310 | 348 | /* |
| 311 | 349 | ** Come up with a unique filename in the zDir directory. |
| 312 | 350 | ** |
| @@ -498,12 +536,17 @@ | ||
| 498 | 536 | } |
| 499 | 537 | blob_append(pOut, blob_buffer(pHdr), blob_size(pHdr)); |
| 500 | 538 | blob_appendf(pOut, "From: %s\r\n", p->zFrom); |
| 501 | 539 | blob_add_final_newline(pBody); |
| 502 | 540 | blob_appendf(pOut,"Content-Type: text/plain\r\n"); |
| 541 | +#if 0 | |
| 503 | 542 | blob_appendf(pOut, "Content-Transfer-Encoding: base64\r\n\r\n"); |
| 504 | 543 | append_base64(pOut, pBody); |
| 544 | +#else | |
| 545 | + blob_appendf(pOut, "Content-Transfer-Encoding: quoted-printable\r\n\r\n"); | |
| 546 | + append_quoted(pOut, pBody); | |
| 547 | +#endif | |
| 505 | 548 | if( p->pStmt ){ |
| 506 | 549 | int i, rc; |
| 507 | 550 | sqlite3_bind_text(p->pStmt, 1, blob_str(&all), -1, SQLITE_TRANSIENT); |
| 508 | 551 | for(i=0; i<100 && sqlite3_step(p->pStmt)==SQLITE_BUSY; i++){ |
| 509 | 552 | sqlite3_sleep(10); |
| @@ -1610,12 +1653,12 @@ | ||
| 1610 | 1653 | /* |
| 1611 | 1654 | ** Append the "unsubscribe" notification and other footer text to |
| 1612 | 1655 | ** the end of an email alert being assemblied in pOut. |
| 1613 | 1656 | */ |
| 1614 | 1657 | void email_footer(Blob *pOut){ |
| 1615 | - blob_appendf(pOut, "\n%.72c\nTo unsubscribe: %s/unsubscribe\n", | |
| 1616 | - '-', db_get("email-url","http://localhost:8080")); | |
| 1658 | + blob_appendf(pOut, "\n-- \nTo unsubscribe: %s/unsubscribe\n", | |
| 1659 | + db_get("email-url","http://localhost:8080")); | |
| 1617 | 1660 | } |
| 1618 | 1661 | |
| 1619 | 1662 | /* |
| 1620 | 1663 | ** COMMAND: test-alert |
| 1621 | 1664 | ** |
| @@ -1771,12 +1814,12 @@ | ||
| 1771 | 1814 | nHit++; |
| 1772 | 1815 | blob_append(&body, "\n", 1); |
| 1773 | 1816 | blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt)); |
| 1774 | 1817 | } |
| 1775 | 1818 | if( nHit==0 ) continue; |
| 1776 | - blob_appendf(&body,"\n%.72c\nSubscription info: %s/alerts/%s\n", | |
| 1777 | - '-', zUrl, zCode); | |
| 1819 | + blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", | |
| 1820 | + zUrl, zCode); | |
| 1778 | 1821 | email_send(pSender,&hdr,&body); |
| 1779 | 1822 | blob_truncate(&hdr, 0); |
| 1780 | 1823 | blob_truncate(&body, 0); |
| 1781 | 1824 | } |
| 1782 | 1825 | blob_zero(&hdr); |
| @@ -1949,22 +1992,22 @@ | ||
| 1949 | 1992 | } |
| 1950 | 1993 | if( bAll || bAA ){ |
| 1951 | 1994 | Stmt q; |
| 1952 | 1995 | int nUsed = blob_size(&body); |
| 1953 | 1996 | const char *zURL = db_get("email-url",0); |
| 1954 | - db_prepare(&q, "SELECT semail, subscriberCode FROM subscriber " | |
| 1997 | + db_prepare(&q, "SELECT semail, hex(subscriberCode) FROM subscriber " | |
| 1955 | 1998 | " WHERE sverified AND NOT sdonotcall %s", |
| 1956 | 1999 | bAll ? "" : " AND ssub LIKE '%a%'"); |
| 1957 | 2000 | while( db_step(&q)==SQLITE_ROW ){ |
| 1958 | 2001 | const char *zCode = db_column_text(&q, 1); |
| 1959 | 2002 | zTo = db_column_text(&q, 0); |
| 1960 | 2003 | blob_truncate(&hdr, 0); |
| 1961 | 2004 | blob_appendf(&hdr, "To: %s\nSubject: %s %s\n", zTo, zSub, zSubject); |
| 1962 | 2005 | if( zURL ){ |
| 1963 | 2006 | blob_truncate(&body, nUsed); |
| 1964 | - blob_appendf(&body,"\n%.72c\nSubscription info: %s/alerts/%s\n", | |
| 1965 | - '-', zURL, zCode); | |
| 2007 | + blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", | |
| 2008 | + zURL, zCode); | |
| 1966 | 2009 | } |
| 1967 | 2010 | email_send(pSender, &hdr, &body); |
| 1968 | 2011 | } |
| 1969 | 2012 | db_finalize(&q); |
| 1970 | 2013 | } |
| 1971 | 2014 |
| --- src/email.c | |
| +++ src/email.c | |
| @@ -291,10 +291,11 @@ | |
| 291 | @ </div></form> |
| 292 | db_end_transaction(0); |
| 293 | style_footer(); |
| 294 | } |
| 295 | |
| 296 | /* |
| 297 | ** Encode pMsg as MIME base64 and append it to pOut |
| 298 | */ |
| 299 | static void append_base64(Blob *pOut, Blob *pMsg){ |
| 300 | int n, i, k; |
| @@ -303,10 +304,47 @@ | |
| 303 | for(i=0; i<n; i+=54){ |
| 304 | k = translateBase64(blob_buffer(pMsg)+i, i+54<n ? 54 : n-i, zBuf); |
| 305 | blob_append(pOut, zBuf, k); |
| 306 | blob_append(pOut, "\r\n", 2); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | /* |
| 311 | ** Come up with a unique filename in the zDir directory. |
| 312 | ** |
| @@ -498,12 +536,17 @@ | |
| 498 | } |
| 499 | blob_append(pOut, blob_buffer(pHdr), blob_size(pHdr)); |
| 500 | blob_appendf(pOut, "From: %s\r\n", p->zFrom); |
| 501 | blob_add_final_newline(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); |
| 505 | if( p->pStmt ){ |
| 506 | int i, rc; |
| 507 | sqlite3_bind_text(p->pStmt, 1, blob_str(&all), -1, SQLITE_TRANSIENT); |
| 508 | for(i=0; i<100 && sqlite3_step(p->pStmt)==SQLITE_BUSY; i++){ |
| 509 | sqlite3_sleep(10); |
| @@ -1610,12 +1653,12 @@ | |
| 1610 | /* |
| 1611 | ** Append the "unsubscribe" notification and other footer text to |
| 1612 | ** the end of an email alert being assemblied in pOut. |
| 1613 | */ |
| 1614 | void email_footer(Blob *pOut){ |
| 1615 | blob_appendf(pOut, "\n%.72c\nTo unsubscribe: %s/unsubscribe\n", |
| 1616 | '-', db_get("email-url","http://localhost:8080")); |
| 1617 | } |
| 1618 | |
| 1619 | /* |
| 1620 | ** COMMAND: test-alert |
| 1621 | ** |
| @@ -1771,12 +1814,12 @@ | |
| 1771 | nHit++; |
| 1772 | blob_append(&body, "\n", 1); |
| 1773 | blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt)); |
| 1774 | } |
| 1775 | if( nHit==0 ) continue; |
| 1776 | blob_appendf(&body,"\n%.72c\nSubscription info: %s/alerts/%s\n", |
| 1777 | '-', zUrl, zCode); |
| 1778 | email_send(pSender,&hdr,&body); |
| 1779 | blob_truncate(&hdr, 0); |
| 1780 | blob_truncate(&body, 0); |
| 1781 | } |
| 1782 | blob_zero(&hdr); |
| @@ -1949,22 +1992,22 @@ | |
| 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 |
| --- src/email.c | |
| +++ src/email.c | |
| @@ -291,10 +291,11 @@ | |
| 291 | @ </div></form> |
| 292 | db_end_transaction(0); |
| 293 | style_footer(); |
| 294 | } |
| 295 | |
| 296 | #if 0 |
| 297 | /* |
| 298 | ** Encode pMsg as MIME base64 and append it to pOut |
| 299 | */ |
| 300 | static void append_base64(Blob *pOut, Blob *pMsg){ |
| 301 | int n, i, k; |
| @@ -303,10 +304,47 @@ | |
| 304 | for(i=0; i<n; i+=54){ |
| 305 | k = translateBase64(blob_buffer(pMsg)+i, i+54<n ? 54 : n-i, zBuf); |
| 306 | blob_append(pOut, zBuf, k); |
| 307 | blob_append(pOut, "\r\n", 2); |
| 308 | } |
| 309 | } |
| 310 | #endif |
| 311 | |
| 312 | /* |
| 313 | ** Encode pMsg using the quoted-printable email encoding and |
| 314 | ** append it onto pOut |
| 315 | */ |
| 316 | static void append_quoted(Blob *pOut, Blob *pMsg){ |
| 317 | char *zIn = blob_str(pMsg); |
| 318 | char c; |
| 319 | int iCol = 0; |
| 320 | while( (c = *(zIn++))!=0 ){ |
| 321 | if( (c>='!' && c<='~' && c!='=') |
| 322 | || (c==' ' && zIn[0]!='\r' && zIn[0]!='\n') |
| 323 | ){ |
| 324 | blob_append_char(pOut, c); |
| 325 | iCol++; |
| 326 | if( iCol>=70 ){ |
| 327 | blob_append(pOut, "=\n", 2); |
| 328 | iCol = 0; |
| 329 | } |
| 330 | }else if( c=='\r' && zIn[0]=='\n' ){ |
| 331 | zIn++; |
| 332 | blob_append(pOut, "\r\n", 2); |
| 333 | iCol = 0; |
| 334 | }else if( c=='\n' ){ |
| 335 | blob_append(pOut, "\r\n", 2); |
| 336 | iCol = 0; |
| 337 | }else{ |
| 338 | char x[3]; |
| 339 | x[0] = '='; |
| 340 | x[1] = "0123456789ABCDEF"[(c>>4)&0xf]; |
| 341 | x[2] = "0123456789ABCDEF"[c&0xf]; |
| 342 | blob_append(pOut, x, 3); |
| 343 | iCol += 3; |
| 344 | } |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | /* |
| 349 | ** Come up with a unique filename in the zDir directory. |
| 350 | ** |
| @@ -498,12 +536,17 @@ | |
| 536 | } |
| 537 | blob_append(pOut, blob_buffer(pHdr), blob_size(pHdr)); |
| 538 | blob_appendf(pOut, "From: %s\r\n", p->zFrom); |
| 539 | blob_add_final_newline(pBody); |
| 540 | blob_appendf(pOut,"Content-Type: text/plain\r\n"); |
| 541 | #if 0 |
| 542 | blob_appendf(pOut, "Content-Transfer-Encoding: base64\r\n\r\n"); |
| 543 | append_base64(pOut, pBody); |
| 544 | #else |
| 545 | blob_appendf(pOut, "Content-Transfer-Encoding: quoted-printable\r\n\r\n"); |
| 546 | append_quoted(pOut, pBody); |
| 547 | #endif |
| 548 | if( p->pStmt ){ |
| 549 | int i, rc; |
| 550 | sqlite3_bind_text(p->pStmt, 1, blob_str(&all), -1, SQLITE_TRANSIENT); |
| 551 | for(i=0; i<100 && sqlite3_step(p->pStmt)==SQLITE_BUSY; i++){ |
| 552 | sqlite3_sleep(10); |
| @@ -1610,12 +1653,12 @@ | |
| 1653 | /* |
| 1654 | ** Append the "unsubscribe" notification and other footer text to |
| 1655 | ** the end of an email alert being assemblied in pOut. |
| 1656 | */ |
| 1657 | void email_footer(Blob *pOut){ |
| 1658 | blob_appendf(pOut, "\n-- \nTo unsubscribe: %s/unsubscribe\n", |
| 1659 | db_get("email-url","http://localhost:8080")); |
| 1660 | } |
| 1661 | |
| 1662 | /* |
| 1663 | ** COMMAND: test-alert |
| 1664 | ** |
| @@ -1771,12 +1814,12 @@ | |
| 1814 | nHit++; |
| 1815 | blob_append(&body, "\n", 1); |
| 1816 | blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt)); |
| 1817 | } |
| 1818 | if( nHit==0 ) continue; |
| 1819 | blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", |
| 1820 | zUrl, zCode); |
| 1821 | email_send(pSender,&hdr,&body); |
| 1822 | blob_truncate(&hdr, 0); |
| 1823 | blob_truncate(&body, 0); |
| 1824 | } |
| 1825 | blob_zero(&hdr); |
| @@ -1949,22 +1992,22 @@ | |
| 1992 | } |
| 1993 | if( bAll || bAA ){ |
| 1994 | Stmt q; |
| 1995 | int nUsed = blob_size(&body); |
| 1996 | const char *zURL = db_get("email-url",0); |
| 1997 | db_prepare(&q, "SELECT semail, hex(subscriberCode) FROM subscriber " |
| 1998 | " WHERE sverified AND NOT sdonotcall %s", |
| 1999 | bAll ? "" : " AND ssub LIKE '%a%'"); |
| 2000 | while( db_step(&q)==SQLITE_ROW ){ |
| 2001 | const char *zCode = db_column_text(&q, 1); |
| 2002 | zTo = db_column_text(&q, 0); |
| 2003 | blob_truncate(&hdr, 0); |
| 2004 | blob_appendf(&hdr, "To: %s\nSubject: %s %s\n", zTo, zSub, zSubject); |
| 2005 | if( zURL ){ |
| 2006 | blob_truncate(&body, nUsed); |
| 2007 | blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", |
| 2008 | zURL, zCode); |
| 2009 | } |
| 2010 | email_send(pSender, &hdr, &body); |
| 2011 | } |
| 2012 | db_finalize(&q); |
| 2013 | } |
| 2014 |