주제: 이곳에서 오는 메일의 표준 위반

이 게시판 원저자의 책임입니다만, 표준에 대한 마인드를 널리 퍼뜨릴 목적으로 만든 곳이니까 웹 표준 뿐 아니라 다른 인터넷 표준도 잘 지켜 주면 좋겠기에 글을 씁니다. (웹 표준 혹은 유니코드 표준 등에 대해 많은 지식이 있고, 보급에 힘쓰는 이가  인터넷 메일 표준 등은 어기는 경우를 가끔 봅니다. 일부러 그랬다기 보다는 몰라서 그런 것이겠지요)

여기서 발송하는 메일은 메시지 헤더에서 8bit octet를 그대로 쓰고 있습니다. 이것은 IETF RFC (2)822 위반입니다. RFC 2047에서 규정한 방법에 따라 B 혹은 Q  인코딩해야 합니다. (http://www.faqs.org/rfcs/rfc2047.html)

gmail이 원본 보기를 해도 Subject를 잘 안 보여주는군요. gmail이 아닌 일반 주소로 받았으면 왜 그런 것인지 분석하는 게 쉬울 텐데,  gmail로 받는 바람에 - 혹시 가능할 수도 있지만 - 귀찮아서 그냥 올립니다.


Subject: '�  � 과 � ��  � ��   �  �   � ��  � ...' � �   �  �   �  �
From: "CSS Design Korea beta 포럼" <freshworks@gmail.com>
Message-ID: <200508020618.95eed7669286@css.macple.com>
X-Priority: 3
X-Mailer: vBulletin Mail via PHP
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit

답변: 이곳에서 오는 메일의 표준 위반

    function BEncode($string, $charset = 'utf-8') {
    return "=?".$charset."?B?".base64_encode($string)."?=";
}

저도 예전에 이 문제 때문에 만들었던 함수가 있어서, 여기 올려봅니다. php구요.

mail($to, BEncode($subject), $msg, $headers);

뭐 요런식으로 쓰면 되지 않을까요? ㅎ

The problem with 'What You See Is What You Get' is that what you see is all you've got.Brian Kernighan.

답변: 이곳에서 오는 메일의 표준 위반

dahlia wrote:
    function BEncode($string, $charset = 'utf-8') {
    return "=?".$charset."?B?".base64_encode($string)."?=";
}

저도 예전에 이 문제 때문에 만들었던 함수가 있어서, 여기 올려봅니다. php구요.

저 코드만으로는 부족합니다 :-) RFC 2047은  encoded word의 길이가 72(?) octets을 넘지 못 하도록 규정하고 있습니다. 문자열 전체를 통째로 B encode하면 긴 문자열의 경우 가볍게 72 octets을 넘어갑니다.

http://pear.php.net/bugs/bug.php?id=30

http://livedocs.php.mirrors.powertrip.c … .mail.html
http://pear.php.net/manual/en/package.m … l-mime.php

등을 보세요.

답변: 이곳에서 오는 메일의 표준 위반

photon wrote:

저 코드만으로는 부족합니다 :-) RFC 2047은  encoded word의 길이가 72(?) octets을 넘지 못 하도록 규정하고 있습니다. 문자열 전체를 통째로 B encode하면 긴 문자열의 경우 가볍게 72 octets을 넘어갑니다.

http://pear.php.net/bugs/bug.php?id=30

http://livedocs.php.mirrors.powertrip.c … .mail.html
http://pear.php.net/manual/en/package.m … l-mime.php

등을 보세요.

function BEncode($string) {
    $b64_str = base64_encode($string);
    return "=?utf-8?B?".wordwrap( $b64_str, 60, "?=rn=?utf-8?B?", 1)."?=";
}

이렇게 하면 되겠네요 smile 테스트는 아래에서 해보심 됩니다...

http://mytears.org/resources/mysrc/php/base64/test.php

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

답변: 이곳에서 오는 메일의 표준 위반

dahlia wrote:
    function BEncode($string, $charset = 'utf-8') {
    return "=?".$charset."?B?".base64_encode($string)."?=";
}

저도 예전에 이 문제 때문에 만들었던 함수가 있어서, 여기 올려봅니다. php구요.

