in hphp/runtime/ext/iconv/ext_iconv.cpp [1363:1717]
static Variant HHVM_FUNCTION(iconv_mime_encode,
const String& field_name, const String& field_value,
const Variant& preferences /* = uninit_variant */) {
php_iconv_enc_scheme_t scheme_id = PHP_ICONV_ENC_SCHEME_BASE64;
String in_charset;
String out_charset;
long line_len = 76;
String lfchars = "\r\n";
StringBuffer ret;
char *buf = NULL;
if (!preferences.isNull()) {
Variant scheme = preferences.toArray()[s_scheme];
if (scheme.isString()) {
String s = scheme.toString();
switch (*s.data()) {
case 'B': case 'b':
scheme_id = PHP_ICONV_ENC_SCHEME_BASE64;
break;
case 'Q': case 'q':
scheme_id = PHP_ICONV_ENC_SCHEME_QPRINT;
break;
}
}
Variant input_charset = preferences.toArray()[s_input_charset];
if (input_charset.isString()) {
in_charset = input_charset.toString();
if (!validate_charset(in_charset)) return false;
}
Variant output_charset = preferences.toArray()[s_output_charset];
if (output_charset.isString()) {
out_charset = output_charset.toString();
if (!validate_charset(out_charset)) return false;
}
Variant line_length = preferences.toArray()[s_line_length];
if (!line_length.isNull()) {
line_len = line_length.toInt64();
}
Variant line_break_chars = preferences.toArray()[s_line_break_chars];
if (!line_break_chars.isNull()) {
lfchars = line_break_chars.toString();
}
}
static int qp_table[256] = {
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x00 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 */
3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x20 */
1, 1, 1, 1, 1, 1, 1 ,1, 1, 1, 1, 1, 1, 3, 1, 3, /* 0x30 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x50 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x70 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x80 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x90 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xA0 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xB0 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xC0 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xD0 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xE0 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 /* 0xF0 */
};
php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1);
if ((field_name.size() + 2) >= line_len ||
(out_charset.size() + 12) >= line_len) {
/* field name is too long */
err = PHP_ICONV_ERR_TOO_BIG;
goto out;
}
cd_pl = iconv_open_helper("ASCII", in_charset.data());
if (cd_pl == (iconv_t)(-1)) {
#if ICONV_SUPPORTS_ERRNO
if (errno == EINVAL) {
err = PHP_ICONV_ERR_WRONG_CHARSET;
} else {
err = PHP_ICONV_ERR_CONVERTER;
}
#else
err = PHP_ICONV_ERR_UNKNOWN;
#endif
goto out;
}
cd = iconv_open_helper(out_charset.data(), in_charset.data());
if (cd == (iconv_t)(-1)) {
#if ICONV_SUPPORTS_ERRNO
if (errno == EINVAL) {
err = PHP_ICONV_ERR_WRONG_CHARSET;
} else {
err = PHP_ICONV_ERR_CONVERTER;
}
#else
err = PHP_ICONV_ERR_UNKNOWN;
#endif
goto out;
}
const char *in_p;
size_t in_left;
char *out_p;
size_t out_left;
buf = (char*)req::malloc_noptrs(line_len + 5);
unsigned int char_cnt;
char_cnt = line_len;
_php_iconv_appendl(ret, field_name.data(), field_name.size(), cd_pl);
char_cnt -= field_name.size();
ret.append(": ");
char_cnt -= 2;
in_p = field_value.data();
in_left = field_value.size();
do {
size_t prev_in_left;
size_t out_size;
if ((int)char_cnt < (out_charset.size() + 12)) {
ret.append(lfchars); // lfchars must be encoded in ASCII here
ret.append(' ');
char_cnt = line_len - 1;
}
ret.append("=?");
char_cnt -= 2;
ret.append(out_charset);
char_cnt -= out_charset.size();
ret.append('?');
char_cnt --;
switch (scheme_id) {
case PHP_ICONV_ENC_SCHEME_BASE64:
{
size_t ini_in_left;
const char *ini_in_p;
size_t out_reserved = 4;
ret.append('B');
char_cnt--;
ret.append('?');
char_cnt--;
prev_in_left = ini_in_left = in_left;
ini_in_p = in_p;
out_size = (char_cnt - 2) / 4 * 3;
for (;;) {
out_p = buf;
if (out_size <= out_reserved) {
err = PHP_ICONV_ERR_TOO_BIG;
goto out;
}
out_left = out_size - out_reserved;
if (iconv(cd, (ICONV_CONST char **)&in_p, &in_left,
(char **)&out_p, &out_left) == (size_t)-1) {
#if ICONV_SUPPORTS_ERRNO
switch (errno) {
case EINVAL: err = PHP_ICONV_ERR_ILLEGAL_CHAR; goto out;
case EILSEQ: err = PHP_ICONV_ERR_ILLEGAL_SEQ; goto out;
case E2BIG:
if (prev_in_left == in_left) {
err = PHP_ICONV_ERR_TOO_BIG;
goto out;
}
break;
default:
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
#else
if (prev_in_left == in_left) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
#endif
}
out_left += out_reserved;
if (iconv(cd, NULL, NULL, (char **)&out_p, &out_left) ==
(size_t)-1) {
#if ICONV_SUPPORTS_ERRNO
if (errno != E2BIG) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
#else
if (out_left != 0) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
#endif
} else {
break;
}
if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
out_reserved += 4;
in_left = ini_in_left;
in_p = ini_in_p;
}
prev_in_left = in_left;
int encoded_len = out_size - out_left;
String encoded = string_base64_encode(buf, encoded_len);
if ((int)char_cnt < encoded.size()) {
/* something went wrong! */
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
ret.append(encoded);
char_cnt -= encoded.size();
ret.append("?=");
char_cnt -= 2;
}
break; /* case PHP_ICONV_ENC_SCHEME_BASE64: */
case PHP_ICONV_ENC_SCHEME_QPRINT:
{
size_t ini_in_left;
const char *ini_in_p;
const unsigned char *p;
size_t nbytes_required;
ret.append('Q');
char_cnt--;
ret.append('?');
char_cnt--;
prev_in_left = ini_in_left = in_left;
ini_in_p = in_p;
for (out_size = (char_cnt - 2) / 3; out_size > 0;) {
size_t prev_out_left ATTRIBUTE_UNUSED;
nbytes_required = 0;
out_p = buf;
out_left = out_size;
if (iconv(cd, (ICONV_CONST char **)&in_p, &in_left,
(char **)&out_p, &out_left) == (size_t)-1) {
#if ICONV_SUPPORTS_ERRNO
switch (errno) {
case EINVAL: err = PHP_ICONV_ERR_ILLEGAL_CHAR; goto out;
case EILSEQ: err = PHP_ICONV_ERR_ILLEGAL_SEQ; goto out;
case E2BIG:
if (prev_in_left == in_left) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
break;
default:
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
#else
if (prev_in_left == in_left) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
#endif
}
prev_out_left = out_left;
if (iconv(cd, NULL, NULL, (char **)&out_p, &out_left) ==
(size_t)-1) {
#if ICONV_SUPPORTS_ERRNO
if (errno != E2BIG) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
#else
if (out_left == prev_out_left) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
#endif
}
for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
nbytes_required += qp_table[*p];
}
if (nbytes_required <= char_cnt - 2) {
break;
}
out_size -= ((nbytes_required - (char_cnt - 2)) + 1) / (3 - 1);
in_left = ini_in_left;
in_p = ini_in_p;
}
for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
if (qp_table[*p] == 1) {
ret.append(*(char*)p);
char_cnt--;
} else {
static char qp_digits[] = "0123456789ABCDEF";
ret.append('=');
ret.append(qp_digits[(*p >> 4) & 0x0f]);
ret.append(qp_digits[(*p & 0x0f)]);
char_cnt -= 3;
}
}
prev_in_left = in_left;
ret.append("?=");
char_cnt -= 2;
if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
err = PHP_ICONV_ERR_UNKNOWN;
goto out;
}
} break; /* case PHP_ICONV_ENC_SCHEME_QPRINT: */
}
} while (in_left > 0);
out:
if (cd != (iconv_t)(-1)) {
iconv_close(cd);
}
if (cd_pl != (iconv_t)(-1)) {
iconv_close(cd_pl);
}
if (buf != NULL) {
req::free(buf);
}
if (err != PHP_ICONV_ERR_SUCCESS) {
return false;
}
return ret.detach();
}