Modified UTF-7について

IMAPでのフォルダ名が見たこともないエンコードで返ってきたので調べてみました。
その結果このエンコード方式は、修正UTF-7というものらしいです。IMAP-UTF7とも表現するみたいです。

当然ながら、Javaなどの言語で標準的な解釈器が用意されているわけでもないので、自分でデコード処理を行わないと、本来の文字列を得ることはできません。

この修正UTF-7は、次のようなデータとなっています。
・ “&”以外の文字列はそのまま格納されている。
・ “&”を表現したい場合には、”&-“として格納される。
・ “&”と”-“で挟まれているデータは変則的なBase64としてエンコードされている。
・ この変則的なBase64は通常のBase64と比べ、”/” と “,” が置き換わっている点と、Base64のパディング”=”は含めない点が異なる。

さて、このような特徴があるので、”&”の出現でASCII処理モードとBASE64処理モードを切り替えて実装することで、割と楽なデコーダーを作ることができそうです。BASE64の処理モードでは、”,”を”/”に置き換えてからすでにあるBase64のデコーダーに処理させます。

public static String decodeModifiedUTF7( byte[] encodeBuf ) {
  if( encodeBuf == null ) {
    return null;
  }
  StringBuffer result = new StringBuffer();
  int count = encodeBuf.length;
  for( int i = 0; i < count; ++i ) {
    if( encodeBuf[i] != '&' ) {
      result.append( (char) encodeBuf[i] );
      continue;
    }
    ++i;
    if( encodeBuf[i] == '-' ) {
      // "&-"が出現したということで "&"を格納.
      result.append( "&" );
      continue;
    }

    // "&"から始まるデータブロック.
    // ここから "-"が出るまでは変則なBase64で格納されている.
    StringBuilder buf = new StringBuilder();
    boolean isBlockEnd = false;
    do {
      if( encodeBuf[i] == '-' ) {
        isBlockEnd = true;
      } else {
        buf.append( (char) encodeBuf[i++] );
      }
    } while( isBlockEnd == false );

    byte[] decodeBase64 = decodeModifiedUTF7asBase64( buf.toString() );
    try {
      result.append( new String( decodeBase64, "UTF-16") );
    } catch( UnsupportedEncodingException e ) {
    }
  }
  return result.toString();
}

private static byte[] decodeModifiedUTF7asBase64( String str ) {
  // 修正UTF-7では ","が使われてるので戻す.
  String buf = str.replace( ',', '/' );

  byte[] ret = null;
  try {
    int paddingCount = 4 - str.length() & 0x03;
    if( paddingCount == 4 ) {
      // 詰め物なしでデコードできる.
      ret = Base64.decode( buf.getBytes( "ASCII" ), Base64.DEFAULT );
    } else {
      StringBuilder sb = new StringBuilder(buf);
      for( int i = 0; i < paddingCount; ++i ) {
        sb.append( "=" );
      }
      ret = Base64.decode( sb.toString().getBytes( "ASCII" ), Base64.DEFAULT );
    }
  } catch( UnsupportedEncodingException e ) {
  }
  return ret;
}

感想

初めてUTF-7なんてものを知りました。いいきっかけだったと思います。
エイプリルフール時のネタで作られたRFCで、UTF-9なんてものがあったのは知っていたのですが…。
まだまだ知らないことで一杯ですが、このようなチャレンジを通して知識を広げていけたらいいなと思います。

Android プログラミング
すらりんをフォローする
すらりん日記

コメント

タイトルとURLをコピーしました