티스토리 뷰

Network/PHP

[PHPSchool] 일본어 송신 관련

알 수 없는 사용자 2006. 6. 3. 04:14
5년전에 잠시 일본에서 무역회사를 하던 때의 메일 주소를 아직도 쓰고
있는데 지금도 자주 한국에서 광고 메일이 들어오고 일본어 메일을
받아 보면 깨어지는 메일이 많이 있습니다. 특히 웹메일을 사용하여
다량으로 메일을 보내는 업체들에서 이러한 경우가 많이 발생합니다.
이런 깨진 메일 보내면 광고 효과는 커녕 역효과만 내리라고 생각합니다.
그리고 몇주 전에는 일본의 다이얼패드에서 보내온 일본어 메일을
받았는데 이 메일 역시 깨져 있었습니다. 어느 정도 이름이 있는 회사가
보낸 메일이 깨어 진다면 회사의 신용 문제와도 연결되는 것이라고
생각해 여기에 오시는 PHPER분들이 제작하는 일본어 메일 프로그램만큼은
절대로 깨어지지 않도록 하는 팁과 소스를 올려보고자 합니다.

의외로 한국의 큰 소프트 개발회사에서 개발한 일본어 소프트에서도
위와같은 사례를 많이 보아왔습니다. 아래의 내용은 PHP뿐만아니라 C,
PERL, JAVA로 개발을 하더라도 해당이 되는 부분입니다.

<철칙>
일본어 메일은 반드시 JIS코드로 송신해야합니다.

메일 해더의 캐릭터셋은 ISO-2022-JP로 합니다.

일본어가 들어간 송신자이름, 수신자이름, 제목, 파일명은 반드시
ISO-2022-JP코드로 64B인코딩해야합니다.
(예,Subject: =?ISO-2022-JP?B?GyRCI1cjRSNCRVBPPyROJCpDTiRpJDsbKEI=?=)

RFC규정에 Quoted Printable 인코딩 방법이 있는데 이 인코딩 방법은
일본어 메일 헤더내에 절대로 사용하지 않아야 합니다. 의외로 이 인코딩
방법을 쓰는 메일들을 많이 봅니다. 많은 클라이언트 메일러에서
이 인코딩 방법을 지원하지 않습니다. 그리고 I-MODE등의 휴대전화로
메일을 보냈을때 여지 없이 깨어집니다. 많은 비지네스맨들이
메일을 휴대전화로 전송하여 확인하고 있다는 사실을 염두해야 합니다....

Shift_JIS코드로 아무 처리없이 그냥 메일을 보내지 말아야 합니다.
일부 메일러에서는 처리해주는 경우가 있으나 아직도 미비합니다.
이렇게 보내지는 메일도 많이 보아 왔습니다.휴대전화에서는 깨짐.

가급적이면 Content-type: text/html; 을 사용하지 않는다. 대부분의
PC에서는 지원이 되나 휴대전화에서 지원하지 않아 많은 사람들이
짜증을 냅니다. 이 콘텐트 타이프를 사용하고자 할때는
Content-Type: multipart/alternative; 를 사용합니다.
이 때에는 처음 텍스트 파트는
------=_NextPart
Content-Type: text/plain;
charset="iso-2022-jp"
Content-Transfer-Encoding: 7bit
와 같은 방법으로 그냥 JIS코드로 본문을 작성합니다.
JIS코드는 ISO에 등록된 7bit코드라서 위와같이 지정합니다.
다음 HTML파트는
------=_NextPart
Content-Type: text/html;
charset="iso-2022-jp"
Content-Transfer-Encoding: quoted-printable
와 같은 방법으로 여기에서는 quoted-printable를 사용합니다.
일반적으로 64인코딩을 하는 경우도 있는데 일부 메일러와
휴대전화에서 지원하지 않습니다. 위와 같이 처리하면
휴대전화에서도 HTML로 송신된 메일을 받아볼 수 있습니다.

무작정 전부다 64인코딩이나 Q인코딩하지 말아야 합니다.
일부 텍스트 메일과 위의 multipart/alternative타입의 경우
메일 본문을 모두 64인코딩이나 Q인코딩하는 사례 역시 많이
발견합니다만, 휴대전화에서는 지원하지 않고 일본어 메일러에서
지원하지 않는 경우가 있습니다.

Content-Type: multipart/mixed; 의 경우도 위와 같은 방법으로
헤더와 메일 본문을 작성합니다.(아래 소스 참조)

