summaryrefslogtreecommitdiff
path: root/src/quotprint.c
diff options
context:
space:
mode:
authorAndreas Fankhauser hiddenalpha.ch2026-03-01 23:24:58 +0100
committerAndreas Fankhauser hiddenalpha.ch2026-03-01 23:24:58 +0100
commite28111ce46c7bf771c72ec02184cbb34e4aa7086 (patch)
tree97b2212b7acc8f278cdf850ca6d010ce10c364a6 /src/quotprint.c
parent3267012a05118edf0c2fbb226ef23fd693862422 (diff)
downloadquotprint-e28111ce46c7bf771c72ec02184cbb34e4aa7086.tar.gz
quotprint-e28111ce46c7bf771c72ec02184cbb34e4aa7086.zip
encoder hardening for edge-cases
Diffstat (limited to 'src/quotprint.c')
-rw-r--r--src/quotprint.c122
1 files changed, 64 insertions, 58 deletions
diff --git a/src/quotprint.c b/src/quotprint.c
index 9e1caff..c5c7719 100644
--- a/src/quotprint.c
+++ b/src/quotprint.c
@@ -12,6 +12,7 @@
#define FLG_isEnc (1<<0)
#define FLG_isDec (1<<1)
#define FLG_isError (1<<2)
+#define FLG_isBinary (1<<3)
#ifndef BUF_CAP /* aka "buffer capacity" */
# define BUF_CAP 0x10000
@@ -91,8 +92,13 @@ printHelp( void ){
" quotprint - QuotedPrintable codec\n"
" Version: " STR_QUOT(PROJECT_VERSION) "\n"
" \n"
+ " About the quoted-printable encoding see:\n"
+ " https://www.rfc-editor.org/rfc/rfc2045#section-6.7\n"
+ " \n"
" -e `encode` to QuotedPrintable\n"
" -d `decode` from QuotedPrintable\n"
+ " -b When given to `-e`, will encode `binary` safe. Eg newlines will\n"
+ " be quoted always.\n"
" \n"
"\n");
}
@@ -123,6 +129,8 @@ nextArg:
this->flg |= FLG_isEnc;
}else if( !strcmp(arg, "-d") ){
this->flg |= FLG_isDec;
+ }else if( !strcmp(arg, "-b") ){
+ this->flg |= FLG_isBinary;
}else{
fprintf(stderr, "EINVAL: %s\n", arg);
return -1;
@@ -291,62 +299,57 @@ mainOfEncode( void*_ ){
if( iSrc >= LEN_SRC )
{ goto onSrcBufDrained; }
uint_fast8_t c = BUF_SRC[iSrc++];
- switch( c ){
-
- /*these are allowed to be raw*/
- case 0x20: /*space*/
- case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28:
- case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: /*!"#$%&'()*+,-./ */
- case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
- case 0x38: case 0x39: /* 0-9 */
- case 0x3A: case 0x3B: case 0x3C: /* :;< */
- case 0x3E: case 0x3F: case 0x40: /* >?@ */
- case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: case 0x48:
- case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F: case 0x50:
- case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58:
- case 0x59: case 0x5A: /* A-Z */
- case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60: /* [\]^_` */
- case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68:
- case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: case 0x70:
- case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78:
- case 0x79: case 0x7A: /* a-z */
- case 0x7B: case 0x7C: case 0x7D: case 0x7E: /* {|}~ */
- if( iDst - lineBeg >= 76 - 1 ){
- BUF_DST[iDst++] = '=';
- BUF_DST[iDst++] = '\r';
- BUF_DST[iDst++] = '\n';
- lineBeg = iDst;
- }
+ #define IS_WHITESP(C) (C == 0x09 || C == 0x0A || C == 0x0D || C == 0x20)
+ #define NEXT_SRC_CHR (BUF_SRC[iSrc])
+ #define IS_NEXT_CHR_PRESENT (iSrc + 1 < LEN_SRC)
+ #define IS_DST_SPACE_AT_LEAST(N) (iDst + (N) < DST_CAP)
+ #define IS_BINARY_MODE ((this->flg & FLG_isBinary)==0)
+ #define IS_EBCDIC_RISKY(C) (C == 0x21 || C == 0x23 || C == 0x24 || C == 0x34 \
+ || C == 0x40 || C == 0x5B || C == 0x5C || C == 0x5D || C == 0x5E || C == 0x60 \
+ || C == 0x7B || C == 0x7C || C == 0x7D || C == 0x7E)
+ int const isCrLfRaw = (c == 0x0D && IS_NEXT_CHR_PRESENT && IS_DST_SPACE_AT_LEAST(2)
+ && NEXT_SRC_CHR == 0x0A && IS_BINARY_MODE);
+ int const isRawOk = 0
+ /* A CRLF sequence if a special case, as it CAN be
+ * printed raw under some circumstances. */
+ || isCrLfRaw
+ /*print as-is. Risk is pretty low to get in trouble with these*/
+ || (c == 0x22)
+ || (c >= 0x25 && c <= 0x33)
+ || (c >= 0x35 && c <= 0x3C)
+ || (c >= 0x3E && c <= 0x3F)
+ || (c >= 0x41 && c <= 0x5A)
+ || (c >= 0x5F && c <= 0x5F)
+ || (c >= 0x61 && c <= 0x7A)
+ /*for EBCDIC, some chars are risky. For now, we'll encode them if binary
+ * mode given, as I do NOT want to introduce yet-another-bullshit-flag*/
+ || (IS_EBCDIC_RISKY(c) && !IS_BINARY_MODE)
+ /*space MUST NOT be at endOfLine, so check some more conditions*/
+ || (c == 0x20 && IS_NEXT_CHR_PRESENT && IS_DST_SPACE_AT_LEAST(4)
+ && !IS_WHITESP(NEXT_SRC_CHR))
+ ;
+ #undef IS_WHITESP
+ #undef NEXT_SRC_CHR
+ #undef IS_NEXT_CHR_PRESENT
+ #undef IS_DST_HAS_SPACE
+ #undef IS_EBCDIC_RISKY
+ int const nBytesOutput = ((isRawOk != 0) * 1 + (isRawOk == 0) * 3);
+ int const isSoftWrapNecessary = (iDst - lineBeg >= 76 - nBytesOutput);
+ if( isSoftWrapNecessary ){
+ BUF_DST[iDst++] = '=';
+ BUF_DST[iDst++] = '\r';
+ BUF_DST[iDst++] = '\n';
+ lineBeg = iDst;
+ }
+ if( isRawOk ){
BUF_DST[iDst++] = c;
- break;
- /* The CrLf sequence if a special case, as it CAN be printed raw. So lets
- * try if we have enough context (eg the next byte) to do it. */
- case 0x0D:;
- int const isNxtChrPresent = (iSrc + 1 < LEN_SRC);
- int const isDstHasSpace = (iDst + 2 < DST_CAP);
- if( isNxtChrPresent && isDstHasSpace && BUF_SRC[iSrc] == 0x0A ){
- /* we CAN print raw, as we know it IS a CrLf sequence! */
- BUF_DST[iDst++] = c;
- BUF_DST[iDst++] = BUF_SRC[iSrc++];
- lineBeg = iDst;
- break;
- }else{
- /* fall, to escape them the same way */
- }
- /* everything else has to go through escaping */
- default:
- if( iDst - lineBeg >= (76-3) ){
- BUF_DST[iDst++] = '=';
- BUF_DST[iDst++] = '\r';
- BUF_DST[iDst++] = '\n';
- lineBeg = iDst;
- }
- char const hx[] = "0123456789ABCDEF";
+ if( isCrLfRaw ) /* we have to add the LF octet too */
+ { BUF_DST[iDst++] = BUF_SRC[iSrc++]; }
+ }else{
+ static char const hx[] = "0123456789ABCDEF";
BUF_DST[iDst++] = '=';
BUF_DST[iDst++] = hx[c >> 4];
BUF_DST[iDst++] = hx[c & 0xF];
- break;
-
}
}
assert(!"unreachable");
@@ -415,6 +418,9 @@ mainOfEncode( void*_ ){
INLINE(int)
quotPrintDecode( App*this ){
assert(!"TODO_i5TKHBVqBa0lRhqy");
+ /* Cited from RFC: when decoding a Quoted-Printable body, any
+ * trailing white space on a line must be deleted, as it will
+ * necessarily have been added by intermediate transport agents */
}
@@ -427,6 +433,12 @@ run( App*this ){
LOGD("dstBufReadyToFill := dstBufOne.buf\n");
this->srcBufReadyToFill = this->srcBufOne.buf;
this->dstBufReadyToFill = this->dstBufOne.buf;
+ }/*launch a thread for reading*/{
+ err = pthread_create(&this->thrdRd, NULL, mainOfInputThread, this);
+ if( err ){
+ assert(err > 0);
+ err = -err; goto resolveWithErr;
+ }
}/*launch a thread for processing*/{
void *(*processor)(void*) = (this->flg & FLG_isEnc)
? mainOfEncode
@@ -437,12 +449,6 @@ run( App*this ){
assert(err > 0);
err = -err; goto resolveWithErr;
}
- }/*launch a thread for reading*/{
- err = pthread_create(&this->thrdRd, NULL, mainOfInputThread, this);
- if( err ){
- assert(err > 0);
- err = -err; goto resolveWithErr;
- }
}/*use main thread to do the output writing*/{
mainOfOutputThread(this);
err = 0;