/* mime64 */ /* MIME base64 encoder/decoder by Karl Hahn hahn@lds.loral.com 3-Aug-94 */ /* modified 30-Sep-94 by Karl Hahn hahn@lds.loral.com: handle multiple content */ /* modified 12-Jan-95 by Karl Hahn hahn@lds.loral.com: handle file names that are encased in quotes */ /* modified 18-Jan-95 by Karl Hahn hahn@lds.loral.com: prevent complete failure if filename in name field matches name of input file */ /* modified 19-Jan-95 by Karl Hahn hahn@lds.loral.com: prevent early exit if last decoded character falls on a multiple of 3 -- would cause error message and failure to rename output file if rename was necessary */ /* modified 19-Jan-95 by Karl Hahn hahn@lds.loral.com: prevent complete failure if a line of text preceding the MIME64 stuff contains no non-base64 characters */ /* modified 19-Jan-95 by Karl Hahn hahn@lds.loral.com: fixed command line parser to prevent missing a name field preceded by another name field */ /* modified 19-Jan-95 by Karl Hahn hahn@lds.loral.com: prevent error message at the end of decoding each section. Terminates output file now on a blank line as well as the conditions that did so previously */ #include #include #include #ifdef __FreeBSD__ #define strcmpi(x,y) strcasecmp(x,y) #endif char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789+/"; enum TOKENTYPE { NONE, BLANKS, PUNCT, TAG, NAME, CONTENT }; struct TOKEN { char *text; int length; int index; enum TOKENTYPE type; }; int compare_token( struct TOKEN *token, char *text ) { int index=0; int count; int result; char blivit1, blivit2; count = token->length; if ( count > 0 ) { result = 1; } else { result = 0; } while ( (count > 0) && ( result != 0 ) ) { blivit1 = token->text[index++]; if ( (blivit1 >= 'a' ) && (blivit1 <= 'z') ) { blivit1 -= ' '; } blivit2 = *text++; if ( (blivit2 >= 'a' ) && (blivit2 <= 'z') ) { blivit2 -= ' '; } if ( blivit1 != blivit2 ) { result = 0; } count--; } return result; } int ispunct( char blivit ) { if ( ( blivit >= 'a' ) && (blivit <= 'z' ) ) { blivit -= ' '; } if ( ( ( blivit < '0' ) || ( ( blivit > '9' ) && (blivit < 'A') ) || ( blivit > 'Z' ) ) && ( blivit != '-') && (blivit != '/') && (blivit != '.') ) { return 1; } else { return 0; } } void fixname( char *name ) { while ( *name != '\0' ) { if (ispunct( *name ) ) { *name = '\0'; } name++; } } void acquire_token( char *line, enum TOKENTYPE type, struct TOKEN *token ) { int doneflag=0, startflag=1; int index; enum TOKENTYPE nextstate=NONE; char blivit; if (token->type == NONE) { token->index = 0; token->length = 0; } index = token->index + token->length; token->text = 0; while ( doneflag == 0 ) { blivit = line[index]; if ( (blivit >= 'a') && (blivit <= 'z') ) { blivit -= ' '; } switch (token->type) { case NONE: if ( blivit == ' ') { index++; token->index++; } else { token->type = TAG; nextstate = TAG; } break; case BLANKS: if ( blivit == ' ') { index++; } else if ( ispunct( blivit ) ) { token->type = PUNCT; token->index = index; } else { token->type = nextstate; token->index = index; } break; case PUNCT: if ( blivit < ' ') { doneflag = 1; token->type = NONE; token->index = index; token->text = line + index; token->length = 0; } else if ( blivit == ' ' ) { token->type = BLANKS; token->index = index; if ( line[ token->index ] == ';' ) { nextstate = NAME; } else if ( line[ token->index ] == '=' ) { nextstate = CONTENT; } } else if ( ispunct( blivit ) ) { index++; } else { if ( line[ token->index ] == ';' ) { nextstate = NAME; } else if ( line[ token->index ] == '=' ) { nextstate = CONTENT; } token->type = nextstate; token->index = index; } break; case TAG: if ( ispunct( blivit ) ) { token->length = index - token->index; token->text = line + token->index; nextstate = NAME; if ( ( ( type == TAG ) || ( type == NONE ) ) && !startflag) { doneflag = 1; } else if (blivit == ' ') { token->type = BLANKS; token->index = index; } else { token->type = PUNCT; token->index = index; } } else { index++; } break; case NAME: if ( ispunct( blivit ) ) { token->length = index - token->index; token->text = line + token->index; if ( blivit != ';' ) { nextstate = CONTENT; } else { nextstate = NAME; } if ( ( ( type == NAME ) || ( type == NONE ) ) && !startflag ) { doneflag = 1; } else if (blivit == ' ') { token->type = BLANKS; token->index = index; } else { token->type = PUNCT; token->index = index; } } else { index++; } break; case CONTENT: if ( ispunct( blivit ) ) { token->length = index - token->index; token->text = line + token->index; nextstate = NAME; if ( ( ( type == CONTENT ) || ( type == NONE ) ) && !startflag ) { doneflag = 1; } else if (blivit == ' ') { token->type = BLANKS; token->index = index; } else { token->type = PUNCT; token->index = index; } } else { index++; } break; } startflag = 0; } } void fputch( char blivit, FILE *f ) { /* if (blivit == '\n') fputc( '\r', f );*/ fputc( blivit, f ); } int classify_args( int narg, char *rawargs[], char *fileargs[], char *optargs[] ) { int index, jndex, kndex; char *argptr; for ( index = 0, jndex = 0, kndex = 0; index < narg; index++ ) { argptr = rawargs[index]; if (*argptr == '-') { argptr++; optargs[kndex++] = argptr; } else { fileargs[jndex++] = argptr; } } return kndex; } int cvt_ascii( unsigned char alpha ) { if ( (alpha >= 'A') && (alpha <= 'Z') ) return (int)(alpha - 'A'); else if ( (alpha >= 'a') && (alpha <= 'z') ) return 26 + (int)(alpha - 'a'); else if ( (alpha >= '0') && (alpha <= '9' ) ) return 52 + (int)(alpha - '0'); else if ( alpha == '+' ) return 62; else if ( alpha == '/' ) return 63; else if ( alpha == '=' ) return -2; else return -1; } char *fileargs[64], *optargs[64]; struct STATE64 { unsigned long int accum; int shift; }; int main( int nargs, char *cargs[] ) { int n_options, n_files, index, jndex, shift, save_shift; enum { ENCODE, DECODE } whattodo = DECODE; int help_flag = 0, replace_flag = 0, perm_replace_flag = 0, quit = 0; int cycle_flag = 0; FILE *fin, *fout = NULL, *dummy; unsigned char blivit; unsigned long accum, value; char buf[80], dumname[80]; char *cptr, *altptr; int decode_state; struct TOKEN token; int firsttime = 1; int skipflag = 0; int printmsg = 1; int outcount = 0; n_options = classify_args( nargs, cargs, fileargs, optargs ); n_files = nargs - n_options; if ( n_files < 2 ) help_flag = 1; for ( index = 0; index < n_options; index++ ) { if ( ( optargs[index][0] == 'e' ) || ( optargs[index][0] == 'E' ) ) whattodo = ENCODE; if ( optargs[index][0] == '?' ) help_flag = 1; } if ( help_flag ) { printf( "mime64 infile [outfile] [-option] [-option] etc.\n\n" "convert between binary and MIME BASE64 format\n\n" " -e MIME base64 encode (default is decode)\n" " -? display help message\n\n" "if no outfile given, output file replaces infile\n" ); } if ( n_files < 2 ) exit(0); if ( whattodo == DECODE ) { fin = fopen( fileargs[1], "r" ); } else { fin = fopen( fileargs[1], "rb" ); } if ( fin == 0 ) { printf( "%s file not found\n", fileargs[1] ); exit(-1); } if ( n_files > 2 ) { if ( whattodo == DECODE ) { sprintf( dumname, "%s", fileargs[2] ); } else { fout = fopen( fileargs[2], "w" ); if ( fout == 0 ) { printf( "Couldn't open %s for output\n", fileargs[2] ); } } } else { if ( whattodo == DECODE ) { sprintf( dumname, "%s", fileargs[1] ); } else { fout = fopen( "$$$$$$$$.$$$", "w" ); } replace_flag = 1; } do { quit = 0; printmsg = 1; if ( whattodo == DECODE ) { shift = 0; accum = 0; decode_state = 0; while ( ( !feof( fin ) ) && (quit == 0) ) { fgets( buf, 80, fin ); if ( feof( fin ) ) { if ( ( dumname[0] != '\0' ) && ( shift != 0 ) ) { printf( "Unexpected end of file encountered in %s\n" "last few bytes may have been lost\n", dumname ); quit = 1; decode_state = 1; continue; } else if ( cycle_flag == 0 ) { quit = 1; decode_state = 1; continue; } } else { cycle_flag = 1; if ( (decode_state == 1) && ( (buf[0] == '\n') || (buf[0] < '+') ) ) { quit = 1; if ( shift != 0 ) { printf( "Unexpected end of section in %s\n" "last few bytes may have been lost\n", dumname ); } continue; } } if ( decode_state == 0 ) { for ( index = 0; (buf[index] != '\n') && (buf[index] != '\0') && (decode_state >= 0); index++ ) { if ( ( (buf[index] >= 'A') && (buf[index] <= 'Z') ) || ( (buf[index] >= 'a') && (buf[index] <= 'z') ) || ( (buf[index] >= '0') && (buf[index] <= '9') ) || (buf[index] == '+') || (buf[index] == '/') || (buf[index] == '=') ) { decode_state = 1; } else { decode_state = -2; } } if ( decode_state <= 0 ) { decode_state = 0; token.type = NONE; acquire_token( buf, TAG, &token ); if ( compare_token( &token, "Content-Type") ) { do { acquire_token( buf, NAME, &token ); if ( compare_token( &token, "name" ) ) { acquire_token( buf, CONTENT, &token ); if ( ( replace_flag ) || ( firsttime == 0 ) ) { sscanf( token.text, "%s", dumname ); fixname( dumname ); if ( strcmpi( dumname, fileargs[1] ) != 0 ) { replace_flag = 0; } else { if ( perm_replace_flag ) { printf( "More than one output file named %s\n", dumname ); exit(-1); } } } } } while ( token.type != NONE ); } else if ( compare_token( &token, "Content-transfer-encoding" ) ) { skipflag = 1; do { acquire_token( buf, NAME, &token ); if ( compare_token( &token, "base64" ) ) { skipflag = 0; } } while ( token.type != NONE ); } continue; } else if ( skipflag != 0 ) { continue; } } if ( printmsg ) { if ( skipflag ) { printf( "Section %s not MIME base64\n", dumname ); } else { printf( "Creating %s\n", dumname ); if ( strcmpi( dumname, fileargs[1] ) == 0 ) { replace_flag = 1; } if ( replace_flag ) { fout = fopen( "$$$$$$$$.$$$", "wb" ); } else { fout = fopen( dumname, "wb" ); } if ( fout == 0 ) { printf( "Couldn't open %s for output\n", dumname ); } } printmsg = 0; } if ( fout == 0 ) { printf( "No filename given for subsequent section\n" ); exit(-1); } if ( feof(fin) ) { quit = 1; } if ( quit != 0 ) { buf[0] = '\0'; } for ( index = 0; (buf[index] != '\n') && (buf[index] != '\0'); index++) { value = cvt_ascii( buf[index] ); if ( value < 64 ) { accum <<= 6; shift += 6; accum |= value; if ( shift >= 8 ) { shift -= 8; value = accum >> shift; blivit = (unsigned char)value & 0xFFl; fputc( blivit, fout ); } } else { quit = 1; break; } } } } else { fprintf ( fout, "Content-Type: text/plain; charset=US-ASCII; name=%s\n" "Content-transfer-encoding: base64\n\n", fileargs[1] ); shift = 0; accum = 0; index = 0; while ( ( !feof( fin ) ) || (shift != 0) ) { if ( ( !feof( fin ) ) && ( quit == 0 ) ) { blivit = fgetc( fin ); if ( feof( fin ) ) { quit = 1; save_shift = shift; blivit = 0; } } else { quit = 1; save_shift = shift; blivit = 0; } if ( (quit == 0) || (shift != 0) ) { value = (unsigned long)blivit; accum <<= 8; shift += 8; accum |= value; } /* ENDIF */ while ( shift >= 6 ) { shift -= 6; value = (accum >> shift) & 0x3Fl; blivit = alphabet[value]; buf[index++] = blivit; if ( index >= 60 ) { buf[index] = '\0'; fprintf( fout, "%s\n", buf ); index = 0; } if ( quit != 0 ) { shift = 0; } } } if ( save_shift == 2 ) { buf[index++] = '='; if ( index >= 60 ) { buf[index] = '\0'; fprintf( fout, "%s\n", buf ); index = 0; } buf[index++] = '='; if ( index >= 60 ) { buf[index] = '\0'; fprintf( fout, "%s\n", buf ); index = 0; } } else if ( save_shift == 4 ) { buf[index++] = '='; if ( index >= 60 ) { buf[index] = '\0'; fprintf( fout, "%s\n", buf ); index = 0; } } if ( index != 0 ) { buf[index] = '\0'; fprintf( fout, "%s\n", buf ); } } if ( fout ) { ++outcount; fclose( fout ); } if ( replace_flag ) { perm_replace_flag = 1; if ( ( whattodo == DECODE ) && ( decode_state <= 0 ) && ( outcount == 0 ) ) { remove( "$$$$$$$$.$$$" ); printf( "No MIME base64 lines found in %s\n", fileargs[1] ); } } else { if ( ( whattodo == DECODE ) && ( decode_state <= 0 ) && ( outcount == 0 ) ) { remove( fileargs[2] ); printf( "No MIME base64 lines found in %s\n", fileargs[1] ); } } fout = 0; firsttime = 0; dumname[0] = '\0'; cycle_flag = 0; } while ( !feof( fin ) ); if ( perm_replace_flag ) { remove( fileargs[1] ); rename( "$$$$$$$$.$$$", fileargs[1] ); } fclose( fin ); }