(다 알고 있는 부분이겠지만...)
korea.internet.com의 경우 Content-Type: multipart/alternative; 를
사용하여 메일을 송신하고 있는데 위의 텍스트 부분은 64인코딩을하고
HTML부분은 Q인코딩하여 송신을 합니다. 이것은 한글의 완성형
euc-kr이나 ks_c_5601-1987은 8bit 코드 라서 텍스트 부분을 RFC규정에
준하여 64인코딩을 해주는 겁니다. 물론 이렇게 안해줘도 한국에서는
메일이 잘 전달이 됩니다. 요즈음은 거의 안 쓰이지만 이전의 7bit
ISO 한글코드를 사용하면 64인코딩을 하지 않아도 된다는 이야기 입니다.
이렇게 인코딩을 하는 것은 일부 메일 서버에서 아직도 8bit메일을 통과
시키지 못하는 경우가 가끔 있기 때문입니다.

철칙 끝

위의 철칙만 지키면 절대로 일본어 메일은 깨어지지 않습니다.

그 다음은 현재 PHP소스중 일본어 문자 코드를 변환하는 소스가
일본에도 거의 없습니다. 일반적으로 일본의 HTML에서 Shift_JIS와 EUC가
많이 사용이 되는데 웹에서 메일을 보내려면 Shift_JIS로 입력받은
데이터와 EUC로 입력 받은 데이터를 JIS로 변환하지 않으면 안됩니다.
일본의 경우 일본의 PHP유저 그룹에서 일본어 코드를 변환하는 함수를
C소스로 추가해서 국제판 PHP라는 이름으로 배포를 하여 일본에서는
이 버전을 많이 사용하고 있습니다. --- 나쁜놈들.. 무슨 국제판이야....
그러나 요즈음은 버전업이 자주되고 유저그룹에서 지원해 주지 못해
php.net에서 다운 받은 오리지날 버전 사용자가 늘어나고 있습니다...Good
그래서 이 오리지날 버전 PHP에서도 사용할 수 있도록 일본어 코드를
변환하는 소스를 이전 PERL로 작성한 것이 있어 PHP로 고쳐서 썼는데
속도가 따라주지 않아 일본의 유닉스 시스템의 NKF라는 한자 코드 변환
프로그램 C소스를 참고하여 PHP소스로 수정하였습니다. 아래의 소스를
참고하시기 바랍니다. 그리고 일부 고수분들이 pack함수를 쓰지 chr함수를
사용하였느냐는 질문이 있을것 같아서 설명을 드립니다.

chr함수가 실행속도 면에서 휠씬 빠릅니다.그리고 IEEE부동소수점을
채택하고 있는 메모리(IEEE사양의 메모리에서는 바이트 오더까지
규정하고 있지 않기 때문에)에서 메모리 상에서 계산을 할때 수치를
double에서 float로 변환하고 다시 double로 재 변환 하기 때문에
정밀도가 떨어져 어떤 기종의 컴퓨터에서 pack한 데이터를 다른
기종에서는 unpack할 수 없는 가능성이 있기 때문입니다.

아래는 Sendmail을 사용하여 일본어 메일과 첨부 파일을 송신하는
소스 샘플입니다. SMTP나 mail함수를 사용하여 보낼때는 아래 내용을
참고하여 적절히 헤더와 본문의 데이터를 수정하시면 됩니다.