mail($to, BEncode($subject), $msg, $headers);

뭐 요런식으로 쓰면 되지 않을까요? ㅎ

이것만으론 안되겠네요... from: 누구누구 <메일주소> 이 부분에서도 인코딩될 필요가 있으니까요 smile 이름을 영어로만 쓴다면 상관없을지도 모르겠군요....

근데 제목은 utf-8 도 아닌 듯 합니다...

0000250: 6f72 670a 5375 626a 6563 743a 2043 5353  org.Subject: CSS
0000260: 2044 6573 6967 6e20 4b6f 7265 6120 6265   Design Korea be
0000270: 7461 ec20 2020 ec20 a4ec 20a0 20ea b220  ta.   . .. . ..
0000280: ec20 2020 ed20 20ec 2020 ed20 a9eb 2020  .   .  .  . ..
0000290: eb20 a421 0a46 726f 6d3a 2022 4353 5320  . .!.From: "CSS
00002a0: 4465 7369 676e 204b 6f72 6561 2062 6574  Design Korea bet
00002b0: 6120 ed8f aceb 9fbc 2220 3c66 7265 7368  a ......" <fresh

혹시나 추적해보실 분을 위해 xxd 로 메일파일 원본을 출력한 결과를 wink

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

답변: 이곳에서 오는 메일의 표준 위반

정태영 wrote:
function BEncode($string) {
    $b64_str = base64_encode($string);
    return "=?utf-8?B?".wordwrap( $b64_str, 60, "?=rn=?utf-8?B?", 1)."?=";
}

이렇게 하면 되겠네요 smile 테스트는 아래에서 해보심 됩니다...

http://mytears.org/resources/mysrc/php/base64/test.php

그래도 안 됩니다 :-) 무조건 잘라 버리면  UTF-8에서 2 바이트 이상으로 표현되는 글자가 두 개의 encoded word로 쪼개지는 일이 벌어질 수 있습니다. 좀 무식하기는 하지만, 바꿀 문자열을 UTF-32로 변환한 후에 한 글자씩 UTF-8로 변환하고 다시 Base64로 인코드해서 길이 제한을 넘어 가는지 체크하다가 길이 제한을 넘어 가는 순간에 자르는 방법을 쓰는 게 좋을 것입니다. 다음은 psuedocode입니다. 좀더 효율적으로 - encoded word를 새로 시작할 때 안전한 범위에서 최대한 많은 글자를 잡고 시작한다든지, base64 인코딩도 매번 새로 하지 말고, 그 전에 해 놓은 것에 더한다든지 (후자의 경우는 코드가 더 복잡해지는 문제가 있긴 합니다.)- 만들 수 있지만, 요새 컴퓨터에서 이렇게 '무식하게 천천히 한 자씩 더해도 속도에 큰 지장은 없을' 것입니다. 메일을 1초에 수천 통씩 보내는 것도 아니고요.

$l = length($uft32string);
$prefix="=?utf-8?B?";
$suffix = "?=";
$ohl = length($prefix) + length($suffix);    
$idx = 0;
$maxEncLen = 75 - $ohl;
$utf8str = "";
$i = 0; 
$encodedArray=[];
while ($idx < $l)
{
    $utf8Char = convUTF32toUTF8($utf32string[$idx]);
    $utf8str = $utf8str + $utf8Char; 
    $encoded = b64encode($utf8str);
    if (length($encoded) > $maxEncLen) {
       $encodedArray[$i++] = $prevEncoded;
       $utf8str = $utf8Char;
       $prevEncoded = b64encode($utf8str); 
    }
    else {
       $prevEncoded = $encoded; 
    }
    $idx++;
}

$encodedArray[$i] = $prevEncoded;


$rfc2047Str = $prefix + join ($encodedArray, $suffix + "rn " + $prefix) + $suffix; 

 


참, 위의 글을 쓰신 다음에 올리신 글에서 지적하신 대로 애초에 저 함수에 넘어가는 $string이 UTF-8이 아닌 것 같군요. (subject의 경우)

답변: 이곳에서 오는 메일의 표준 위반

photon wrote:

