Fossil SCM
Webmail navigation updates. Move message between Unread/Read/Trash. Show 50 messages per page with Newer and Older links when appropriate.
Commit
485d01fb17d22f6da320da797bde045d3800c99be1423375d935b519cee1fcba
Parent
c1f8668bca7a7eb…
1 file changed
+201
-68
+201
-68
| --- src/webmail.c | ||
| +++ src/webmail.c | ||
| @@ -352,10 +352,164 @@ | ||
| 352 | 352 | } |
| 353 | 353 | } |
| 354 | 354 | emailtoc_free(p); |
| 355 | 355 | blob_reset(&email); |
| 356 | 356 | } |
| 357 | + | |
| 358 | +/* | |
| 359 | +** Paint a page showing a single email message | |
| 360 | +*/ | |
| 361 | +static void webmail_show_one_message( | |
| 362 | + HQuery *pUrl, /* Calling context */ | |
| 363 | + int emailid, /* emailbox.ebid to display */ | |
| 364 | + const char *zUser /* User who owns it, or NULL if does not matter */ | |
| 365 | +){ | |
| 366 | + Blob sql; | |
| 367 | + Stmt q; | |
| 368 | + int eState = -1; | |
| 369 | + char zENum[30]; | |
| 370 | + style_submenu_element("Index", "%s", url_render(pUrl,"id",0,0,0)); | |
| 371 | + blob_init(&sql, 0, 0); | |
| 372 | + db_begin_transaction(); | |
| 373 | + blob_append_sql(&sql, | |
| 374 | + "SELECT decompress(etxt), estate" | |
| 375 | + " FROM emailblob, emailbox" | |
| 376 | + " WHERE emailid=emsgid AND ebid=%d", | |
| 377 | + emailid | |
| 378 | + ); | |
| 379 | + if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser); | |
| 380 | + db_prepare_blob(&q, &sql); | |
| 381 | + blob_reset(&sql); | |
| 382 | + style_header("Message %d",emailid); | |
| 383 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 384 | + Blob msg = db_column_text_as_blob(&q, 0); | |
| 385 | + int eFormat = atoi(PD("f","0")); | |
| 386 | + eState = db_column_int(&q, 1); | |
| 387 | + url_add_parameter(pUrl, "id", P("id")); | |
| 388 | + if( eFormat==1 ){ | |
| 389 | + @ <pre>%h(db_column_text(&q, 0))</pre> | |
| 390 | + style_submenu_element("Decoded", "%s", url_render(pUrl,"f",0,0,0)); | |
| 391 | + }else{ | |
| 392 | + EmailToc *p = emailtoc_from_email(&msg); | |
| 393 | + int i, j; | |
| 394 | + style_submenu_element("Raw", "%s", url_render(pUrl,"f","1",0,0)); | |
| 395 | + @ <p> | |
| 396 | + for(i=0; i<p->nHdr; i++){ | |
| 397 | + char *z = p->azHdr[i]; | |
| 398 | + email_hdr_unfold(z); | |
| 399 | + for(j=0; z[j] && z[j]!=':'; j++){} | |
| 400 | + if( z[j]!=':' ){ | |
| 401 | + @ %h(z)<br> | |
| 402 | + }else{ | |
| 403 | + z[j] = 0; | |
| 404 | + @ <b>%h(z):</b> %h(z+j+1)<br> | |
| 405 | + } | |
| 406 | + } | |
| 407 | + for(i=0; i<p->nBody; i++){ | |
| 408 | + @ <hr><b>Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \ | |
| 409 | + if( p->aBody[i].zFilename ){ | |
| 410 | + @ "%h(p->aBody[i].zFilename)" | |
| 411 | + } | |
| 412 | + @ </b> | |
| 413 | + if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue; | |
| 414 | + switch( p->aBody[i].encoding ){ | |
| 415 | + case EMAILENC_B64: { | |
| 416 | + int n = 0; | |
| 417 | + decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); | |
| 418 | + break; | |
| 419 | + } | |
| 420 | + case EMAILENC_QUOTED: { | |
| 421 | + int n = 0; | |
| 422 | + decodeQuotedPrintable(p->aBody[i].zContent, &n); | |
| 423 | + break; | |
| 424 | + } | |
| 425 | + } | |
| 426 | + @ <pre>%h(p->aBody[i].zContent)</pre> | |
| 427 | + } | |
| 428 | + } | |
| 429 | + } | |
| 430 | + db_finalize(&q); | |
| 431 | + | |
| 432 | + if( eState==0 ){ | |
| 433 | + /* If is message is currently Unread, change it to Read */ | |
| 434 | + blob_append_sql(&sql, | |
| 435 | + "UPDATE emailbox SET estate=1 " | |
| 436 | + " WHERE estate=0 AND ebid=%d", | |
| 437 | + emailid | |
| 438 | + ); | |
| 439 | + if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser); | |
| 440 | + db_multi_exec("%s", blob_sql_text(&sql)); | |
| 441 | + blob_reset(&sql); | |
| 442 | + eState = 1; | |
| 443 | + } | |
| 444 | + | |
| 445 | + url_add_parameter(pUrl, "id", 0); | |
| 446 | + sqlite3_snprintf(sizeof(zENum), zENum, "e%d", emailid); | |
| 447 | + if( eState==2 ){ | |
| 448 | + style_submenu_element("Undelete","%s", | |
| 449 | + url_render(pUrl,"read","1",zENum,"1")); | |
| 450 | + } | |
| 451 | + if( eState==1 ){ | |
| 452 | + style_submenu_element("Delete", "%s", | |
| 453 | + url_render(pUrl,"trash","1",zENum,"1")); | |
| 454 | + style_submenu_element("Mark As Unread", "%s", | |
| 455 | + url_render(pUrl,"unread","1",zENum,"1")); | |
| 456 | + } | |
| 457 | + | |
| 458 | + db_end_transaction(0); | |
| 459 | + style_footer(); | |
| 460 | + return; | |
| 461 | +} | |
| 462 | + | |
| 463 | +/* | |
| 464 | +** Scan the query parameters looking for parameters with name of the | |
| 465 | +** form "eN" where N is an integer. For all such integers, change | |
| 466 | +** the state of every emailbox entry with ebid==N to eStateNew provided | |
| 467 | +** that either zUser is NULL or matches. | |
| 468 | +*/ | |
| 469 | +static void webmail_change_state(int eNewState, const char *zUser){ | |
| 470 | + Blob sql; | |
| 471 | + int sep = '('; | |
| 472 | + int i; | |
| 473 | + const char *zName; | |
| 474 | + int n; | |
| 475 | + if( !cgi_csrf_safe(0) ) return; | |
| 476 | + blob_init(&sql, 0, 0); | |
| 477 | + blob_append_sql(&sql, "UPDATE emailbox SET estate=%d WHERE ebid IN ", | |
| 478 | + eNewState); | |
| 479 | + for(i=0; (zName = cgi_parameter_name(i))!=0; i++){ | |
| 480 | + if( zName[0]!='e' ) continue; | |
| 481 | + if( !fossil_isdigit(zName[1]) ) continue; | |
| 482 | + n = atoi(zName+1); | |
| 483 | + blob_append_sql(&sql, "%c%d", sep, n); | |
| 484 | + sep = ','; | |
| 485 | + } | |
| 486 | + if( zUser ){ | |
| 487 | + blob_append_sql(&sql, ") AND euser=%Q", zUser); | |
| 488 | + }else{ | |
| 489 | + blob_append_sql(&sql, ")"); | |
| 490 | + } | |
| 491 | + if( sep==',' ){ | |
| 492 | + db_multi_exec("%s", blob_sql_text(&sql)); | |
| 493 | + } | |
| 494 | + blob_reset(&sql); | |
| 495 | +} | |
| 496 | + | |
| 497 | + | |
| 498 | +/* | |
| 499 | +** Add the select/option box to the timeline submenu that shows | |
| 500 | +** which messages to include in the index. | |
| 501 | +*/ | |
| 502 | +static void webmail_d_submenu(void){ | |
| 503 | + static const char *az[] = { | |
| 504 | + "0", "InBox", | |
| 505 | + "1", "Unread", | |
| 506 | + "2", "Trash", | |
| 507 | + "3", "Everything", | |
| 508 | + }; | |
| 509 | + style_submenu_multichoice("d", sizeof(az)/(2*sizeof(az[0])), az, 0); | |
| 510 | +} | |
| 357 | 511 | |
| 358 | 512 | /* |
| 359 | 513 | ** WEBPAGE: webmail |
| 360 | 514 | ** |
| 361 | 515 | ** This page can be used to read content from the EMAILBOX table |
| @@ -378,10 +532,16 @@ | ||
| 378 | 532 | int emailid; |
| 379 | 533 | Stmt q; |
| 380 | 534 | Blob sql; |
| 381 | 535 | int showAll = 0; |
| 382 | 536 | const char *zUser = 0; |
| 537 | + int d = 0; /* Display mode. 0..3. d= query parameter */ | |
| 538 | + int pg = 0; /* Page number */ | |
| 539 | + int N = 50; /* Results per page */ | |
| 540 | + int got; /* Number of results on this page */ | |
| 541 | + char zPPg[30]; /* Previous page */ | |
| 542 | + char zNPg[30]; /* Next page */ | |
| 383 | 543 | HQuery url; |
| 384 | 544 | login_check_credentials(); |
| 385 | 545 | if( !login_is_individual() ){ |
| 386 | 546 | login_needed(0); |
| 387 | 547 | return; |
| @@ -402,88 +562,37 @@ | ||
| 402 | 562 | if( fossil_strcmp(zUser,"*")==0 ){ |
| 403 | 563 | showAll = 1; |
| 404 | 564 | zUser = 0; |
| 405 | 565 | } |
| 406 | 566 | } |
| 567 | + }else{ | |
| 568 | + zUser = g.zLogin; | |
| 407 | 569 | } |
| 570 | + if( P("d") ) url_add_parameter(&url, "d", P("d")); | |
| 408 | 571 | if( emailid>0 ){ |
| 409 | - style_submenu_element("Index", "%s", url_render(&url,"id",0,0,0)); | |
| 410 | - blob_init(&sql, 0, 0); | |
| 411 | - db_begin_transaction(); | |
| 412 | - blob_append_sql(&sql, "SELECT decompress(etxt)" | |
| 413 | - " FROM emailblob, emailbox" | |
| 414 | - " WHERE emailid=emsgid AND ebid=%d", | |
| 415 | - emailid); | |
| 416 | - if( !g.perm.Admin ){ | |
| 417 | - blob_append_sql(&sql, " AND euser=%Q", g.zLogin); | |
| 418 | - } | |
| 419 | - db_prepare_blob(&q, &sql); | |
| 420 | - blob_reset(&sql); | |
| 421 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 422 | - Blob msg = db_column_text_as_blob(&q, 0); | |
| 423 | - int eFormat = atoi(PD("f","0")); | |
| 424 | - url_add_parameter(&url, "id", P("id")); | |
| 425 | - style_header("Message %d",emailid); | |
| 426 | - if( eFormat==1 ){ | |
| 427 | - @ <pre>%h(db_column_text(&q, 0))</pre> | |
| 428 | - style_submenu_element("Decoded", "%s", url_render(&url,"f",0,0,0)); | |
| 429 | - }else{ | |
| 430 | - EmailToc *p = emailtoc_from_email(&msg); | |
| 431 | - int i, j; | |
| 432 | - style_submenu_element("Raw", "%s", url_render(&url,"f","1",0,0)); | |
| 433 | - @ <p> | |
| 434 | - for(i=0; i<p->nHdr; i++){ | |
| 435 | - char *z = p->azHdr[i]; | |
| 436 | - email_hdr_unfold(z); | |
| 437 | - for(j=0; z[j] && z[j]!=':'; j++){} | |
| 438 | - if( z[j]!=':' ){ | |
| 439 | - @ %h(z)<br> | |
| 440 | - }else{ | |
| 441 | - z[j] = 0; | |
| 442 | - @ <b>%h(z):</b> %h(z+j+1)<br> | |
| 443 | - } | |
| 444 | - } | |
| 445 | - for(i=0; i<p->nBody; i++){ | |
| 446 | - @ <hr><b>Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \ | |
| 447 | - if( p->aBody[i].zFilename ){ | |
| 448 | - @ "%h(p->aBody[i].zFilename)" | |
| 449 | - } | |
| 450 | - @ </b> | |
| 451 | - if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue; | |
| 452 | - switch( p->aBody[i].encoding ){ | |
| 453 | - case EMAILENC_B64: { | |
| 454 | - int n = 0; | |
| 455 | - decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); | |
| 456 | - break; | |
| 457 | - } | |
| 458 | - case EMAILENC_QUOTED: { | |
| 459 | - int n = 0; | |
| 460 | - decodeQuotedPrintable(p->aBody[i].zContent, &n); | |
| 461 | - break; | |
| 462 | - } | |
| 463 | - } | |
| 464 | - @ <pre>%h(p->aBody[i].zContent)</pre> | |
| 465 | - } | |
| 466 | - } | |
| 467 | - style_footer(); | |
| 468 | - db_finalize(&q); | |
| 469 | - return; | |
| 470 | - } | |
| 471 | - db_finalize(&q); | |
| 572 | + webmail_show_one_message(&url, emailid, zUser); | |
| 573 | + return; | |
| 472 | 574 | } |
| 473 | 575 | style_header("Webmail"); |
| 576 | + webmail_d_submenu(); | |
| 577 | + db_begin_transaction(); | |
| 578 | + if( P("trash")!=0 ) webmail_change_state(2,zUser); | |
| 579 | + if( P("unread")!=0 ) webmail_change_state(0,zUser); | |
| 580 | + if( P("read")!=0 ) webmail_change_state(1,zUser); | |
| 474 | 581 | blob_init(&sql, 0, 0); |
| 475 | 582 | blob_append_sql(&sql, |
| 583 | + "CREATE TEMP TABLE tmbox AS " | |
| 476 | 584 | "SELECT ebid," /* 0 */ |
| 477 | 585 | " efrom," /* 1 */ |
| 478 | 586 | " datetime(edate,'unixepoch')," /* 2 */ |
| 479 | 587 | " estate," /* 3 */ |
| 480 | 588 | " esubject," /* 4 */ |
| 481 | 589 | " euser" /* 5 */ |
| 482 | 590 | " FROM emailbox" |
| 483 | 591 | ); |
| 484 | - switch( atoi(PD("d","0")) ){ | |
| 592 | + d = atoi(PD("d","0")); | |
| 593 | + switch( d ){ | |
| 485 | 594 | case 0: { /* Show unread and read */ |
| 486 | 595 | blob_append_sql(&sql, " WHERE estate<=1"); |
| 487 | 596 | break; |
| 488 | 597 | } |
| 489 | 598 | case 1: { /* Unread messages only */ |
| @@ -497,11 +606,10 @@ | ||
| 497 | 606 | case 3: { /* Everything */ |
| 498 | 607 | blob_append_sql(&sql, " WHERE 1"); |
| 499 | 608 | break; |
| 500 | 609 | } |
| 501 | 610 | } |
| 502 | - | |
| 503 | 611 | if( showAll ){ |
| 504 | 612 | style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0)); |
| 505 | 613 | }else if( zUser!=0 ){ |
| 506 | 614 | style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); |
| 507 | 615 | if( fossil_strcmp(zUser, g.zLogin)!=0 ){ |
| @@ -516,14 +624,38 @@ | ||
| 516 | 624 | if( g.perm.Admin ){ |
| 517 | 625 | style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); |
| 518 | 626 | } |
| 519 | 627 | blob_append_sql(&sql, " AND euser=%Q", g.zLogin); |
| 520 | 628 | } |
| 521 | - blob_append_sql(&sql, " ORDER BY edate DESC limit 50"); | |
| 522 | - db_prepare_blob(&q, &sql); | |
| 629 | + pg = atoi(PD("pg","0")); | |
| 630 | + blob_append_sql(&sql, " ORDER BY edate DESC limit %d offset %d", N+1, pg*N); | |
| 631 | + db_multi_exec("%s", blob_sql_text(&sql)); | |
| 632 | + got = db_int(0, "SELECT count(*) FROM tmbox"); | |
| 633 | + db_prepare(&q, "SELECT * FROM tmbox LIMIT %d", N); | |
| 523 | 634 | blob_reset(&sql); |
| 524 | 635 | @ <form action="%R/webmail" method="POST"> |
| 636 | + @ <table border="0" width="100%%"> | |
| 637 | + @ <tr><td align="left"> | |
| 638 | + if( d==2 ){ | |
| 639 | + @ <input type="submit" name="read" value="Undelete"> | |
| 640 | + }else{ | |
| 641 | + @ <input type="submit" name="trash", value="Delete"> | |
| 642 | + if( d!=1 ){ | |
| 643 | + @ <input type="submit" name="unread" value="Mark as unread"> | |
| 644 | + } | |
| 645 | + @ <input type="submit" name="read" value="Mark as read"> | |
| 646 | + } | |
| 647 | + @ </td><td align="right"> | |
| 648 | + if( pg>0 ){ | |
| 649 | + sqlite3_snprintf(sizeof(zPPg), zPPg, "%d", pg-1); | |
| 650 | + @ <a href="%s(url_render(&url,"pg",zPPg,0,0))">< Newer</a> | |
| 651 | + } | |
| 652 | + if( got>50 ){ | |
| 653 | + sqlite3_snprintf(sizeof(zNPg),zNPg,"%d",pg+1); | |
| 654 | + @ <a href="%s(url_render(&url,"pg",zNPg,0,0))">Older ></a></td> | |
| 655 | + } | |
| 656 | + @ </table> | |
| 525 | 657 | @ <table> |
| 526 | 658 | while( db_step(&q)==SQLITE_ROW ){ |
| 527 | 659 | const char *zId = db_column_text(&q,0); |
| 528 | 660 | const char *zFrom = db_column_text(&q, 1); |
| 529 | 661 | const char *zDate = db_column_text(&q, 2); |
| @@ -540,7 +672,8 @@ | ||
| 540 | 672 | @ </tr> |
| 541 | 673 | } |
| 542 | 674 | db_finalize(&q); |
| 543 | 675 | @ </table> |
| 544 | 676 | @ </form> |
| 545 | - style_footer(); | |
| 677 | + style_footer(); | |
| 678 | + db_end_transaction(0); | |
| 546 | 679 | } |
| 547 | 680 |
| --- src/webmail.c | |
| +++ src/webmail.c | |
| @@ -352,10 +352,164 @@ | |
| 352 | } |
| 353 | } |
| 354 | emailtoc_free(p); |
| 355 | blob_reset(&email); |
| 356 | } |
| 357 | |
| 358 | /* |
| 359 | ** WEBPAGE: webmail |
| 360 | ** |
| 361 | ** This page can be used to read content from the EMAILBOX table |
| @@ -378,10 +532,16 @@ | |
| 378 | int emailid; |
| 379 | Stmt q; |
| 380 | Blob sql; |
| 381 | int showAll = 0; |
| 382 | const char *zUser = 0; |
| 383 | HQuery url; |
| 384 | login_check_credentials(); |
| 385 | if( !login_is_individual() ){ |
| 386 | login_needed(0); |
| 387 | return; |
| @@ -402,88 +562,37 @@ | |
| 402 | if( fossil_strcmp(zUser,"*")==0 ){ |
| 403 | showAll = 1; |
| 404 | zUser = 0; |
| 405 | } |
| 406 | } |
| 407 | } |
| 408 | if( emailid>0 ){ |
| 409 | style_submenu_element("Index", "%s", url_render(&url,"id",0,0,0)); |
| 410 | blob_init(&sql, 0, 0); |
| 411 | db_begin_transaction(); |
| 412 | blob_append_sql(&sql, "SELECT decompress(etxt)" |
| 413 | " FROM emailblob, emailbox" |
| 414 | " WHERE emailid=emsgid AND ebid=%d", |
| 415 | emailid); |
| 416 | if( !g.perm.Admin ){ |
| 417 | blob_append_sql(&sql, " AND euser=%Q", g.zLogin); |
| 418 | } |
| 419 | db_prepare_blob(&q, &sql); |
| 420 | blob_reset(&sql); |
| 421 | if( db_step(&q)==SQLITE_ROW ){ |
| 422 | Blob msg = db_column_text_as_blob(&q, 0); |
| 423 | int eFormat = atoi(PD("f","0")); |
| 424 | url_add_parameter(&url, "id", P("id")); |
| 425 | style_header("Message %d",emailid); |
| 426 | if( eFormat==1 ){ |
| 427 | @ <pre>%h(db_column_text(&q, 0))</pre> |
| 428 | style_submenu_element("Decoded", "%s", url_render(&url,"f",0,0,0)); |
| 429 | }else{ |
| 430 | EmailToc *p = emailtoc_from_email(&msg); |
| 431 | int i, j; |
| 432 | style_submenu_element("Raw", "%s", url_render(&url,"f","1",0,0)); |
| 433 | @ <p> |
| 434 | for(i=0; i<p->nHdr; i++){ |
| 435 | char *z = p->azHdr[i]; |
| 436 | email_hdr_unfold(z); |
| 437 | for(j=0; z[j] && z[j]!=':'; j++){} |
| 438 | if( z[j]!=':' ){ |
| 439 | @ %h(z)<br> |
| 440 | }else{ |
| 441 | z[j] = 0; |
| 442 | @ <b>%h(z):</b> %h(z+j+1)<br> |
| 443 | } |
| 444 | } |
| 445 | for(i=0; i<p->nBody; i++){ |
| 446 | @ <hr><b>Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \ |
| 447 | if( p->aBody[i].zFilename ){ |
| 448 | @ "%h(p->aBody[i].zFilename)" |
| 449 | } |
| 450 | @ </b> |
| 451 | if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue; |
| 452 | switch( p->aBody[i].encoding ){ |
| 453 | case EMAILENC_B64: { |
| 454 | int n = 0; |
| 455 | decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); |
| 456 | break; |
| 457 | } |
| 458 | case EMAILENC_QUOTED: { |
| 459 | int n = 0; |
| 460 | decodeQuotedPrintable(p->aBody[i].zContent, &n); |
| 461 | break; |
| 462 | } |
| 463 | } |
| 464 | @ <pre>%h(p->aBody[i].zContent)</pre> |
| 465 | } |
| 466 | } |
| 467 | style_footer(); |
| 468 | db_finalize(&q); |
| 469 | return; |
| 470 | } |
| 471 | db_finalize(&q); |
| 472 | } |
| 473 | style_header("Webmail"); |
| 474 | blob_init(&sql, 0, 0); |
| 475 | blob_append_sql(&sql, |
| 476 | "SELECT ebid," /* 0 */ |
| 477 | " efrom," /* 1 */ |
| 478 | " datetime(edate,'unixepoch')," /* 2 */ |
| 479 | " estate," /* 3 */ |
| 480 | " esubject," /* 4 */ |
| 481 | " euser" /* 5 */ |
| 482 | " FROM emailbox" |
| 483 | ); |
| 484 | switch( atoi(PD("d","0")) ){ |
| 485 | case 0: { /* Show unread and read */ |
| 486 | blob_append_sql(&sql, " WHERE estate<=1"); |
| 487 | break; |
| 488 | } |
| 489 | case 1: { /* Unread messages only */ |
| @@ -497,11 +606,10 @@ | |
| 497 | case 3: { /* Everything */ |
| 498 | blob_append_sql(&sql, " WHERE 1"); |
| 499 | break; |
| 500 | } |
| 501 | } |
| 502 | |
| 503 | if( showAll ){ |
| 504 | style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0)); |
| 505 | }else if( zUser!=0 ){ |
| 506 | style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); |
| 507 | if( fossil_strcmp(zUser, g.zLogin)!=0 ){ |
| @@ -516,14 +624,38 @@ | |
| 516 | if( g.perm.Admin ){ |
| 517 | style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); |
| 518 | } |
| 519 | blob_append_sql(&sql, " AND euser=%Q", g.zLogin); |
| 520 | } |
| 521 | blob_append_sql(&sql, " ORDER BY edate DESC limit 50"); |
| 522 | db_prepare_blob(&q, &sql); |
| 523 | blob_reset(&sql); |
| 524 | @ <form action="%R/webmail" method="POST"> |
| 525 | @ <table> |
| 526 | while( db_step(&q)==SQLITE_ROW ){ |
| 527 | const char *zId = db_column_text(&q,0); |
| 528 | const char *zFrom = db_column_text(&q, 1); |
| 529 | const char *zDate = db_column_text(&q, 2); |
| @@ -540,7 +672,8 @@ | |
| 540 | @ </tr> |
| 541 | } |
| 542 | db_finalize(&q); |
| 543 | @ </table> |
| 544 | @ </form> |
| 545 | style_footer(); |
| 546 | } |
| 547 |
| --- src/webmail.c | |
| +++ src/webmail.c | |
| @@ -352,10 +352,164 @@ | |
| 352 | } |
| 353 | } |
| 354 | emailtoc_free(p); |
| 355 | blob_reset(&email); |
| 356 | } |
| 357 | |
| 358 | /* |
| 359 | ** Paint a page showing a single email message |
| 360 | */ |
| 361 | static void webmail_show_one_message( |
| 362 | HQuery *pUrl, /* Calling context */ |
| 363 | int emailid, /* emailbox.ebid to display */ |
| 364 | const char *zUser /* User who owns it, or NULL if does not matter */ |
| 365 | ){ |
| 366 | Blob sql; |
| 367 | Stmt q; |
| 368 | int eState = -1; |
| 369 | char zENum[30]; |
| 370 | style_submenu_element("Index", "%s", url_render(pUrl,"id",0,0,0)); |
| 371 | blob_init(&sql, 0, 0); |
| 372 | db_begin_transaction(); |
| 373 | blob_append_sql(&sql, |
| 374 | "SELECT decompress(etxt), estate" |
| 375 | " FROM emailblob, emailbox" |
| 376 | " WHERE emailid=emsgid AND ebid=%d", |
| 377 | emailid |
| 378 | ); |
| 379 | if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser); |
| 380 | db_prepare_blob(&q, &sql); |
| 381 | blob_reset(&sql); |
| 382 | style_header("Message %d",emailid); |
| 383 | if( db_step(&q)==SQLITE_ROW ){ |
| 384 | Blob msg = db_column_text_as_blob(&q, 0); |
| 385 | int eFormat = atoi(PD("f","0")); |
| 386 | eState = db_column_int(&q, 1); |
| 387 | url_add_parameter(pUrl, "id", P("id")); |
| 388 | if( eFormat==1 ){ |
| 389 | @ <pre>%h(db_column_text(&q, 0))</pre> |
| 390 | style_submenu_element("Decoded", "%s", url_render(pUrl,"f",0,0,0)); |
| 391 | }else{ |
| 392 | EmailToc *p = emailtoc_from_email(&msg); |
| 393 | int i, j; |
| 394 | style_submenu_element("Raw", "%s", url_render(pUrl,"f","1",0,0)); |
| 395 | @ <p> |
| 396 | for(i=0; i<p->nHdr; i++){ |
| 397 | char *z = p->azHdr[i]; |
| 398 | email_hdr_unfold(z); |
| 399 | for(j=0; z[j] && z[j]!=':'; j++){} |
| 400 | if( z[j]!=':' ){ |
| 401 | @ %h(z)<br> |
| 402 | }else{ |
| 403 | z[j] = 0; |
| 404 | @ <b>%h(z):</b> %h(z+j+1)<br> |
| 405 | } |
| 406 | } |
| 407 | for(i=0; i<p->nBody; i++){ |
| 408 | @ <hr><b>Messsage Body #%d(i): %h(p->aBody[i].zMimetype) \ |
| 409 | if( p->aBody[i].zFilename ){ |
| 410 | @ "%h(p->aBody[i].zFilename)" |
| 411 | } |
| 412 | @ </b> |
| 413 | if( strncmp(p->aBody[i].zMimetype, "text/", 5)!=0 ) continue; |
| 414 | switch( p->aBody[i].encoding ){ |
| 415 | case EMAILENC_B64: { |
| 416 | int n = 0; |
| 417 | decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); |
| 418 | break; |
| 419 | } |
| 420 | case EMAILENC_QUOTED: { |
| 421 | int n = 0; |
| 422 | decodeQuotedPrintable(p->aBody[i].zContent, &n); |
| 423 | break; |
| 424 | } |
| 425 | } |
| 426 | @ <pre>%h(p->aBody[i].zContent)</pre> |
| 427 | } |
| 428 | } |
| 429 | } |
| 430 | db_finalize(&q); |
| 431 | |
| 432 | if( eState==0 ){ |
| 433 | /* If is message is currently Unread, change it to Read */ |
| 434 | blob_append_sql(&sql, |
| 435 | "UPDATE emailbox SET estate=1 " |
| 436 | " WHERE estate=0 AND ebid=%d", |
| 437 | emailid |
| 438 | ); |
| 439 | if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser); |
| 440 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 441 | blob_reset(&sql); |
| 442 | eState = 1; |
| 443 | } |
| 444 | |
| 445 | url_add_parameter(pUrl, "id", 0); |
| 446 | sqlite3_snprintf(sizeof(zENum), zENum, "e%d", emailid); |
| 447 | if( eState==2 ){ |
| 448 | style_submenu_element("Undelete","%s", |
| 449 | url_render(pUrl,"read","1",zENum,"1")); |
| 450 | } |
| 451 | if( eState==1 ){ |
| 452 | style_submenu_element("Delete", "%s", |
| 453 | url_render(pUrl,"trash","1",zENum,"1")); |
| 454 | style_submenu_element("Mark As Unread", "%s", |
| 455 | url_render(pUrl,"unread","1",zENum,"1")); |
| 456 | } |
| 457 | |
| 458 | db_end_transaction(0); |
| 459 | style_footer(); |
| 460 | return; |
| 461 | } |
| 462 | |
| 463 | /* |
| 464 | ** Scan the query parameters looking for parameters with name of the |
| 465 | ** form "eN" where N is an integer. For all such integers, change |
| 466 | ** the state of every emailbox entry with ebid==N to eStateNew provided |
| 467 | ** that either zUser is NULL or matches. |
| 468 | */ |
| 469 | static void webmail_change_state(int eNewState, const char *zUser){ |
| 470 | Blob sql; |
| 471 | int sep = '('; |
| 472 | int i; |
| 473 | const char *zName; |
| 474 | int n; |
| 475 | if( !cgi_csrf_safe(0) ) return; |
| 476 | blob_init(&sql, 0, 0); |
| 477 | blob_append_sql(&sql, "UPDATE emailbox SET estate=%d WHERE ebid IN ", |
| 478 | eNewState); |
| 479 | for(i=0; (zName = cgi_parameter_name(i))!=0; i++){ |
| 480 | if( zName[0]!='e' ) continue; |
| 481 | if( !fossil_isdigit(zName[1]) ) continue; |
| 482 | n = atoi(zName+1); |
| 483 | blob_append_sql(&sql, "%c%d", sep, n); |
| 484 | sep = ','; |
| 485 | } |
| 486 | if( zUser ){ |
| 487 | blob_append_sql(&sql, ") AND euser=%Q", zUser); |
| 488 | }else{ |
| 489 | blob_append_sql(&sql, ")"); |
| 490 | } |
| 491 | if( sep==',' ){ |
| 492 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 493 | } |
| 494 | blob_reset(&sql); |
| 495 | } |
| 496 | |
| 497 | |
| 498 | /* |
| 499 | ** Add the select/option box to the timeline submenu that shows |
| 500 | ** which messages to include in the index. |
| 501 | */ |
| 502 | static void webmail_d_submenu(void){ |
| 503 | static const char *az[] = { |
| 504 | "0", "InBox", |
| 505 | "1", "Unread", |
| 506 | "2", "Trash", |
| 507 | "3", "Everything", |
| 508 | }; |
| 509 | style_submenu_multichoice("d", sizeof(az)/(2*sizeof(az[0])), az, 0); |
| 510 | } |
| 511 | |
| 512 | /* |
| 513 | ** WEBPAGE: webmail |
| 514 | ** |
| 515 | ** This page can be used to read content from the EMAILBOX table |
| @@ -378,10 +532,16 @@ | |
| 532 | int emailid; |
| 533 | Stmt q; |
| 534 | Blob sql; |
| 535 | int showAll = 0; |
| 536 | const char *zUser = 0; |
| 537 | int d = 0; /* Display mode. 0..3. d= query parameter */ |
| 538 | int pg = 0; /* Page number */ |
| 539 | int N = 50; /* Results per page */ |
| 540 | int got; /* Number of results on this page */ |
| 541 | char zPPg[30]; /* Previous page */ |
| 542 | char zNPg[30]; /* Next page */ |
| 543 | HQuery url; |
| 544 | login_check_credentials(); |
| 545 | if( !login_is_individual() ){ |
| 546 | login_needed(0); |
| 547 | return; |
| @@ -402,88 +562,37 @@ | |
| 562 | if( fossil_strcmp(zUser,"*")==0 ){ |
| 563 | showAll = 1; |
| 564 | zUser = 0; |
| 565 | } |
| 566 | } |
| 567 | }else{ |
| 568 | zUser = g.zLogin; |
| 569 | } |
| 570 | if( P("d") ) url_add_parameter(&url, "d", P("d")); |
| 571 | if( emailid>0 ){ |
| 572 | webmail_show_one_message(&url, emailid, zUser); |
| 573 | return; |
| 574 | } |
| 575 | style_header("Webmail"); |
| 576 | webmail_d_submenu(); |
| 577 | db_begin_transaction(); |
| 578 | if( P("trash")!=0 ) webmail_change_state(2,zUser); |
| 579 | if( P("unread")!=0 ) webmail_change_state(0,zUser); |
| 580 | if( P("read")!=0 ) webmail_change_state(1,zUser); |
| 581 | blob_init(&sql, 0, 0); |
| 582 | blob_append_sql(&sql, |
| 583 | "CREATE TEMP TABLE tmbox AS " |
| 584 | "SELECT ebid," /* 0 */ |
| 585 | " efrom," /* 1 */ |
| 586 | " datetime(edate,'unixepoch')," /* 2 */ |
| 587 | " estate," /* 3 */ |
| 588 | " esubject," /* 4 */ |
| 589 | " euser" /* 5 */ |
| 590 | " FROM emailbox" |
| 591 | ); |
| 592 | d = atoi(PD("d","0")); |
| 593 | switch( d ){ |
| 594 | case 0: { /* Show unread and read */ |
| 595 | blob_append_sql(&sql, " WHERE estate<=1"); |
| 596 | break; |
| 597 | } |
| 598 | case 1: { /* Unread messages only */ |
| @@ -497,11 +606,10 @@ | |
| 606 | case 3: { /* Everything */ |
| 607 | blob_append_sql(&sql, " WHERE 1"); |
| 608 | break; |
| 609 | } |
| 610 | } |
| 611 | if( showAll ){ |
| 612 | style_submenu_element("My Emails", "%s", url_render(&url,"user",0,0,0)); |
| 613 | }else if( zUser!=0 ){ |
| 614 | style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); |
| 615 | if( fossil_strcmp(zUser, g.zLogin)!=0 ){ |
| @@ -516,14 +624,38 @@ | |
| 624 | if( g.perm.Admin ){ |
| 625 | style_submenu_element("All Users", "%s", url_render(&url,"user","*",0,0)); |
| 626 | } |
| 627 | blob_append_sql(&sql, " AND euser=%Q", g.zLogin); |
| 628 | } |
| 629 | pg = atoi(PD("pg","0")); |
| 630 | blob_append_sql(&sql, " ORDER BY edate DESC limit %d offset %d", N+1, pg*N); |
| 631 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 632 | got = db_int(0, "SELECT count(*) FROM tmbox"); |
| 633 | db_prepare(&q, "SELECT * FROM tmbox LIMIT %d", N); |
| 634 | blob_reset(&sql); |
| 635 | @ <form action="%R/webmail" method="POST"> |
| 636 | @ <table border="0" width="100%%"> |
| 637 | @ <tr><td align="left"> |
| 638 | if( d==2 ){ |
| 639 | @ <input type="submit" name="read" value="Undelete"> |
| 640 | }else{ |
| 641 | @ <input type="submit" name="trash", value="Delete"> |
| 642 | if( d!=1 ){ |
| 643 | @ <input type="submit" name="unread" value="Mark as unread"> |
| 644 | } |
| 645 | @ <input type="submit" name="read" value="Mark as read"> |
| 646 | } |
| 647 | @ </td><td align="right"> |
| 648 | if( pg>0 ){ |
| 649 | sqlite3_snprintf(sizeof(zPPg), zPPg, "%d", pg-1); |
| 650 | @ <a href="%s(url_render(&url,"pg",zPPg,0,0))">< Newer</a> |
| 651 | } |
| 652 | if( got>50 ){ |
| 653 | sqlite3_snprintf(sizeof(zNPg),zNPg,"%d",pg+1); |
| 654 | @ <a href="%s(url_render(&url,"pg",zNPg,0,0))">Older ></a></td> |
| 655 | } |
| 656 | @ </table> |
| 657 | @ <table> |
| 658 | while( db_step(&q)==SQLITE_ROW ){ |
| 659 | const char *zId = db_column_text(&q,0); |
| 660 | const char *zFrom = db_column_text(&q, 1); |
| 661 | const char *zDate = db_column_text(&q, 2); |
| @@ -540,7 +672,8 @@ | |
| 672 | @ </tr> |
| 673 | } |
| 674 | db_finalize(&q); |
| 675 | @ </table> |
| 676 | @ </form> |
| 677 | style_footer(); |
| 678 | db_end_transaction(0); |
| 679 | } |
| 680 |