function jpmail ($from_mail, $from_name, $to, $subject, $body, $attach, $filename)
{

$boundary = "____nogada" . uniqid("b");
$from_name = '=?ISO-2022-JP?B?'.base64_encode(euc2jis($from_name).'?=';

###입력값이 Shift_JIS 코드일때
//$from_name = '=?ISO-2022-JP?B?'.base64_encode(sjis2jis($from_name).'?=';

$from = $from_name." <".$from_mail.">";

### 제목을 jis(iso-2022-jp)코드로 변환해서 MIME 인코딩한다.
$subject = '=?ISO-2022-JP?B?'.base64_encode(euc2jis($subject)).'?=';

###입력값이 Shift_JIS 코드일때
//$subject = '=?ISO-2022-JP?B?'.base64_encode(sjis2jis($subject)).'?=';

### 본문을 jis코드로 변환한다.
$body = euc2jis($body);

###입력값이 Shift_JIS 코드일때
//$body = sjis2jis($body);

$body = str_replace(" ", " ", $body);
$body = str_replace(" ", " ", $body);

### 첨부할 파일 데이터를 base64 인코딩 한다.
$attach = base64_encode($attach);

### 파일명을 jis( iso-2022-jp)코드로해서 MIME 인코딩한다.
$filename = '=?ISO-2022-JP?B?'.base64_encode(euc2jis($filename).'?=';

###입력값이 Shift_JIS 코드일때
//$filename = '=?ISO-2022-JP?B?'.base64_encode(sjis2jis($filename).'?=';

### 메일 송신

$sendmail = '/usr/sbin/sendmail';

$MAIL = popen("$sendmail -f $from_mail $to", "w");

###송신자의 메일어드레스가 없어도 송신이 가능한 경우
//$MAIL = popen("$sendmail -t -f $to", "w");

########################## 메일 데이타 작성

### 본문 헤더

fputs($MAIL, "From: $from ");
fputs($MAIL, "To: $to ");
fputs($MAIL, "Subject: $subject ");
fputs($MAIL, "MIME-Version: 1.0 ");
fputs($MAIL, "Content-Type: Multipart/Mixed; ");
fputs($MAIL, " ? ?boundary="$boundary" ");
fputs($MAIL, " ");

### 메일 본문 파트
fputs($MAIL, "This is a multi-part message in MIME format. ");
fputs($MAIL, " ");
fputs($MAIL, "--$boundary ");
fputs($MAIL, "Content-Type: text/plain; ");
fputs($MAIL, " ? ?charset="ISO-2022-JP" ");
fputs($MAIL, "Content-Transfer-Encoding: 7bit ");
fputs($MAIL, " ");
fputs($MAIL, "$body ");

### 첨부파일 파트
fputs($MAIL, "--$boundary ");
fputs($MAIL, "Content-Type: application/octet-stream; ");
fputs($MAIL, " ? ?name="$filename" ");
fputs($MAIL, "Content-Transfer-Encoding: base64 ");
fputs($MAIL, "Content-Disposition: attachment; ");
fputs($MAIL, " ? ?filename="$filename" ");
fputs($MAIL, " ");
fputs($MAIL, "$attach ");
fputs($MAIL, " ");

### 멀티 파트 끝
fputs($MAIL, "--$boundary" . "-- ");
pclose($MAIL);
}

###Shift_JIS코드를 JIS코드로 변환
function sjis2jis(&$sjis_string)
{
$jis_code = ';
$c = 0;
$b = unpack("C*", $sjis_string);
$n = count($b);
$esc = array(chr(0x1B).chr(0x28).chr(0x42),
chr(0x1B).chr(0x24).chr(0x42),
chr(0x1B).chr(0x28).chr(0x49));

for ($i = 1; $i <= $n; $i++) {
$b1 = $b[$i];
if (0xA1 <= $b1 && $b1 <= 0xDF) {
if ($c != 2) {
$c = 2;
$jis_code .= $esc[$c];
}
$jis_code .= chr($b1 - 0x80);
} elseif ($b1 >= 0x80) {
if ($c != 1) {
$c = 1;
$jis_code .= $esc[$c];
}
$b2 = $b[$i+1];
$b1 <<= 1;
if ($b2 < 0x9F) {
if ($b1 < 0x13F) $b1 -= 0xE1; else $b1 -= 0x61;
if ($b2 > 0x7E) $b2 -= 0x20; else $b2 -= 0x1F;
} else {
if ($b1 < 0x13F) $b1 -= 0xE0; else $b1 -= 0x60;
$b2 -= 0x7E;
}
$jis_code .= chr($b1).chr($b2);
$i++;
} else {
if ($c != 0) {
$c = 0;
$jis_code .= $esc[$c];
}
$jis_code .= chr($b1);
}
}
if ($c != 0) $jis_code .= $esc[0];

return $jis_code;
}

###일본어 EUC코드를 JIS코드로 변환
function euc2jis(&$euc_string)
{
$jis_code = ';
$c = 0;
$b = unpack("C*", $euc_string);
$n = count($b);
$esc = array(chr(0x1B).chr(0x28).chr(0x42),
chr(0x1B).chr(0x24).chr(0x42),
chr(0x1B).chr(0x28).chr(0x49));

for ($i = 1; $i <= $n; $i++) {
$b1 = $b[$i];
if ($b1 == 0x8E) {
if ($c != 2) {
$c = 2;
$jis_code .= $esc[$c];
}
$jis_code .= chr($b[$i+1] - 0x80);
$i++;
} elseif ($b1 > 0x8E) {
if ($c != 1) {
$c = 1;
$jis_code .= $esc[$c];
}
$jis_code .= chr($b1 - 0x80).chr($b[$i+1] - 0x80);
$i++;
} else {
if ($c != 0) {
$c = 0;
$jis_code .= $esc[$c];
}
$jis_code .= chr($b1);
}
}
if ($c != 0) $jis_code .= $esc[0];

return $jis_code;
}

?>

###메일 송신 테스트 샘플

$from_name = 坂本
$from_mail = "my@sendmail.com";
$to = "your@testmail.com";
$subject = "日本語題名";
$body = "これは日本語の本文です.";
$attach_data = "これは添付ファイルです. ";
$attach_data .= "添付ファイルはバイナリデ-タの場合もあります. ";
$filename = "日本語ファイル.txt";

jpmail( $from_mail, $from_name, $to, $subject, $body, $attach_data, $filename);

メ-ル送信完了

?>

'Network > PHP' 카테고리의 다른 글

PHP를 이용한 다중 연결 소켓 통신  (0) 2008.02.13
PHP 함수  (0) 2006.08.18
[PHPSchool] 오늘 본 상품 출력  (0) 2006.08.03
쇼핑몰에 들어가는 오늘 본 상품  (0) 2006.08.03
PHP를 이용한 개발 기간의 단축  (0) 2006.06.04