%h(db_column_text(&q, 0))
+ }else{
+ EmailToc *p = emailtoc_from_email(&msg);
+ int i, j;
+ @
+ for(i=0; inHdr; i++){
+ char *z = p->azHdr[i];
+ email_hdr_unfold(z);
+ for(j=0; z[j] && z[j]!=':'; j++){}
+ if( eFormat==0 && !webmail_normal_header(z, j) ) continue;
+ if( z[j]!=':' ){
+ @ %h(z)
+ }else{
+ z[j] = 0;
+ @ %h(z): %h(z+j+1)
+ }
+ }
+ for(i=0; inBody; i++){
+ @
Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \
+ if( p->aBody[i].zFilename ){
+ @ "%h(p->aBody[i].zFilename)"
+ }
+ @
+ if( eFormat==0 ){
+ if( strncmp(p->aBody[i].zMimetype, "text/plain", 10)!=0 ) continue;
+ if( p->aBody[i].zFilename ) continue;
+ }else{
+ if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue;
+ }
+ switch( p->aBody[i].encoding ){
+ case EMAILENC_B64: {
+ int n = 0;
+ decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent);
+ break;
+ }
+ case EMAILENC_QUOTED: {
+ int n = 0;
+ decodeQuotedPrintable(p->aBody[i].zContent, &n);
+ break;
+ }
+ }
+ @ %h(p->aBody[i].zContent)
+ }
+ }
+ }
+ db_finalize(&q);
+
+ if( eState==0 ){
+ /* If is message is currently Unread, change it to Read */
+ blob_append_sql(&sql,
+ "UPDATE emailbox SET estate=1 "
+ " WHERE estate=0 AND ebid=%d",
+ emailid
+ );
+ if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser);
+ db_multi_exec("%s", blob_sql_text(&sql));
+ blob_reset(&sql);
+ eState = 1;
+ }
+
+ url_add_parameter(pUrl, "id", 0);
+ sqlite3_snprintf(sizeof(zENum), zENum, "e%d", emailid);
+ if( eState==2 ){
+ style_submenu_element("Undelete","%s",
+ url_render(pUrl,"read","1",zENum,"1"));
+ }
+ if( eState==1 ){
+ style_submenu_element("Delete", "%s",
+ url_render(pUrl,"trash","1",zENum,"1"));
+ style_submenu_element("Mark As Unread", "%s",
+ url_render(pUrl,"unread","1",zENum,"1"));
+ }
+ if( eState==3 ){
+ style_submenu_element("Delete", "%s",
+ url_render(pUrl,"trash","1",zENum,"1"));
+ }
+
+ db_end_transaction(0);
+ style_footer();
+ return;
+}
+
+/*
+** Scan the query parameters looking for parameters with name of the
+** form "eN" where N is an integer. For all such integers, change
+** the state of every emailbox entry with ebid==N to eStateNew provided
+** that either zUser is NULL or matches.
+**
+** Or if eNewState==99, then delete the entries.
+*/
+static void webmail_change_state(int eNewState, const char *zUser){
+ Blob sql;
+ int sep = '(';
+ int i;
+ const char *zName;
+ int n;
+ if( !cgi_csrf_safe(0) ) return;
+ blob_init(&sql, 0, 0);
+ if( eNewState==99 ){
+ blob_append_sql(&sql, "DELETE FROM emailbox WHERE estate==2 AND ebid IN ");
+ }else{
+ blob_append_sql(&sql, "UPDATE emailbox SET estate=%d WHERE ebid IN ",
+ eNewState);
+ }
+ for(i=0; (zName = cgi_parameter_name(i))!=0; i++){
+ if( zName[0]!='e' ) continue;
+ if( !fossil_isdigit(zName[1]) ) continue;
+ n = atoi(zName+1);
+ blob_append_sql(&sql, "%c%d", sep, n);
+ sep = ',';
+ }
+ if( zUser ){
+ blob_append_sql(&sql, ") AND euser=%Q", zUser);
+ }else{
+ blob_append_sql(&sql, ")");
+ }
+ if( sep==',' ){
+ db_multi_exec("%s", blob_sql_text(&sql));
+ }
+ blob_reset(&sql);
+}
+
+
+/*
+** Add the select/option box to the timeline submenu that shows
+** which messages to include in the index.
+*/
+static void webmail_d_submenu(void){
+ static const char *az[] = {
+ "0", "InBox",
+ "1", "Unread",
+ "2", "Trash",
+ "3", "Sent",
+ "4", "Everything",
+ };
+ style_submenu_multichoice("d", sizeof(az)/(2*sizeof(az[0])), az, 0);
+}
/*
** WEBPAGE: webmail
**
** This page can be used to read content from the EMAILBOX table
** that contains email received by the "fossil smtpd" command.
+**
+** Query parameters:
+**
+** id=N Show a single email entry emailbox.ebid==N
+** f=N Display format. 0: decoded 1: raw
+** user=USER Show mailbox for USER (admin only).
+** user=* Show mailbox for all users (admin only).
+** d=N 0: inbox+unread 1: unread-only 2: trash 3: all
+** eN Select email entry emailbox.ebid==N
+** trash Move selected entries to trash (estate=2)
+** read Mark selected entries as read (estate=1)
+** unread Mark selected entries as unread (estate=0)
+**
*/
void webmail_page(void){
int emailid;
Stmt q;
Blob sql;
int showAll = 0;
const char *zUser = 0;
+ int d = 0; /* Display mode. 0..3. d= query parameter */
+ int pg = 0; /* Page number */
+ int N = 50; /* Results per page */
+ int got; /* Number of results on this page */
+ char zPPg[30]; /* Previous page */
+ char zNPg[30]; /* Next page */
HQuery url;
login_check_credentials();
- if( g.zLogin==0 ){
+ if( !login_is_individual() ){
login_needed(0);
return;
}
if( !db_table_exists("repository","emailbox") ){
style_header("Webmail Not Available");
@@ -389,116 +605,208 @@
if( fossil_strcmp(zUser,"*")==0 ){
showAll = 1;
zUser = 0;
}
}
+ }else{
+ zUser = g.zLogin;
}
+ if( P("d") ) url_add_parameter(&url, "d", P("d"));
if( emailid>0 ){
- style_submenu_element("Index", "%s", url_render(&url,"id",0,0,0));
- blob_init(&sql, 0, 0);
- blob_append_sql(&sql, "SELECT decompress(etxt)"
- " FROM emailblob WHERE emailid=%d",
- emailid);
- if( !g.perm.Admin ){
- blob_append_sql(&sql, " AND EXISTS(SELECT 1 FROM emailbox WHERE"
- " euser=%Q AND emsgid=emailid)", g.zLogin);
- }
- db_prepare_blob(&q, &sql);
- blob_reset(&sql);
- if( db_step(&q)==SQLITE_ROW ){
- Blob msg = db_column_text_as_blob(&q, 0);
- url_add_parameter(&url, "id", P("id"));
- style_header("Message %d",emailid);
- if( PB("raw") ){
- @ %h(db_column_text(&q, 0))
- style_submenu_element("Decoded", "%s", url_render(&url,"raw",0,0,0));
- }else{
- EmailToc *p = emailtoc_from_email(&msg);
- int i, j;
- style_submenu_element("Raw", "%s", url_render(&url,"raw","1",0,0));
- @
- for(i=0; inHdr; i++){
- char *z = p->azHdr[i];
- email_hdr_unfold(z);
- for(j=0; z[j] && z[j]!=':'; j++){}
- if( z[j]!=':' ){
- @ %h(z)
- }else{
- z[j] = 0;
- @ %h(z): %h(z+j+1)
- }
- }
- for(i=0; inBody; i++){
- @
Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \
- if( p->aBody[i].zFilename ){
- @ "%h(p->aBody[i].zFilename)"
- }
- @
- if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue;
- switch( p->aBody[i].encoding ){
- case EMAILENC_B64: {
- int n = 0;
- decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent);
- break;
- }
- case EMAILENC_QUOTED: {
- int n = 0;
- decodeQuotedPrintable(p->aBody[i].zContent, &n);
- break;
- }
- }
- @ %h(p->aBody[i].zContent)
- }
- }
- style_footer();
- db_finalize(&q);
- return;
- }
- db_finalize(&q);
+ webmail_show_one_message(&url, emailid, zUser);
+ return;
}
style_header("Webmail");
+ webmail_d_submenu();
+ db_begin_transaction();
+ if( P("trash")!=0 ) webmail_change_state(2,zUser);
+ if( P("unread")!=0 ) webmail_change_state(0,zUser);
+ if( P("read")!=0 ) webmail_change_state(1,zUser);
+ if( P("purge")!=0 ) webmail_change_state(99,zUser);
blob_init(&sql, 0, 0);
blob_append_sql(&sql,
- /* 0 1 2 3 4 5 */
- "SELECT efrom, datetime(edate,'unixepoch'), estate, esubject, emsgid, euser"
+ "CREATE TEMP TABLE tmbox AS "
+ "SELECT ebid," /* 0 */
+ " efrom," /* 1 */
+ " datetime(edate,'unixepoch')," /* 2 */
+ " estate," /* 3 */
+ " esubject," /* 4 */
+ " euser" /* 5 */
" FROM emailbox"
);
+ d = atoi(PD("d","0"));
+ switch( d ){
+ case 0: { /* Show unread and read */
+ blob_append_sql(&sql, " WHERE estate<=1");
+ break;
+ }
+ case 1: { /* Unread messages only */
+ blob_append_sql(&sql, " WHERE estate=0");
+ break;
+ }
+ case 2: { /* Trashcan only */
+ blob_append_sql(&sql, " WHERE estate=2");
+ break;
+ }
+ case 3: { /* Outgoing email only */
+ blob_append_sql(&sql, " WHERE estate=3");
+ break;
+ }
+ case 4: { /* Everything */
+ blob_append_sql(&sql, " WHERE 1");
+ break;
+ }
+ }
if( showAll ){
style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0));
}else if( zUser!=0 ){
style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0));
if( fossil_strcmp(zUser, g.zLogin)!=0 ){
style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0));
}
if( zUser ){
- blob_append_sql(&sql, " WHERE euser=%Q", zUser);
+ blob_append_sql(&sql, " AND euser=%Q", zUser);
}else{
- blob_append_sql(&sql, " WHERE euser=%Q", g.zLogin);
+ blob_append_sql(&sql, " AND euser=%Q", g.zLogin);
}
}else{
if( g.perm.Admin ){
style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0));
}
- blob_append_sql(&sql, " WHERE euser=%Q", g.zLogin);
+ blob_append_sql(&sql, " AND euser=%Q", g.zLogin);
}
- blob_append_sql(&sql, " ORDER BY edate DESC limit 50");
- db_prepare_blob(&q, &sql);
+ pg = atoi(PD("pg","0"));
+ blob_append_sql(&sql, " ORDER BY edate DESC limit %d offset %d", N+1, pg*N);
+ db_multi_exec("%s", blob_sql_text(&sql));
+ got = db_int(0, "SELECT count(*) FROM tmbox");
+ db_prepare(&q, "SELECT * FROM tmbox LIMIT %d", N);
blob_reset(&sql);
- @
+ @
+ style_footer();
+ db_end_transaction(0);
+}
+
+/*
+** WEBPAGE: test-emailblob
+**
+** This page, accessible only to administrators, allows easy viewing of
+** the emailblob table - the table that contains the text of email messages
+** both inbound and outbound, and transcripts of SMTP sessions.
+**
+** id=N Show the text of emailblob with emailid==N
+**
+*/
+void webmail_emailblob_page(void){
+ int id = atoi(PD("id","0"));
+ Stmt q;
+ login_check_credentials();
+ if( !g.perm.Setup ){
+ login_needed(0);
+ return;
+ }
+ add_content_sql_commands(g.db);
+ style_header("emailblob table");
+ if( id>0 ){
+ style_submenu_element("Index", "%R/test-emailblob");
+ @
+ db_prepare(&q, "SELECT emailid FROM emailblob WHERE ets=%d", id);
+ while( db_step(&q)==SQLITE_ROW ){
+ int id = db_column_int(&q, 0);
+ @ - emailblob entry %d(id)
+ }
+ db_finalize(&q);
+ db_prepare(&q, "SELECT euser, estate FROM emailbox WHERE emsgid=%d", id);
+ while( db_step(&q)==SQLITE_ROW ){
+ const char *zUser = db_column_text(&q, 0);
+ int e = db_column_int(&q, 1);
+ @
- emailbox for %h(zUser) state %d(e)
+ }
+ db_finalize(&q);
+ db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE emsgid=%d", id);
+ while( db_step(&q)==SQLITE_ROW ){
+ const char *zFrom = db_column_text(&q, 0);
+ const char *zTo = db_column_text(&q, 1);
+ @
- emailoutq message body from %h(zFrom) to %h(zTo)
+ }
+ db_finalize(&q);
+ db_prepare(&q, "SELECT efrom, eto FROM emailoutq WHERE ets=%d", id);
+ while( db_step(&q)==SQLITE_ROW ){
+ const char *zFrom = db_column_text(&q, 0);
+ const char *zTo = db_column_text(&q, 1);
+ @
- emailoutq transcript from %h(zFrom) to %h(zTo)
+ }
+ db_finalize(&q);
+ @
+ @
+ db_prepare(&q, "SELECT decompress(etxt) FROM emailblob WHERE emailid=%d",
+ id);
+ while( db_step(&q)==SQLITE_ROW ){
+ const char *zContent = db_column_text(&q, 0);
+ @ %h(zContent)
+ }
+ db_finalize(&q);
+ }else{
+ db_prepare(&q,
+ "SELECT emailid, enref, ets, datetime(etime,'unixepoch')"
+ " FROM emailblob ORDER BY etime DESC, emailid DESC");
+ @
+ @ emailid | enref | ets | etime |
+ while( db_step(&q)==SQLITE_ROW ){
+ int id = db_column_int(&q, 0);
+ int nref = db_column_int(&q, 1);
+ int ets = db_column_int(&q, 2);
+ const char *zDate = db_column_text(&q, 3);
+ @
+ @ %d(id)
+ @ | %d(nref) |
+ @ %d(ets) |
+ @ %h(zDate) |
+ @
+ }
+ @
+ db_finalize(&q);
+ }
+ style_footer();
}