#pragma module DEMIME "DEMIME-1-X" #define __MODULE__ "DEMIME" /* **++ ** FACILITY: A simple MIME to ASCII decoder ** ** MODULE DESCRIPTION: ** ** ** AUTHORS: ** ** Ruslan R. Laishev ** ** CREATION DATE: 28-JUL-2003 ** ** DESIGN ISSUES: ** ** ** ** MODIFICATION HISTORY: ** ** {@tbs@}... ** 29-SEP-2009 RRL Fixed: incorrect output length computing in the convdc(), ** overlapping buffers; **-- */ /* ** ** INCLUDE FILES ** */ #include #include #include #include #include #include #include #include #include #include #include #include /* ** ** LIB$TPARSE stuff ** */ extern char ufd_state, ufd_key; /* ** An array to store of the found fields */ struct tp_block { struct tpadef tpb; unsigned char cs_encode, *buf; unsigned short bufsz, buflen; }; #define BYTE unsigned char /* size is 1 */ #define DWORD unsigned long /* size is 4 */ #define CS$UNDEF 0 #define CS$ISOCYR 0x01 #define CS$KOI8R 0x02 #define CS$CP1251 0x03 #define CS$UTF8 0x04 #define ENCODE$UNDEF 0 #define ENCODE$QP 0x10 #define ENCODE$B64 0x20 #define DEBUG if(debug_flag)printf("%-24.24s:%-08.0u ",__MODULE__,__LINE__);if(debug_flag)printf extern int debug_flag; /* ** RUSSIAN CHARACTER SET TABLES */ $DESCRIPTOR(isocyr, "°±²³´µ®¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕ÷ÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîï"); $DESCRIPTOR(koicyr, "áâ÷çäå³öúéêëìíîïðòóôõæèãþûýøùÿüàñÁÂ×ÇÄÅ£ÖÚÉÊËÌÍÎÏÐÒÓÔÕÆÈÃÞÛÝØÙßÜÀÑ"); $DESCRIPTOR(wincyr, "ÀÁÂÃÄŨÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãä叿çèéêëìíîïðñòóôõö÷øùúûüýþÿ"); struct dsc$descriptor_s *tblcyr[] = { NULL, &isocyr, &koicyr, &wincyr}; #define INIT_SDESC(dsc, len, ptr) {(dsc).dsc$b_dtype = DSC$K_DTYPE_T;\ (dsc).dsc$b_class = DSC$K_CLASS_S; (dsc).dsc$w_length = (short) (len);\ (dsc).dsc$a_pointer = (ptr);} int convdc ( int srcdc, unsigned char *src, unsigned short *srclen ) { struct dsc$descriptor src_dsc; static iconv_t cd; static int iconv_flag = 0; char buf [ 255 ] , *inpbuf = src, *outbuf = &buf; int status,inpleft = *srclen,outbytesleft = sizeof(buf); if ( !iconv_flag ) { if ( !(cd = iconv_open("ISO8859-5","UTF-8")) ) perror("iconv_open"); else iconv_flag = 1; } if ( (srcdc & 0x0f) == CS$ISOCYR ) return SS$_NORMAL; else if ( (srcdc & 0x0f) == CS$UTF8 ) { DEBUG("toIConvert[0:%u]='%.*s'\n",*srclen,*srclen,src); if ( 0 > (status = iconv (cd,&inpbuf,&inpleft,&outbuf,&outbytesleft)) ) perror("iconv"); *srclen = sizeof(buf) - outbytesleft; memcpy(src,buf,*srclen); DEBUG("IConvertEd[0:%u]='%.*s'\n",*srclen,*srclen,src); return SS$_NORMAL; } INIT_SDESC(src_dsc,*srclen,src); return str$translate(&src_dsc,&src_dsc,tblcyr[CS$ISOCYR],tblcyr[srcdc]); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** An action routine called from LIB$TPARSE routine, if a field token is matched ** store a length and a pointer to the found field. ** ** FORMAL PARAMETERS: ** ** tblock: A pointer to the TPARSE block ** ** RETURN VALUE: ** ** VMS condition code ** ** SIDE EFFECTS: ** ** Modify a string pointer and length in the TPARSE block. ** ** **-- */ int demime__set_cs_encode( struct tp_block *tblock ) { /* ** Store information about current character set */ tblock->cs_encode |= tblock->tpb.tpa$l_param; /* ** Always return a success status */ return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** An action routine called from LIB$TPARSE routine, if a field token is matched ** store a length and a pointer to the found field. ** ** FORMAL PARAMETERS: ** ** tblock: A pointer to the TPARSE block ** ** RETURN VALUE: ** ** VMS condition code ** ** SIDE EFFECTS: ** ** Modify a string pointer and length in the TPARSE block. ** ** **-- */ int demime__savechar( struct tp_block *tblock ) { if ( tblock->buflen >= tblock->bufsz ) return (SS$_BUFFEROVF & (~1)); /* ** Store information about current character set */ tblock->buf[tblock->buflen++] = tblock->tpb.tpa$l_char; return SS$_NORMAL; } /*----------------------------------------------------------------------------- | demimeode -- single line Quoted-Printable decoder | DataIn : inLine = input line text | outLine = output line data | pcbOut = output byte count | Remark : | Example: '----------------------------------------------------------------------------*/ unsigned short QPDecode ( unsigned char *inLine, unsigned short inSz, unsigned char *outLine, unsigned short outSz, unsigned short *outLen ) { int i; unsigned char ab2Hex[3] = {0,0,0}, *pb, bHex; for( *outLen = 0,i = 0; (i < inSz) && (*outLen < outSz); i++ ) { /* ** Check for termination sequence ("?=") in the string */ if ( (inLine[i] == '?') && (i < inSz) && (inLine[i+1] == '=') ) { /* ** Return a number processed characters */ return i+2; } if ( inLine[i] == '=' ) { if( ( inLine[i + 1] == 13 && inLine[i + 2] == 10 ) || inLine[i + 1] == 10 ) break; ab2Hex[0] = inLine[i + 1]; ab2Hex[1] = inLine[i + 2]; bHex = (BYTE) strtoul( ab2Hex, &pb, 16 ); if( bHex == 0 && ( inLine[i + 1] != '0' || inLine[i + 2] != '0' ) ) continue; else { outLine[(*outLen)++] = bHex; i += 2; } } else if ( inLine[i] == 13 && inLine[i + 1] == 10 ) { outLine[(*outLen)++] = 10; break; } else outLine[(*outLen)++] = inLine[i]; } /* ** Return a number processed characters */ return i; } unsigned char szBase64Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /*----------------------------------------------------------------------------- | B64Decode -- single line Base64 decoder | DataIn : inLine = input line text | outLine = output line data | pcbOut = output byte count | init = | Remark : | Example: '----------------------------------------------------------------------------*/ unsigned short B64Decode( unsigned char *inLine, unsigned short inSz, unsigned char *outLine, unsigned short outSz, unsigned short *outLen ) { static union dw2b { unsigned dw; unsigned char b[4]; } u = {0,{0,0,0,0}}; int icount = 0,i = 0, iVal = 0 , iDiscount = 0; unsigned char *pvoid; for( *outLen = 0, i = 0; (i < inSz) && (*outLen < outSz); i++ ) { /* ** Check for termination sequence ("?=") in the string */ if ( (inLine[i] == '?') && (i < inSz) && (inLine[i+1] == '=') ) { /* ** Return a number processed characters */ return i+2; } if ( !(pvoid = memchr( szBase64Table, inLine[i], 64 )) ) { if ( inLine[i] == '=' ) { iDiscount++; iVal = 0; } else continue; } else iVal = pvoid - szBase64Table; u.dw = ( u.dw << 6 ) | ( iVal & 0x003F ); icount++; if ( icount >=4 ) { outLine[(*outLen)++] = u.b[2]; outLine[(*outLen)++] = u.b[1]; outLine[(*outLen)++] = u.b[0]; icount = 0; if ( iDiscount ) { *outLen -= iDiscount; break; } } } return i; } /* **++ ** FUNCTIONAL DESCRIPTION: ** Performs an decoding a givent string from QP/B64 format, return a result ** string length, and character set code. ** ** FORMAL PARAMETERS: ** ** src: A source string pointer ** srclen: A length of the source string ** dst: A buffer to placing decoded string ** dstsz: A size of the buffer for decoded string ** retlen: An actual length of the decoded string ** retcs: A character set of the decoded string ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ int demime ( unsigned char *src, unsigned short srclen, unsigned char *dst, unsigned short dstsz, unsigned short *retlen, unsigned int *retcs ) { int status,i = 0; unsigned short totlen = 0,len = 0; unsigned char *outptr = dst; struct tp_block tblock = {{TPA$K_COUNT0,TPA$M_BLANKS},CS$UNDEF | ENCODE$UNDEF,0,0,0}; /* ** Prepare a TPARSE block */ tblock.tpb.tpa$l_stringcnt = srclen; tblock.tpb.tpa$l_stringptr = src; tblock.cs_encode = 0; for (*retlen = 0, *retcs = 0; (i < 512) && (tblock.tpb.tpa$l_stringcnt) && (*retlen <= dstsz);i++) { /* ** Call parser, and return status */ tblock.buf = dst; tblock.bufsz = dstsz; tblock.buflen = 0; DEBUG("<%.*s>\n",tblock.tpb.tpa$l_stringcnt,tblock.tpb.tpa$l_stringptr); status = lib$table_parse(&tblock,&ufd_state,&ufd_key); // DEBUG("<%.*s>\n",tblock.tpb.tpa$l_stringcnt,tblock.tpb.tpa$l_stringptr); // DEBUG("<%.*s>\n",tblock.buflen,tblock.buf); DEBUG("tblock.cs_encode = %0x\n",tblock.cs_encode); totlen += tblock.buflen; dstsz -= tblock.buflen; dst += tblock.buflen; if ( tblock.cs_encode & ENCODE$QP ) { len = QPDecode(tblock.tpb.tpa$l_stringptr,tblock.tpb.tpa$l_stringcnt, dst,dstsz,retlen); totlen += *retlen; dstsz -= *retlen; dst += *retlen; tblock.tpb.tpa$l_stringcnt -= len; tblock.tpb.tpa$l_stringptr += len; } else if ( tblock.cs_encode & ENCODE$B64 ) { len = B64Decode(tblock.tpb.tpa$l_stringptr,tblock.tpb.tpa$l_stringcnt, dst,dstsz,retlen); totlen += *retlen; dstsz -= *retlen; dst += *retlen; tblock.tpb.tpa$l_stringcnt -= len; tblock.tpb.tpa$l_stringptr += len; } } *retcs = tblock.cs_encode & 0x0f; *retlen = totlen; return status; } #if 0 /* char qpsrc [] = "Subject: Re: =?KOI8-R?Q?=F7=CF=D0=D2=CF=D3_=CE=C1_=D3=C1=CA=D4=C5_=F3?=\ =?KOI8-R?Q?=EB=E1=EA=EC=E9=EE=EB?="; */ unsigned char src [] = "=?koi8-r?B?68/O09TBztTJziDiwcLB0tnLyc4=?="; void main (void) { int status; unsigned short retlen = 0; unsigned char dst [ 255 ]; printf("%u,<%.*s>\n",sizeof(src)-1,sizeof(src)-1,src); if ( !(1 & (status = demime(src,sizeof(src)-1,dst,sizeof(dst),&retlen))) ) lib$signal(status); printf("<%.*s>\n",retlen,dst); } #endif