좀 무식하기는 하지만, 바꿀 문자열을 UTF-32로 변환한 후에 한 글자씩 UTF-8로 변환하고 다시 Base64로 인코드해서 길이 제한을 넘어 가는지 체크하다가 길이 제한을 넘어 가는 순간에 자르는 방법을 쓰는 게 좋을 것입니다.

어짜피 base64라던가 quoted print 로 인코딩을 하게 되면 어떤 인코딩을 사용하는지 정보가 남게되는데 구지 utf-32 로 변환했다가 utf-8 로 변환했다가 할 필요가 있나요?

$utf32_str = convUtf8toUtf32($str);
$b64_str = base64_encode( $utf32_str);

이전의 글에 남겼던 식으로 wordwrap 을 이용한 무식한 짜르기 
(단 utf32는 한 자에 4바이트씩이니까 글자가 짤리지 않도록 4의 배수로 짜르기)

식으로만 해도 되지 않을까 싶네요 smile


p.s) 글자 단위로 얻기 위해 utf-32 로 변환할 필요는 없어보이네요... utf-8 인코딩의 첫 바이트에 있는 값을 이용하면 바이트 단위가 아닌 글자 단위로 얻는 것도 어렵지는 않으니까요... utf-8 로 인코딩된 텍스트에서 글자 단위로 얻어오기 관련해선 예전에 짜놨던 게 있어서 링크를 겁니다

http://mytears.org/resources/mysrc/php/unicode/utf8.php


그나저나 예전에 웹메일을 짜면서 메일들을 분석하다 보면 저렇게 무식하게 짤라서 보낸 경우가 상당히 많았던 걸로 기억하는데... 그 메일을 보낸 프로그램이 문제였나보군요...



그리고 약간 오프토픽이지만... 은글꼴 관련해서
http://kldp.net/tracker/index.php?func= … tid=100300

혹시나 뭔가 새로운 정보를 얻으신게 있는지 궁금하네요...

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

답변: 이곳에서 오는 메일의 표준 위반

정태영 wrote:

어짜피 base64라던가 quoted print 로 인코딩을 하게 되면 어떤 인코딩을 사용하는지 정보가 남게되는데 구지 utf-32 로 변환했다가 utf-8 로 변환했다가 할 필요가 있나요?

$utf32_str = convUtf8toUtf32($str);
$b64_str = base64_encode( $utf32_str);

이전의 글에 남겼던 식으로 wordwrap 을 이용한 무식한 짜르기 
(단 utf32는 한 자에 4바이트씩이니까 글자가 짤리지 않도록 4의 배수로 짜르기)

식으로만 해도 되지 않을까 싶네요 smile

utf-32를 base64 encoding한다고요? 무슨 뜻인지 잘.....  메일에서는 utf-32나 utf-16 등 octet이 기본 코드 단위가 아닌 'character encoding'을 쓰면 이상한 일이 생길 가능성이 높습니다. (표준에서 명시적으로 금하고 있지 않다고 해도) 그런 문제가 없다고 해도, utf-8을 두고서,  utf-32를 base64로 인코딩하면 전송 효율성 면에서도 (대부분의 경우에) 문제가 있고요.

전체 문자열을 base64로 인코딩한 후에 wrap으로 자르면 안 되는 이유는 또 있습니다. base64는 3byte를 4byte로 바꿉니다.  (3 * 8bits/byte = 4 * 6bits/byte,  2^8=256, 2^6=64)  전체 문자열의 길이가  3의 배수가 아니면 끝에 패딩을 해 주어야 합니다. wrap으로 자르면 잘려서 생긴 각 부분에 그런 패딩이 제대로 들어가지 않겠지요.

