Fossil SCM
Fully function email message decoder now in place.
Commit
d0ae0898ded03e032ed1df2447ce4b37c6bfa99c3b3b0c7f042aec1aba61d31e
Parent
ec3fccd3b236135…
2 files changed
+68
-35
+20
-1
+68
-35
| --- src/encode.c | ||
| +++ src/encode.c | ||
| @@ -492,10 +492,50 @@ | ||
| 492 | 492 | fossil_print("%s\n", z); |
| 493 | 493 | free(z); |
| 494 | 494 | } |
| 495 | 495 | } |
| 496 | 496 | |
| 497 | + | |
| 498 | +/* Decode base64 text. Write the output into zData. The caller | |
| 499 | +** must ensure that zData is large enough. It is ok for z64 and | |
| 500 | +** zData to be the same buffer. In other words, it is ok to decode | |
| 501 | +** in-place. A zero terminator is always placed at the end of zData. | |
| 502 | +*/ | |
| 503 | +void decodeBase64(const char *z64, int *pnByte, char *zData){ | |
| 504 | + const unsigned char *zIn = (const unsigned char*)z64; | |
| 505 | + int i, j, k; | |
| 506 | + int x[4]; | |
| 507 | + static int isInit = 0; | |
| 508 | + static signed char trans[256]; | |
| 509 | + | |
| 510 | + if( !isInit ){ | |
| 511 | + for(i=0; i<256; i++){ trans[i] = -1; } | |
| 512 | + for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } | |
| 513 | + isInit = 1; | |
| 514 | + } | |
| 515 | + for(j=k=0; zIn[0]; zIn++){ | |
| 516 | + int v = trans[zIn[0]]; | |
| 517 | + if( v>=0 ){ | |
| 518 | + x[k++] = v; | |
| 519 | + if( k==4 ){ | |
| 520 | + zData[j++] = ((x[0]<<2) & 0xfc) | ((x[1]>>4) & 0x03); | |
| 521 | + zData[j++] = ((x[1]<<4) & 0xf0) | ((x[2]>>2) & 0x0f); | |
| 522 | + zData[j++] = ((x[2]<<6) & 0xc0) | (x[3] & 0x3f); | |
| 523 | + k = 0; | |
| 524 | + } | |
| 525 | + } | |
| 526 | + } | |
| 527 | + if( k>=2 ){ | |
| 528 | + zData[j++] = ((x[0]<<2) & 0xfc) | ((x[1]>>4) & 0x03); | |
| 529 | + if( k==3 ){ | |
| 530 | + zData[j++] = ((x[1]<<4) & 0xf0) | ((x[2]>>2) & 0x0f); | |
| 531 | + } | |
| 532 | + } | |
| 533 | + zData[j] = 0; | |
| 534 | + *pnByte = j; | |
| 535 | +} | |
| 536 | + | |
| 497 | 537 | |
| 498 | 538 | /* |
| 499 | 539 | ** This function treats its input as a base-64 string and returns the |
| 500 | 540 | ** decoded value of that string. Characters of input that are not |
| 501 | 541 | ** valid base-64 characters (such as spaces and newlines) are ignored. |
| @@ -504,46 +544,14 @@ | ||
| 504 | 544 | ** |
| 505 | 545 | ** The number of bytes decoded is returned in *pnByte |
| 506 | 546 | */ |
| 507 | 547 | char *decode64(const char *z64, int *pnByte){ |
| 508 | 548 | char *zData; |
| 509 | - int n64; | |
| 510 | - int i, j; | |
| 511 | - int a, b, c, d; | |
| 512 | - static int isInit = 0; | |
| 513 | - static int trans[128]; | |
| 514 | - | |
| 515 | - if( !isInit ){ | |
| 516 | - for(i=0; i<128; i++){ trans[i] = 0; } | |
| 517 | - for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } | |
| 518 | - isInit = 1; | |
| 519 | - } | |
| 520 | - n64 = strlen(z64); | |
| 549 | + int n64 = (int)strlen(z64); | |
| 521 | 550 | while( n64>0 && z64[n64-1]=='=' ) n64--; |
| 522 | 551 | zData = fossil_malloc( (n64*3)/4 + 4 ); |
| 523 | - for(i=j=0; i+3<n64; i+=4){ | |
| 524 | - a = trans[z64[i] & 0x7f]; | |
| 525 | - b = trans[z64[i+1] & 0x7f]; | |
| 526 | - c = trans[z64[i+2] & 0x7f]; | |
| 527 | - d = trans[z64[i+3] & 0x7f]; | |
| 528 | - zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03); | |
| 529 | - zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f); | |
| 530 | - zData[j++] = ((c<<6) & 0xc0) | (d & 0x3f); | |
| 531 | - } | |
| 532 | - if( i+2<n64 ){ | |
| 533 | - a = trans[z64[i] & 0x7f]; | |
| 534 | - b = trans[z64[i+1] & 0x7f]; | |
| 535 | - c = trans[z64[i+2] & 0x7f]; | |
| 536 | - zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03); | |
| 537 | - zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f); | |
| 538 | - }else if( i+1<n64 ){ | |
| 539 | - a = trans[z64[i] & 0x7f]; | |
| 540 | - b = trans[z64[i+1] & 0x7f]; | |
| 541 | - zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03); | |
| 542 | - } | |
| 543 | - zData[j] = 0; | |
| 544 | - *pnByte = j; | |
| 552 | + decodeBase64(z64, pnByte, zData); | |
| 545 | 553 | return zData; |
| 546 | 554 | } |
| 547 | 555 | |
| 548 | 556 | /* |
| 549 | 557 | ** COMMAND: test-decode64 |
| @@ -554,11 +562,11 @@ | ||
| 554 | 562 | char *z; |
| 555 | 563 | int i, n; |
| 556 | 564 | for(i=2; i<g.argc; i++){ |
| 557 | 565 | z = decode64(g.argv[i], &n); |
| 558 | 566 | fossil_print("%d: %s\n", n, z); |
| 559 | - free(z); | |
| 567 | + fossil_free(z); | |
| 560 | 568 | } |
| 561 | 569 | } |
| 562 | 570 | |
| 563 | 571 | /* |
| 564 | 572 | ** The base-16 encoding using the following characters: |
| @@ -654,10 +662,35 @@ | ||
| 654 | 662 | while( *z && n-- ){ |
| 655 | 663 | *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; |
| 656 | 664 | z++; |
| 657 | 665 | } |
| 658 | 666 | } |
| 667 | + | |
| 668 | +/* | |
| 669 | +** Decode a string encoded using "quoted-printable". | |
| 670 | +** | |
| 671 | +** (1) "=" followed by two hex digits becomes a single | |
| 672 | +** byte specified by the two digits | |
| 673 | +** | |
| 674 | +** The decoding is done in-place. | |
| 675 | +*/ | |
| 676 | +void decodeQuotedPrintable(char *z, int *pnByte){ | |
| 677 | + int i, j, c; | |
| 678 | + for(i=j=0; (c = z[i])!=0; i++){ | |
| 679 | + if( c=='=' ){ | |
| 680 | + if( z[i+1]!='\r' ){ | |
| 681 | + decode16((unsigned char*)&z[i+1], (unsigned char*)&z[j], 2); | |
| 682 | + j++; | |
| 683 | + } | |
| 684 | + i += 2; | |
| 685 | + }else{ | |
| 686 | + z[j++] = c; | |
| 687 | + } | |
| 688 | + } | |
| 689 | + if( pnByte ) *pnByte = j; | |
| 690 | + z[j] = 0; | |
| 691 | +} | |
| 659 | 692 | |
| 660 | 693 | /* Randomness used for XOR-ing by the obscure() and unobscure() routines */ |
| 661 | 694 | static const unsigned char aObscurer[16] = { |
| 662 | 695 | 0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86, |
| 663 | 696 | 0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85 |
| 664 | 697 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -492,10 +492,50 @@ | |
| 492 | fossil_print("%s\n", z); |
| 493 | free(z); |
| 494 | } |
| 495 | } |
| 496 | |
| 497 | |
| 498 | /* |
| 499 | ** This function treats its input as a base-64 string and returns the |
| 500 | ** decoded value of that string. Characters of input that are not |
| 501 | ** valid base-64 characters (such as spaces and newlines) are ignored. |
| @@ -504,46 +544,14 @@ | |
| 504 | ** |
| 505 | ** The number of bytes decoded is returned in *pnByte |
| 506 | */ |
| 507 | char *decode64(const char *z64, int *pnByte){ |
| 508 | char *zData; |
| 509 | int n64; |
| 510 | int i, j; |
| 511 | int a, b, c, d; |
| 512 | static int isInit = 0; |
| 513 | static int trans[128]; |
| 514 | |
| 515 | if( !isInit ){ |
| 516 | for(i=0; i<128; i++){ trans[i] = 0; } |
| 517 | for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } |
| 518 | isInit = 1; |
| 519 | } |
| 520 | n64 = strlen(z64); |
| 521 | while( n64>0 && z64[n64-1]=='=' ) n64--; |
| 522 | zData = fossil_malloc( (n64*3)/4 + 4 ); |
| 523 | for(i=j=0; i+3<n64; i+=4){ |
| 524 | a = trans[z64[i] & 0x7f]; |
| 525 | b = trans[z64[i+1] & 0x7f]; |
| 526 | c = trans[z64[i+2] & 0x7f]; |
| 527 | d = trans[z64[i+3] & 0x7f]; |
| 528 | zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03); |
| 529 | zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f); |
| 530 | zData[j++] = ((c<<6) & 0xc0) | (d & 0x3f); |
| 531 | } |
| 532 | if( i+2<n64 ){ |
| 533 | a = trans[z64[i] & 0x7f]; |
| 534 | b = trans[z64[i+1] & 0x7f]; |
| 535 | c = trans[z64[i+2] & 0x7f]; |
| 536 | zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03); |
| 537 | zData[j++] = ((b<<4) & 0xf0) | ((c>>2) & 0x0f); |
| 538 | }else if( i+1<n64 ){ |
| 539 | a = trans[z64[i] & 0x7f]; |
| 540 | b = trans[z64[i+1] & 0x7f]; |
| 541 | zData[j++] = ((a<<2) & 0xfc) | ((b>>4) & 0x03); |
| 542 | } |
| 543 | zData[j] = 0; |
| 544 | *pnByte = j; |
| 545 | return zData; |
| 546 | } |
| 547 | |
| 548 | /* |
| 549 | ** COMMAND: test-decode64 |
| @@ -554,11 +562,11 @@ | |
| 554 | char *z; |
| 555 | int i, n; |
| 556 | for(i=2; i<g.argc; i++){ |
| 557 | z = decode64(g.argv[i], &n); |
| 558 | fossil_print("%d: %s\n", n, z); |
| 559 | free(z); |
| 560 | } |
| 561 | } |
| 562 | |
| 563 | /* |
| 564 | ** The base-16 encoding using the following characters: |
| @@ -654,10 +662,35 @@ | |
| 654 | while( *z && n-- ){ |
| 655 | *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; |
| 656 | z++; |
| 657 | } |
| 658 | } |
| 659 | |
| 660 | /* Randomness used for XOR-ing by the obscure() and unobscure() routines */ |
| 661 | static const unsigned char aObscurer[16] = { |
| 662 | 0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86, |
| 663 | 0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85 |
| 664 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -492,10 +492,50 @@ | |
| 492 | fossil_print("%s\n", z); |
| 493 | free(z); |
| 494 | } |
| 495 | } |
| 496 | |
| 497 | |
| 498 | /* Decode base64 text. Write the output into zData. The caller |
| 499 | ** must ensure that zData is large enough. It is ok for z64 and |
| 500 | ** zData to be the same buffer. In other words, it is ok to decode |
| 501 | ** in-place. A zero terminator is always placed at the end of zData. |
| 502 | */ |
| 503 | void decodeBase64(const char *z64, int *pnByte, char *zData){ |
| 504 | const unsigned char *zIn = (const unsigned char*)z64; |
| 505 | int i, j, k; |
| 506 | int x[4]; |
| 507 | static int isInit = 0; |
| 508 | static signed char trans[256]; |
| 509 | |
| 510 | if( !isInit ){ |
| 511 | for(i=0; i<256; i++){ trans[i] = -1; } |
| 512 | for(i=0; zBase[i]; i++){ trans[zBase[i] & 0x7f] = i; } |
| 513 | isInit = 1; |
| 514 | } |
| 515 | for(j=k=0; zIn[0]; zIn++){ |
| 516 | int v = trans[zIn[0]]; |
| 517 | if( v>=0 ){ |
| 518 | x[k++] = v; |
| 519 | if( k==4 ){ |
| 520 | zData[j++] = ((x[0]<<2) & 0xfc) | ((x[1]>>4) & 0x03); |
| 521 | zData[j++] = ((x[1]<<4) & 0xf0) | ((x[2]>>2) & 0x0f); |
| 522 | zData[j++] = ((x[2]<<6) & 0xc0) | (x[3] & 0x3f); |
| 523 | k = 0; |
| 524 | } |
| 525 | } |
| 526 | } |
| 527 | if( k>=2 ){ |
| 528 | zData[j++] = ((x[0]<<2) & 0xfc) | ((x[1]>>4) & 0x03); |
| 529 | if( k==3 ){ |
| 530 | zData[j++] = ((x[1]<<4) & 0xf0) | ((x[2]>>2) & 0x0f); |
| 531 | } |
| 532 | } |
| 533 | zData[j] = 0; |
| 534 | *pnByte = j; |
| 535 | } |
| 536 | |
| 537 | |
| 538 | /* |
| 539 | ** This function treats its input as a base-64 string and returns the |
| 540 | ** decoded value of that string. Characters of input that are not |
| 541 | ** valid base-64 characters (such as spaces and newlines) are ignored. |
| @@ -504,46 +544,14 @@ | |
| 544 | ** |
| 545 | ** The number of bytes decoded is returned in *pnByte |
| 546 | */ |
| 547 | char *decode64(const char *z64, int *pnByte){ |
| 548 | char *zData; |
| 549 | int n64 = (int)strlen(z64); |
| 550 | while( n64>0 && z64[n64-1]=='=' ) n64--; |
| 551 | zData = fossil_malloc( (n64*3)/4 + 4 ); |
| 552 | decodeBase64(z64, pnByte, zData); |
| 553 | return zData; |
| 554 | } |
| 555 | |
| 556 | /* |
| 557 | ** COMMAND: test-decode64 |
| @@ -554,11 +562,11 @@ | |
| 562 | char *z; |
| 563 | int i, n; |
| 564 | for(i=2; i<g.argc; i++){ |
| 565 | z = decode64(g.argv[i], &n); |
| 566 | fossil_print("%d: %s\n", n, z); |
| 567 | fossil_free(z); |
| 568 | } |
| 569 | } |
| 570 | |
| 571 | /* |
| 572 | ** The base-16 encoding using the following characters: |
| @@ -654,10 +662,35 @@ | |
| 662 | while( *z && n-- ){ |
| 663 | *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; |
| 664 | z++; |
| 665 | } |
| 666 | } |
| 667 | |
| 668 | /* |
| 669 | ** Decode a string encoded using "quoted-printable". |
| 670 | ** |
| 671 | ** (1) "=" followed by two hex digits becomes a single |
| 672 | ** byte specified by the two digits |
| 673 | ** |
| 674 | ** The decoding is done in-place. |
| 675 | */ |
| 676 | void decodeQuotedPrintable(char *z, int *pnByte){ |
| 677 | int i, j, c; |
| 678 | for(i=j=0; (c = z[i])!=0; i++){ |
| 679 | if( c=='=' ){ |
| 680 | if( z[i+1]!='\r' ){ |
| 681 | decode16((unsigned char*)&z[i+1], (unsigned char*)&z[j], 2); |
| 682 | j++; |
| 683 | } |
| 684 | i += 2; |
| 685 | }else{ |
| 686 | z[j++] = c; |
| 687 | } |
| 688 | } |
| 689 | if( pnByte ) *pnByte = j; |
| 690 | z[j] = 0; |
| 691 | } |
| 692 | |
| 693 | /* Randomness used for XOR-ing by the obscure() and unobscure() routines */ |
| 694 | static const unsigned char aObscurer[16] = { |
| 695 | 0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86, |
| 696 | 0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85 |
| 697 |
+20
-1
| --- src/webmail.c | ||
| +++ src/webmail.c | ||
| @@ -267,11 +267,30 @@ | ||
| 267 | 267 | fossil_print("%3d: %s\n", i, p->azHdr[i]); |
| 268 | 268 | } |
| 269 | 269 | for(i=0; i<p->nBody; i++){ |
| 270 | 270 | fossil_print("\nBODY %d mime \"%s\" encoding %d:\n", |
| 271 | 271 | i, p->aBody[i].zMimetype, p->aBody[i].encoding); |
| 272 | - fossil_print("%s\n", p->aBody[i].zContent); | |
| 272 | + switch( p->aBody[i].encoding ){ | |
| 273 | + case EMAILENC_B64: { | |
| 274 | + int n = 0; | |
| 275 | + decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); | |
| 276 | + fossil_print("%s", p->aBody[i].zContent); | |
| 277 | + if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n"); | |
| 278 | + break; | |
| 279 | + } | |
| 280 | + case EMAILENC_QUOTED: { | |
| 281 | + int n = 0; | |
| 282 | + decodeQuotedPrintable(p->aBody[i].zContent, &n); | |
| 283 | + fossil_print("%s", p->aBody[i].zContent); | |
| 284 | + if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n"); | |
| 285 | + break; | |
| 286 | + } | |
| 287 | + default: { | |
| 288 | + fossil_print("%s\n", p->aBody[i].zContent); | |
| 289 | + break; | |
| 290 | + } | |
| 291 | + } | |
| 273 | 292 | } |
| 274 | 293 | emailtoc_free(p); |
| 275 | 294 | blob_reset(&email); |
| 276 | 295 | } |
| 277 | 296 | |
| 278 | 297 |
| --- src/webmail.c | |
| +++ src/webmail.c | |
| @@ -267,11 +267,30 @@ | |
| 267 | fossil_print("%3d: %s\n", i, p->azHdr[i]); |
| 268 | } |
| 269 | for(i=0; i<p->nBody; i++){ |
| 270 | fossil_print("\nBODY %d mime \"%s\" encoding %d:\n", |
| 271 | i, p->aBody[i].zMimetype, p->aBody[i].encoding); |
| 272 | fossil_print("%s\n", p->aBody[i].zContent); |
| 273 | } |
| 274 | emailtoc_free(p); |
| 275 | blob_reset(&email); |
| 276 | } |
| 277 | |
| 278 |
| --- src/webmail.c | |
| +++ src/webmail.c | |
| @@ -267,11 +267,30 @@ | |
| 267 | fossil_print("%3d: %s\n", i, p->azHdr[i]); |
| 268 | } |
| 269 | for(i=0; i<p->nBody; i++){ |
| 270 | fossil_print("\nBODY %d mime \"%s\" encoding %d:\n", |
| 271 | i, p->aBody[i].zMimetype, p->aBody[i].encoding); |
| 272 | switch( p->aBody[i].encoding ){ |
| 273 | case EMAILENC_B64: { |
| 274 | int n = 0; |
| 275 | decodeBase64(p->aBody[i].zContent, &n, p->aBody[i].zContent); |
| 276 | fossil_print("%s", p->aBody[i].zContent); |
| 277 | if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n"); |
| 278 | break; |
| 279 | } |
| 280 | case EMAILENC_QUOTED: { |
| 281 | int n = 0; |
| 282 | decodeQuotedPrintable(p->aBody[i].zContent, &n); |
| 283 | fossil_print("%s", p->aBody[i].zContent); |
| 284 | if( n && p->aBody[i].zContent[n-1]!='\n' ) fossil_print("\n"); |
| 285 | break; |
| 286 | } |
| 287 | default: { |
| 288 | fossil_print("%s\n", p->aBody[i].zContent); |
| 289 | break; |
| 290 | } |
| 291 | } |
| 292 | } |
| 293 | emailtoc_free(p); |
| 294 | blob_reset(&email); |
| 295 | } |
| 296 | |
| 297 |