2010年11月15日星期一

Quoted-printable邮件编码及编码实现

    Quoted-printable是众多邮件编码协议中的一种,与BASE64作用相同。为了表示传统的NVT ASCII不能表示的字符(128-254)。对于大多可读的ASCII不进行编码,所以QP编码有很多的部分是可读的。
    编码规则如下:

  1. ASCII中,[33,126]之间除了'=',在QP中都可以直接显示无需编码。0-32 + '=' + 127-254这些字符是需要进行QP编码的。
  2. 编码方法:如空格在ASCII中用0x20十六进制表示,QP编码将其十六进制用字符表示,在其前加'='号,为=20。QP编码中,'='符号后必须为大写。
  3. 普通文本中有'\r\n'换行符号,在QP中,编码为=0D=0A,但不换行。
  4. QP编码每行的长度限制为最多76个字符,到达后需要添加QP中的软换行(=\r\n),直接将软换行加入QP编码中,其中'='算在76字符内,而'\r\n'不算。
  5. QP编码的所有行不能以空格或TAB结尾,必须为软换行(=\r\n)或可打印的字符。
  6. 代码中最好加入异常处理:QP编码不为大写,结尾为空格等。



更详细规则参考RFC 2045

代码实现:
#define MAX_LINE_LEN 76  /* RFC规定QP编码单行最大长度 */

/* src_len 源字符串长度
 * dst_len dst缓冲区大小
 */
static int
encode_quoted(const char *src, int src_len, char *dst, int dst_len)
{
 int i = 0;
 int cur_line_len = 0; /* 记录单行长度 */
 int dst_alen = 0; /* 向dst输出总长度 */

 /* 保证dst不被写越界 */
 for (i = 0; i < src_len && dst_alen < dst_len - 3; i++, src++)
 {
  /* ASCII 33-60,62-126原样输出,其余需编码 */
  if ((*src >= '!') && (*src <= '~') && (*src != '='))
  {
   *dst++ = *src;
   dst_alen++;
   cur_line_len++;
  }
  else
  {
   sprintf(dst, "=%02X", *src);
   dst += 3;
   dst_alen += 3;
   cur_line_len += 3;
  }

  /* 单行长度限制
   * 结尾=\r\n中\r\n不计入
   */
  if (cur_line_len >= MAX_LINE_LEN-3)
  {
   sprintf(dst, "=\r\n");
   dst += 3;
   dst_alen += 3;
   cur_line_len = 0;
  }
 }

 *dst = '\0';

 return dst_alen;
}

static int
decode_quoted(const char *src, int src_len, char *dst, int dst_len)
{
 int j = 0;
 int dst_alen = 0;

 for (dst_alen = 0, j = 0; dst_alen < dst_len && j < src_len; dst_alen++, dst++)
 {
  if (strncmp(src, "=\r\n", 3) == 0)
  {
   src += 3;
   j += 3;
  }
  else
  {
   if (*src == '=')
   {
    sscanf(src, "=%02hhX", dst);
    src += 3;
    j += 3;
   }
   else
   {
    *dst = (char)*src++;
    j++;
   }
  }
 }

 *dst = '\0';

 return dst_alen;
}

没有评论:

发表评论