utf32로 바꿀 필요는 물론 없습니다. 제 코드는 정말 그렇게 하라는 얘기가 아니라 그런 개념으로 하라는 얘기입니다. PHP에 iterator와 같은 개념이 있고, utf-8 string을 '글자' 단위로 iterate할 수 있다면(말씀하신 대로 utf-8은 글자 사이의 구별을 매우 쉽게 할 수 있으므로. 그게 utf-8이 다른 구식 멀티 바이트 인코딩에 비교했을 때 지닌 장점 중의 하나.) 그렇게 하면 됩니다. 앞선 글에서 적었듯이 한 글자씩 더하면서 하는 무식한 방법을 쓰지 않고, 초반에 대략 어느 정도에서 잘릴 것인지 계산해서 그 부근에서 앞으로 (혹은 뒤로) 가는 방식을 쓰는게 효율적입니다. 즉, base64로 인코딩한 결과의 최대 길이를 64로 제한하려면 48바이트에서 자른 후에 그게 utf-8의 경계가 아니면 그 이전에 나타나는 가장 가까운 utf-8 글자 경계를 찾으면 되겠지요.

답변: 이곳에서 오는 메일의 표준 위반

정태영 wrote:

그리고 약간 오프토픽이지만... 은글꼴 관련해서
http://kldp.net/tracker/index.php?func= … tid=100300

혹시나 뭔가 새로운 정보를 얻으신게 있는지 궁금하네요...

정보보다는 '시간'이 필요한 문제이지요 :-) 참, 모질라의 경우에 은 글꼴 문제를 해결할 수 있는 패치가 최근에 나오긴 했습니다. 그 패치를 적용할지 여부는 아직 결정되지 않았고요.

답변: 이곳에서 오는 메일의 표준 위반

photon wrote:

앞선 글에서 적었듯이 한 글자씩 더하면서 하는 무식한 방법을 쓰지 않고, 초반에 대략 어느 정도에서 잘릴 것인지 계산해서 그 부근에서 앞으로 (혹은 뒤로) 가는 방식을 쓰는게 효율적입니다. 즉, base64로 인코딩한 결과의 최대 길이를 64로 제한하려면 48바이트에서 자른 후에 그게 utf-8의 경계가 아니면 그 이전에 나타나는 가장 가까운 utf-8 글자 경계를 찾으면 되겠지요.

http://jshin.net/php/utf8bencode.php
에 위 아이디어를 구현해 놓았습니다.

답변: 이곳에서 오는 메일의 표준 위반

photon wrote:

http://jshin.net/php/utf8bencode.php
에 위 아이디어를 구현해 놓았습니다.

멋지군요 ^^;;

안그래도 요즘 웹메일과 비슷한 시스템을 구축중인데 메일 표준이 너무 어려워서
ㅠ_ㅠ);;

안그래도 저도 그냥 base64로 통짜 인코딩해서 UTF-8을 보냇는데

그러면 안되겠네요^^

좋은 정보와 소스 감사합니다 ^-^)

답변: 이곳에서 오는 메일의 표준 위반

photon wrote:

이 게시판 원저자의 책임입니다만, 표준에 대한 마인드를 널리 퍼뜨릴 목적으로 만든 곳이니까 웹 표준 뿐 아니라 다른 인터넷 표준도 잘 지켜 주면 좋겠기에 글을 씁니다. (웹 표준 혹은 유니코드 표준 등에 대해 많은 지식이 있고, 보급에 힘쓰는 이가  인터넷 메일 표준 등은 어기는 경우를 가끔 봅니다. 일부러 그랬다기 보다는 몰라서 그런 것이겠지요)

여기서 발송하는 메일은 메시지 헤더에서 8bit octet를 그대로 쓰고 있습니다. 이것은 IETF RFC (2)822 위반입니다. RFC 2047에서 규정한 방법에 따라 B 혹은 Q  인코딩해야 합니다. (http://www.faqs.org/rfcs/rfc2047.html)

gmail이 원본 보기를 해도 Subject를 잘 안 보여주는군요. gmail이 아닌 일반 주소로 받았으면 왜 그런 것인지 분석하는 게 쉬울 텐데,  gmail로 받는 바람에 - 혹시 가능할 수도 있지만 - 귀찮아서 그냥 올립니다.


Subject: '�  � 과 � ��  � ��   �  �   � ��  � ...' � �   �  �   �  �
From: "CSS Design Korea beta 포럼" <freshworks@gmail.com>
Message-ID: <200508020618.95eed7669286@css.macple.com>
X-Priority: 3
X-Mailer: vBulletin Mail via PHP
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit