diff --git a/src/sync_msg_cvt.c b/src/sync_msg_cvt.c index 472fcf6..1e49cf7 100644 --- a/src/sync_msg_cvt.c +++ b/src/sync_msg_cvt.c @@ -44,52 +44,82 @@ copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) uint in_len = vars->data.len; uint idx = 0, sbreak = 0, ebreak = 0, break2 = UINT_MAX; uint lines = 0, hdr_crs = 0, bdy_crs = 0, app_cr = 0, extra = 0; - uint add_subj = 0; + uint add_subj = 0, fix_tuid = 0, fix_subj = 0, fix_hdr = 0, end_hdr = 0; if (vars->srec) { - nloop: ; - uint start = idx; - uint line_cr = 0; - char pc = 0; - while (idx < in_len) { - char c = in_buf[idx++]; - if (c == '\n') { - if (pc == '\r') - line_cr = 1; - if (!ebreak && starts_with_upper( in_buf + start, (int)(in_len - start), "X-TUID: ", 8 )) { - extra = (sbreak = start) - (ebreak = idx); - if (!vars->minimal) - goto oke; - } else { - if (break2 == UINT_MAX && vars->minimal && - starts_with_upper( in_buf + start, (int)(in_len - start), "SUBJECT:", 8 )) { - break2 = start + 8; - if (break2 < in_len && in_buf[break2] == ' ') - break2++; - } - lines++; - hdr_crs += line_cr; + for (;;) { + uint start = idx; + uint line_cr = 0; + uint got_line = 0; + char pc = 0; + while (idx < in_len) { + char c = in_buf[idx++]; + if (c == '\n') { + if (pc == '\r') + line_cr = 1; + got_line = 1; + break; } - if (idx - line_cr - 1 == start) { - if (!ebreak) - sbreak = ebreak = start; - if (vars->minimal) { - in_len = idx; - if (break2 == UINT_MAX) { - break2 = start; - add_subj = 1; - } - } - goto oke; - } - goto nloop; + pc = c; } - pc = c; + if (!ebreak && starts_with_upper( in_buf + start, (int)(in_len - start), "X-TUID: ", 8 )) { + extra = (sbreak = start) - (ebreak = idx); + if (!vars->minimal) + break; + continue; + } + if (break2 == UINT_MAX && vars->minimal && + starts_with_upper( in_buf + start, (int)(in_len - start), "SUBJECT:", 8 )) { + break2 = start + 8; + if (break2 < in_len && in_buf[break2] == ' ') + break2++; + } + hdr_crs += line_cr; + if (got_line) { + lines++; + if (idx - line_cr - 1 != start) + continue; + // Empty line => end of headers + } else { + // The line is incomplete. + if (pc == '\r') + idx--; // For simplicity, move back before trailing CR + if (idx != start) { + // The line is non-empty, so schedule completing it + fix_hdr = 1; + // ... and put our headers after it. (It would seem easier + // to prepend them, as then we could avoid the fixing - but + // the line might be a continuation. We could also prepend + // it to _all_ pre-exiting headers, but then we would risk + // masking an (incorrectly present) leading 'From ' header.) + start = idx; + } + end_hdr = 1; + } + if (!ebreak) { + sbreak = ebreak = start; + fix_tuid = fix_hdr; + fix_hdr = 0; + } + if (vars->minimal) { + in_len = idx; + if (break2 == UINT_MAX) { + break2 = start; + add_subj = 1; + fix_subj = fix_hdr; + fix_hdr = 0; + } + } else { + fix_hdr = 0; + end_hdr = 0; + } + break; } - free( in_buf ); - return "has incomplete header"; - oke: app_cr = out_cr && (!in_cr || hdr_crs); + if (fix_tuid || fix_subj || fix_hdr) + extra += app_cr + 1; + if (end_hdr) + extra += app_cr + 1; extra += 8 + TUIDL + app_cr + 1; } if (out_cr != in_cr) { @@ -157,6 +187,8 @@ copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) } copy_msg_bytes( &out_buf, in_buf, &idx, sbreak, in_cr, out_cr ); + if (fix_tuid) + ADD_NL(); memcpy( out_buf, "X-TUID: ", 8 ); out_buf += 8; memcpy( out_buf, vars->srec->tuid, TUIDL ); @@ -170,6 +202,8 @@ copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) memcpy( out_buf, dummy_pfx, strlen(dummy_pfx) ); out_buf += strlen(dummy_pfx); } else { + if (fix_subj) + ADD_NL(); memcpy( out_buf, dummy_subj, strlen(dummy_subj) ); out_buf += strlen(dummy_subj); ADD_NL(); @@ -178,8 +212,14 @@ copy_msg_convert( int in_cr, int out_cr, copy_vars_t *vars ) } copy_msg_bytes( &out_buf, in_buf, &idx, in_len, in_cr, out_cr ); - if (vars->minimal) + if (vars->minimal) { + if (end_hdr) { + if (fix_hdr) + ADD_NL(); + ADD_NL(); + } memcpy( out_buf, dummy_msg_buf, dummy_msg_len ); + } free( in_buf ); return NULL; diff --git a/src/tst_msg_cvt.c b/src/tst_msg_cvt.c index 399c5c2..399c565 100644 --- a/src/tst_msg_cvt.c +++ b/src/tst_msg_cvt.c @@ -131,10 +131,13 @@ mintests( const char *name, const char *in, const char *out, int flagged ) } #define FROM "From: de\rvil\r\n" -#define TO "To: me\r\n" -#define IN_TUID "X-TUID: garbage\r\n" +#define R_TO "To: me" +#define TO R_TO "\r\n" +#define R_IN_TUID "X-TUID: garbage" +#define IN_TUID R_IN_TUID "\r\n" #define OUT_TUID "X-TUID: " TUID "\r\n" -#define SUBJECT "Subject: hell\r\n" +#define R_SUBJECT "Subject: hell" +#define SUBJECT R_SUBJECT "\r\n" #define PH_SUBJECT "Subject: [placeholder] hell\r\n" #define NO_SUBJECT "Subject: [placeholder] (No Subject)\r\n" #define BODY "\r\nHi,\r\n\r\n...\r\n" @@ -177,5 +180,79 @@ main( void ) scc out_tuid_from_subj_to[] = OUT_TUID FROM PH_SUBJECT TO PH_BODY; mintests( "tuid / from / subject / to", in_tuid_from_subj_to, out_tuid_from_subj_to, REGULAR ); + + scc in_from_to_b1[] = FROM TO; + fulltests( "from / to w/o end", in_from_to_b1, in_from_to_b1, AS_IS ); + scc out_from_to_b1[] = FROM TO OUT_TUID; + fulltests( "from / to w/o end", in_from_to_b1, out_from_to_b1, ADD_TUID ); + scc in_from_tuid_to_b1[] = FROM IN_TUID TO; + scc out_from_tuid_to_b1[] = FROM OUT_TUID TO; + fulltests( "from / tuid / to w/o end", in_from_tuid_to_b1, out_from_tuid_to_b1, ADD_TUID ); + scc in_from_to_tuid_b1[] = FROM TO IN_TUID; + scc out_from_to_tuid_b1[] = FROM TO OUT_TUID; + fulltests( "from / to / tuid w/o end", in_from_to_tuid_b1, out_from_to_tuid_b1, ADD_TUID ); + + mintests( "from / to w/o end", in_from_to_b1, out_from_to_ph, REGULAR ); + mintests( "from / tuid / to w/o end", in_from_tuid_to_b1, out_from_tuid_to_ph, REGULAR ); + scc in_from_subj_to_b1[] = FROM SUBJECT TO; + mintests( "from / subject / to w/o end", in_from_subj_to_b1, out_from_subj_to, REGULAR ); + scc in_from_subj_tuid_to_b1[] = FROM SUBJECT IN_TUID TO; + mintests( "from / subject / tuid / to w/o end", in_from_subj_tuid_to_b1, out_from_subj_tuid_to, REGULAR ); + scc in_from_subj_to_tuid_b1[] = FROM SUBJECT TO IN_TUID; + scc out_from_subj_to_tuid_b1[] = FROM PH_SUBJECT TO OUT_TUID PH_BODY; + mintests( "from / subject / to / tuid w/o end", in_from_subj_to_tuid_b1, out_from_subj_to_tuid_b1, REGULAR ); + scc in_from_tuid_subj_to_b1[] = FROM IN_TUID SUBJECT TO; + mintests( "from / tuid / subject / to w/o end", in_from_tuid_subj_to_b1, out_from_tuid_subj_to, REGULAR ); + scc in_from_tuid_to_subj_b1[] = FROM IN_TUID TO SUBJECT; + scc out_from_tuid_to_subj_b1[] = FROM OUT_TUID TO PH_SUBJECT PH_BODY; + mintests( "from / tuid / to / subject w/o end", in_from_tuid_to_subj_b1, out_from_tuid_to_subj_b1, REGULAR ); + + + scc in_from_to_b2[] = FROM R_TO "\r"; + fulltests( "from / to w/o lf", in_from_to_b2, in_from_to_b2, AS_IS ); + scc out_from_to_b2[] = FROM TO OUT_TUID "\r"; + fulltests( "from / to w/o lf", in_from_to_b2, out_from_to_b2, ADD_TUID ); + scc in_from_tuid_to_b2[] = FROM IN_TUID R_TO "\r"; + scc out_from_tuid_to_b2[] = FROM OUT_TUID R_TO "\r"; + fulltests( "from / tuid / to w/o lf", in_from_tuid_to_b2, out_from_tuid_to_b2, ADD_TUID ); + scc in_from_to_tuid_b2[] = FROM TO R_IN_TUID "\r"; + fulltests( "from / to / tuid w/o lf", in_from_to_tuid_b2, out_from_to_tuid_b1, ADD_TUID ); + + mintests( "from / to w/o lf", in_from_to_b2, out_from_to_ph, REGULAR ); + mintests( "from / tuid / to w/o lf", in_from_tuid_to_b2, out_from_tuid_to_ph, REGULAR ); + scc in_from_subj_to_b2[] = FROM SUBJECT R_TO "\r"; + mintests( "from / subject / to w/o lf", in_from_subj_to_b2, out_from_subj_to, REGULAR ); + scc in_from_subj_tuid_to_b2[] = FROM SUBJECT IN_TUID R_TO "\r"; + mintests( "from / subject / tuid / to w/o lf", in_from_subj_tuid_to_b2, out_from_subj_tuid_to, REGULAR ); + scc in_from_subj_to_tuid_b2[] = FROM SUBJECT TO R_IN_TUID "\r"; + mintests( "from / subject / to / tuid w/o lf", in_from_subj_to_tuid_b2, out_from_subj_to_tuid_b1, REGULAR ); + scc in_from_tuid_subj_to_b2[] = FROM IN_TUID SUBJECT R_TO "\r"; + mintests( "from / tuid / subject / to w/o lf", in_from_tuid_subj_to_b2, out_from_tuid_subj_to, REGULAR ); + scc in_from_tuid_to_subj_b2[] = FROM IN_TUID TO R_SUBJECT "\r"; + mintests( "from / tuid / to / subject w/o lf", in_from_tuid_to_subj_b2, out_from_tuid_to_subj_b1, REGULAR ); + + + scc in_from_to_b3[] = FROM R_TO; + fulltests( "from / to w/o crlf", in_from_to_b3, in_from_to_b3, AS_IS ); + fulltests( "from / to w/o crlf", in_from_to_b3, out_from_to_b1, ADD_TUID ); + scc in_from_tuid_to_b3[] = FROM IN_TUID R_TO; + scc out_from_tuid_to_b3[] = FROM OUT_TUID R_TO; + fulltests( "from / tuid / to w/o crlf", in_from_tuid_to_b3, out_from_tuid_to_b3, ADD_TUID ); + scc in_from_to_tuid_b3[] = FROM TO R_IN_TUID; + fulltests( "from / to / tuid w/o crlf", in_from_to_tuid_b3, out_from_to_tuid_b1, ADD_TUID ); + + mintests( "from / to w/o crlf", in_from_to_b3, out_from_to_ph, REGULAR ); + mintests( "from / tuid / to w/o crlf", in_from_tuid_to_b3, out_from_tuid_to_ph, REGULAR ); + scc in_from_subj_to_b3[] = FROM SUBJECT R_TO; + mintests( "from / subject / to w/o crlf", in_from_subj_to_b3, out_from_subj_to, REGULAR ); + scc in_from_subj_tuid_to_b3[] = FROM SUBJECT IN_TUID R_TO; + mintests( "from / subject / tuid / to w/o crlf", in_from_subj_tuid_to_b3, out_from_subj_tuid_to, REGULAR ); + scc in_from_subj_to_tuid_b3[] = FROM SUBJECT TO R_IN_TUID; + mintests( "from / subject / to / tuid w/o crlf", in_from_subj_to_tuid_b3, out_from_subj_to_tuid_b1, REGULAR ); + scc in_from_tuid_subj_to_b3[] = FROM IN_TUID SUBJECT R_TO; + mintests( "from / tuid / subject / to w/o crlf", in_from_tuid_subj_to_b3, out_from_tuid_subj_to, REGULAR ); + scc in_from_tuid_to_subj_b3[] = FROM IN_TUID TO R_SUBJECT; + mintests( "from / tuid / to / subject w/o crlf", in_from_tuid_to_subj_b3, out_from_tuid_to_subj_b1, REGULAR ); + return 0; }