12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651 |
- /*
- +----------------------------------------------------------------------+
- | Copyright (c) The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | https://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Rui Hirokawa <rui_hirokawa@ybb.ne.jp> |
- | Stig Bakken <ssb@php.net> |
- | Moriyoshi Koizumi <moriyoshi@php.net> |
- +----------------------------------------------------------------------+
- */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include "php.h"
- #include "php_globals.h"
- #include "ext/standard/info.h"
- #include "main/php_output.h"
- #include "SAPI.h"
- #include "php_ini.h"
- #include <stdlib.h>
- #include <errno.h>
- #include "php_iconv.h"
- #ifdef HAVE_ICONV
- #include <iconv.h>
- #ifdef HAVE_GLIBC_ICONV
- #include <gnu/libc-version.h>
- #endif
- #ifdef HAVE_LIBICONV
- #undef iconv
- #endif
- #include "zend_smart_str.h"
- #include "ext/standard/base64.h"
- #include "ext/standard/quot_print.h"
- #include "iconv_arginfo.h"
- #define _php_iconv_memequal(a, b, c) \
- (memcmp(a, b, c) == 0)
- ZEND_DECLARE_MODULE_GLOBALS(iconv)
- static PHP_GINIT_FUNCTION(iconv);
- /* {{{ iconv_module_entry */
- zend_module_entry iconv_module_entry = {
- STANDARD_MODULE_HEADER,
- "iconv",
- ext_functions,
- PHP_MINIT(miconv),
- PHP_MSHUTDOWN(miconv),
- NULL,
- NULL,
- PHP_MINFO(miconv),
- PHP_ICONV_VERSION,
- PHP_MODULE_GLOBALS(iconv),
- PHP_GINIT(iconv),
- NULL,
- NULL,
- STANDARD_MODULE_PROPERTIES_EX
- };
- /* }}} */
- #ifdef COMPILE_DL_ICONV
- #ifdef ZTS
- ZEND_TSRMLS_CACHE_DEFINE()
- #endif
- ZEND_GET_MODULE(iconv)
- #endif
- /* {{{ PHP_GINIT_FUNCTION */
- static PHP_GINIT_FUNCTION(iconv)
- {
- #if defined(COMPILE_DL_ICONV) && defined(ZTS)
- ZEND_TSRMLS_CACHE_UPDATE();
- #endif
- iconv_globals->input_encoding = NULL;
- iconv_globals->output_encoding = NULL;
- iconv_globals->internal_encoding = NULL;
- }
- /* }}} */
- #if defined(HAVE_LIBICONV) && defined(ICONV_ALIASED_LIBICONV)
- #define iconv libiconv
- #endif
- /* {{{ typedef enum php_iconv_enc_scheme_t */
- typedef enum _php_iconv_enc_scheme_t {
- PHP_ICONV_ENC_SCHEME_BASE64,
- PHP_ICONV_ENC_SCHEME_QPRINT
- } php_iconv_enc_scheme_t;
- /* }}} */
- #define PHP_ICONV_MIME_DECODE_STRICT (1<<0)
- #define PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR (1<<1)
- /* {{{ prototypes */
- static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, iconv_t cd);
- static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd);
- static void _php_iconv_show_error(php_iconv_err_t err, const char *out_charset, const char *in_charset);
- static php_iconv_err_t _php_iconv_strlen(size_t *pretval, const char *str, size_t nbytes, const char *enc);
- static php_iconv_err_t _php_iconv_substr(smart_str *pretval, const char *str, size_t nbytes, zend_long offset, zend_long len, const char *enc);
- static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fname, size_t fname_nbytes, const char *fval, size_t fval_nbytes, size_t max_line_len, const char *lfchars, php_iconv_enc_scheme_t enc_scheme, const char *out_charset, const char *enc);
- static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *str, size_t str_nbytes, const char *enc, const char **next_pos, int mode);
- static php_iconv_err_t php_iconv_stream_filter_register_factory(void);
- static php_iconv_err_t php_iconv_stream_filter_unregister_factory(void);
- static int php_iconv_output_conflict(const char *handler_name, size_t handler_name_len);
- static php_output_handler *php_iconv_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags);
- static int php_iconv_output_handler(void **nothing, php_output_context *output_context);
- /* }}} */
- /* {{{ static globals */
- static const char _generic_superset_name[] = ICONV_UCS4_ENCODING;
- #define GENERIC_SUPERSET_NAME _generic_superset_name
- #define GENERIC_SUPERSET_NBYTES 4
- /* }}} */
- static PHP_INI_MH(OnUpdateInputEncoding)
- {
- if (ZSTR_LEN(new_value) >= ICONV_CSNMAXLEN) {
- return FAILURE;
- }
- if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
- php_error_docref("ref.iconv", E_DEPRECATED, "Use of iconv.input_encoding is deprecated");
- }
- OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
- return SUCCESS;
- }
- static PHP_INI_MH(OnUpdateOutputEncoding)
- {
- if (ZSTR_LEN(new_value) >= ICONV_CSNMAXLEN) {
- return FAILURE;
- }
- if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
- php_error_docref("ref.iconv", E_DEPRECATED, "Use of iconv.output_encoding is deprecated");
- }
- OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
- return SUCCESS;
- }
- static PHP_INI_MH(OnUpdateInternalEncoding)
- {
- if (ZSTR_LEN(new_value) >= ICONV_CSNMAXLEN) {
- return FAILURE;
- }
- if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
- php_error_docref("ref.iconv", E_DEPRECATED, "Use of iconv.internal_encoding is deprecated");
- }
- OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
- return SUCCESS;
- }
- /* {{{ PHP_INI */
- PHP_INI_BEGIN()
- STD_PHP_INI_ENTRY("iconv.input_encoding", "", PHP_INI_ALL, OnUpdateInputEncoding, input_encoding, zend_iconv_globals, iconv_globals)
- STD_PHP_INI_ENTRY("iconv.output_encoding", "", PHP_INI_ALL, OnUpdateOutputEncoding, output_encoding, zend_iconv_globals, iconv_globals)
- STD_PHP_INI_ENTRY("iconv.internal_encoding", "", PHP_INI_ALL, OnUpdateInternalEncoding, internal_encoding, zend_iconv_globals, iconv_globals)
- PHP_INI_END()
- /* }}} */
- /* {{{ PHP_MINIT_FUNCTION */
- PHP_MINIT_FUNCTION(miconv)
- {
- char *version = "unknown";
- REGISTER_INI_ENTRIES();
- #ifdef HAVE_LIBICONV
- {
- static char buf[16];
- snprintf(buf, sizeof(buf), "%d.%d",
- _libiconv_version >> 8, _libiconv_version & 0xff);
- version = buf;
- }
- #elif HAVE_GLIBC_ICONV
- version = (char *)gnu_get_libc_version();
- #endif
- #ifdef PHP_ICONV_IMPL
- REGISTER_STRING_CONSTANT("ICONV_IMPL", PHP_ICONV_IMPL, CONST_CS | CONST_PERSISTENT);
- #elif HAVE_LIBICONV
- REGISTER_STRING_CONSTANT("ICONV_IMPL", "libiconv", CONST_CS | CONST_PERSISTENT);
- #else
- REGISTER_STRING_CONSTANT("ICONV_IMPL", "unknown", CONST_CS | CONST_PERSISTENT);
- #endif
- REGISTER_STRING_CONSTANT("ICONV_VERSION", version, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_STRICT", PHP_ICONV_MIME_DECODE_STRICT, CONST_CS | CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_CONTINUE_ON_ERROR", PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR, CONST_CS | CONST_PERSISTENT);
- if (php_iconv_stream_filter_register_factory() != PHP_ICONV_ERR_SUCCESS) {
- return FAILURE;
- }
- php_output_handler_alias_register(ZEND_STRL("ob_iconv_handler"), php_iconv_output_handler_init);
- php_output_handler_conflict_register(ZEND_STRL("ob_iconv_handler"), php_iconv_output_conflict);
- return SUCCESS;
- }
- /* }}} */
- /* {{{ PHP_MSHUTDOWN_FUNCTION */
- PHP_MSHUTDOWN_FUNCTION(miconv)
- {
- php_iconv_stream_filter_unregister_factory();
- UNREGISTER_INI_ENTRIES();
- return SUCCESS;
- }
- /* }}} */
- /* {{{ PHP_MINFO_FUNCTION */
- PHP_MINFO_FUNCTION(miconv)
- {
- zval *iconv_impl, *iconv_ver;
- iconv_impl = zend_get_constant_str("ICONV_IMPL", sizeof("ICONV_IMPL")-1);
- iconv_ver = zend_get_constant_str("ICONV_VERSION", sizeof("ICONV_VERSION")-1);
- php_info_print_table_start();
- php_info_print_table_row(2, "iconv support", "enabled");
- php_info_print_table_row(2, "iconv implementation", Z_STRVAL_P(iconv_impl));
- php_info_print_table_row(2, "iconv library version", Z_STRVAL_P(iconv_ver));
- php_info_print_table_end();
- DISPLAY_INI_ENTRIES();
- }
- /* }}} */
- static const char *get_internal_encoding(void) {
- if (ICONVG(internal_encoding) && ICONVG(internal_encoding)[0]) {
- return ICONVG(internal_encoding);
- }
- return php_get_internal_encoding();
- }
- static const char *get_input_encoding(void) {
- if (ICONVG(input_encoding) && ICONVG(input_encoding)[0]) {
- return ICONVG(input_encoding);
- }
- return php_get_input_encoding();
- }
- static const char *get_output_encoding(void) {
- if (ICONVG(output_encoding) && ICONVG(output_encoding)[0]) {
- return ICONVG(output_encoding);
- }
- return php_get_output_encoding();
- }
- static int php_iconv_output_conflict(const char *handler_name, size_t handler_name_len)
- {
- if (php_output_get_level()) {
- if (php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("ob_iconv_handler"))
- || php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("mb_output_handler"))) {
- return FAILURE;
- }
- }
- return SUCCESS;
- }
- static php_output_handler *php_iconv_output_handler_init(const char *handler_name, size_t handler_name_len, size_t chunk_size, int flags)
- {
- return php_output_handler_create_internal(handler_name, handler_name_len, php_iconv_output_handler, chunk_size, flags);
- }
- static int php_iconv_output_handler(void **nothing, php_output_context *output_context)
- {
- char *s, *content_type, *mimetype = NULL;
- int output_status, mimetype_len = 0;
- if (output_context->op & PHP_OUTPUT_HANDLER_START) {
- output_status = php_output_get_status();
- if (output_status & PHP_OUTPUT_SENT) {
- return FAILURE;
- }
- if (SG(sapi_headers).mimetype && !strncasecmp(SG(sapi_headers).mimetype, "text/", 5)) {
- if ((s = strchr(SG(sapi_headers).mimetype,';')) == NULL){
- mimetype = SG(sapi_headers).mimetype;
- } else {
- mimetype = SG(sapi_headers).mimetype;
- mimetype_len = (int)(s - SG(sapi_headers).mimetype);
- }
- } else if (SG(sapi_headers).send_default_content_type) {
- mimetype = SG(default_mimetype) ? SG(default_mimetype) : SAPI_DEFAULT_MIMETYPE;
- }
- if (mimetype != NULL && (!(output_context->op & PHP_OUTPUT_HANDLER_CLEAN) || ((output_context->op & PHP_OUTPUT_HANDLER_START) && !(output_context->op & PHP_OUTPUT_HANDLER_FINAL)))) {
- size_t len;
- char *p = strstr(get_output_encoding(), "//");
- if (p) {
- len = spprintf(&content_type, 0, "Content-Type:%.*s; charset=%.*s", mimetype_len ? mimetype_len : (int) strlen(mimetype), mimetype, (int) (p - get_output_encoding()), get_output_encoding());
- } else {
- len = spprintf(&content_type, 0, "Content-Type:%.*s; charset=%s", mimetype_len ? mimetype_len : (int) strlen(mimetype), mimetype, get_output_encoding());
- }
- if (content_type && SUCCESS == sapi_add_header(content_type, len, 0)) {
- SG(sapi_headers).send_default_content_type = 0;
- php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL);
- }
- }
- }
- if (output_context->in.used) {
- zend_string *out;
- output_context->out.free = 1;
- _php_iconv_show_error(php_iconv_string(output_context->in.data, output_context->in.used, &out, get_output_encoding(), get_internal_encoding()), get_output_encoding(), get_internal_encoding());
- if (out) {
- output_context->out.data = estrndup(ZSTR_VAL(out), ZSTR_LEN(out));
- output_context->out.used = ZSTR_LEN(out);
- zend_string_efree(out);
- } else {
- output_context->out.data = NULL;
- output_context->out.used = 0;
- }
- }
- return SUCCESS;
- }
- /* {{{ _php_iconv_appendl() */
- static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, iconv_t cd)
- {
- const char *in_p = s;
- size_t in_left = l;
- char *out_p;
- size_t out_left = 0;
- size_t buf_growth = 128;
- if (in_p != NULL) {
- while (in_left > 0) {
- out_left = buf_growth;
- smart_str_alloc(d, out_left, 0);
- out_p = ZSTR_VAL((d)->s) + ZSTR_LEN((d)->s);
- if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
- switch (errno) {
- case EINVAL:
- return PHP_ICONV_ERR_ILLEGAL_CHAR;
- case EILSEQ:
- return PHP_ICONV_ERR_ILLEGAL_SEQ;
- case E2BIG:
- break;
- default:
- return PHP_ICONV_ERR_UNKNOWN;
- }
- }
- ZSTR_LEN((d)->s) += (buf_growth - out_left);
- buf_growth <<= 1;
- }
- } else {
- for (;;) {
- out_left = buf_growth;
- smart_str_alloc(d, out_left, 0);
- out_p = ZSTR_VAL((d)->s) + ZSTR_LEN((d)->s);
- if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)0) {
- ZSTR_LEN((d)->s) += (buf_growth - out_left);
- break;
- } else {
- if (errno != E2BIG) {
- return PHP_ICONV_ERR_UNKNOWN;
- }
- }
- ZSTR_LEN((d)->s) += (buf_growth - out_left);
- buf_growth <<= 1;
- }
- }
- return PHP_ICONV_ERR_SUCCESS;
- }
- /* }}} */
- /* {{{ _php_iconv_appendc() */
- static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd)
- {
- return _php_iconv_appendl(d, &c, 1, cd);
- }
- /* }}} */
- /* {{{ */
- #ifdef ICONV_BROKEN_IGNORE
- static int _php_check_ignore(const char *charset)
- {
- size_t clen = strlen(charset);
- if (clen >= 9 && strcmp("//IGNORE", charset+clen-8) == 0) {
- return 1;
- }
- if (clen >= 19 && strcmp("//IGNORE//TRANSLIT", charset+clen-18) == 0) {
- return 1;
- }
- return 0;
- }
- #else
- #define _php_check_ignore(x) (0)
- #endif
- /* }}} */
- /* {{{ php_iconv_string() */
- PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len, zend_string **out, const char *out_charset, const char *in_charset)
- {
- iconv_t cd;
- size_t in_left, out_size, out_left;
- char *out_p;
- size_t bsz, result = 0;
- php_iconv_err_t retval = PHP_ICONV_ERR_SUCCESS;
- zend_string *out_buf;
- int ignore_ilseq = _php_check_ignore(out_charset);
- *out = NULL;
- cd = iconv_open(out_charset, in_charset);
- if (cd == (iconv_t)(-1)) {
- if (errno == EINVAL) {
- return PHP_ICONV_ERR_WRONG_CHARSET;
- } else {
- return PHP_ICONV_ERR_CONVERTER;
- }
- }
- in_left= in_len;
- out_left = in_len + 32; /* Avoid realloc() most cases */
- out_size = 0;
- bsz = out_left;
- out_buf = zend_string_alloc(bsz, 0);
- out_p = ZSTR_VAL(out_buf);
- while (in_left > 0) {
- result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left);
- out_size = bsz - out_left;
- if (result == (size_t)(-1)) {
- if (ignore_ilseq && errno == EILSEQ) {
- if (in_left <= 1) {
- result = 0;
- } else {
- errno = 0;
- in_p++;
- in_left--;
- continue;
- }
- }
- if (errno == E2BIG && in_left > 0) {
- /* converted string is longer than out buffer */
- bsz += in_len;
- out_buf = zend_string_extend(out_buf, bsz, 0);
- out_p = ZSTR_VAL(out_buf);
- out_p += out_size;
- out_left = bsz - out_size;
- continue;
- }
- }
- break;
- }
- if (result != (size_t)(-1)) {
- /* flush the shift-out sequences */
- for (;;) {
- result = iconv(cd, NULL, NULL, (char **) &out_p, &out_left);
- out_size = bsz - out_left;
- if (result != (size_t)(-1)) {
- break;
- }
- if (errno == E2BIG) {
- bsz += 16;
- out_buf = zend_string_extend(out_buf, bsz, 0);
- out_p = ZSTR_VAL(out_buf);
- out_p += out_size;
- out_left = bsz - out_size;
- } else {
- break;
- }
- }
- }
- iconv_close(cd);
- if (result == (size_t)(-1)) {
- switch (errno) {
- case EINVAL:
- retval = PHP_ICONV_ERR_ILLEGAL_CHAR;
- break;
- case EILSEQ:
- retval = PHP_ICONV_ERR_ILLEGAL_SEQ;
- break;
- case E2BIG:
- /* should not happen */
- retval = PHP_ICONV_ERR_TOO_BIG;
- break;
- default:
- /* other error */
- zend_string_efree(out_buf);
- return PHP_ICONV_ERR_UNKNOWN;
- }
- }
- *out_p = '\0';
- ZSTR_LEN(out_buf) = out_size;
- *out = out_buf;
- return retval;
- }
- /* }}} */
- /* {{{ _php_iconv_strlen() */
- static php_iconv_err_t _php_iconv_strlen(size_t *pretval, const char *str, size_t nbytes, const char *enc)
- {
- char buf[GENERIC_SUPERSET_NBYTES*2];
- php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
- iconv_t cd;
- const char *in_p;
- size_t in_left;
- char *out_p;
- size_t out_left;
- size_t cnt;
- int more;
- *pretval = (size_t)-1;
- cd = iconv_open(GENERIC_SUPERSET_NAME, enc);
- if (cd == (iconv_t)(-1)) {
- if (errno == EINVAL) {
- return PHP_ICONV_ERR_WRONG_CHARSET;
- } else {
- return PHP_ICONV_ERR_CONVERTER;
- }
- }
- errno = 0;
- out_left = 0;
- more = nbytes > 0;
- for (in_p = str, in_left = nbytes, cnt = 0; more;) {
- out_p = buf;
- out_left = sizeof(buf);
- more = in_left > 0;
- iconv(cd, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
- if (out_left == sizeof(buf)) {
- break;
- } else {
- ZEND_ASSERT((sizeof(buf) - out_left) % GENERIC_SUPERSET_NBYTES == 0);
- cnt += (sizeof(buf) - out_left) / GENERIC_SUPERSET_NBYTES;
- }
- }
- switch (errno) {
- case EINVAL:
- err = PHP_ICONV_ERR_ILLEGAL_CHAR;
- break;
- case EILSEQ:
- err = PHP_ICONV_ERR_ILLEGAL_SEQ;
- break;
- case E2BIG:
- case 0:
- *pretval = cnt;
- break;
- default:
- err = PHP_ICONV_ERR_UNKNOWN;
- break;
- }
- iconv_close(cd);
- return err;
- }
- /* }}} */
- /* {{{ _php_iconv_substr() */
- static php_iconv_err_t _php_iconv_substr(smart_str *pretval,
- const char *str, size_t nbytes, zend_long offset, zend_long len, const char *enc)
- {
- char buf[GENERIC_SUPERSET_NBYTES];
- php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
- iconv_t cd1, cd2;
- const char *in_p;
- size_t in_left;
- char *out_p;
- size_t out_left;
- size_t cnt;
- size_t total_len;
- int more;
- err = _php_iconv_strlen(&total_len, str, nbytes, enc);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- return err;
- }
- if (offset < 0) {
- if ((offset += total_len) < 0) {
- offset = 0;
- }
- } else if ((size_t)offset > total_len) {
- offset = total_len;
- }
- if (len < 0) {
- if ((len += (total_len - offset)) < 0) {
- len = 0;
- }
- } else if ((size_t)len > total_len) {
- len = total_len;
- }
- if ((size_t)(offset + len) > total_len ) {
- /* trying to compute the length */
- len = total_len - offset;
- }
- if (len == 0) {
- smart_str_appendl(pretval, "", 0);
- smart_str_0(pretval);
- return PHP_ICONV_ERR_SUCCESS;
- }
- cd1 = iconv_open(GENERIC_SUPERSET_NAME, enc);
- if (cd1 == (iconv_t)(-1)) {
- if (errno == EINVAL) {
- return PHP_ICONV_ERR_WRONG_CHARSET;
- } else {
- return PHP_ICONV_ERR_CONVERTER;
- }
- }
- cd2 = (iconv_t)NULL;
- errno = 0;
- more = nbytes > 0 && len > 0;
- for (in_p = str, in_left = nbytes, cnt = 0; more; ++cnt) {
- out_p = buf;
- out_left = sizeof(buf);
- more = in_left > 0 && len > 0;
- iconv(cd1, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
- if (out_left == sizeof(buf)) {
- break;
- }
- if ((zend_long)cnt >= offset) {
- if (cd2 == (iconv_t)NULL) {
- cd2 = iconv_open(enc, GENERIC_SUPERSET_NAME);
- if (cd2 == (iconv_t)(-1)) {
- cd2 = (iconv_t)NULL;
- if (errno == EINVAL) {
- err = PHP_ICONV_ERR_WRONG_CHARSET;
- } else {
- err = PHP_ICONV_ERR_CONVERTER;
- }
- break;
- }
- }
- if (_php_iconv_appendl(pretval, buf, sizeof(buf), cd2) != PHP_ICONV_ERR_SUCCESS) {
- break;
- }
- --len;
- }
- }
- switch (errno) {
- case EINVAL:
- err = PHP_ICONV_ERR_ILLEGAL_CHAR;
- break;
- case EILSEQ:
- err = PHP_ICONV_ERR_ILLEGAL_SEQ;
- break;
- case E2BIG:
- break;
- }
- if (err == PHP_ICONV_ERR_SUCCESS) {
- if (cd2 != (iconv_t)NULL) {
- _php_iconv_appendl(pretval, NULL, 0, cd2);
- }
- smart_str_0(pretval);
- }
- if (cd1 != (iconv_t)NULL) {
- iconv_close(cd1);
- }
- if (cd2 != (iconv_t)NULL) {
- iconv_close(cd2);
- }
- return err;
- }
- /* }}} */
- /* {{{ _php_iconv_strpos() */
- static php_iconv_err_t _php_iconv_strpos(size_t *pretval,
- const char *haystk, size_t haystk_nbytes,
- const char *ndl, size_t ndl_nbytes,
- size_t offset, const char *enc, bool reverse)
- {
- char buf[GENERIC_SUPERSET_NBYTES];
- php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
- iconv_t cd;
- const char *in_p;
- size_t in_left;
- char *out_p;
- size_t out_left;
- size_t cnt;
- zend_string *ndl_buf;
- const char *ndl_buf_p;
- size_t ndl_buf_left;
- size_t match_ofs;
- int more;
- size_t iconv_ret;
- *pretval = (size_t)-1;
- err = php_iconv_string(ndl, ndl_nbytes, &ndl_buf, GENERIC_SUPERSET_NAME, enc);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- if (ndl_buf != NULL) {
- zend_string_efree(ndl_buf);
- }
- return err;
- }
- cd = iconv_open(GENERIC_SUPERSET_NAME, enc);
- if (cd == (iconv_t)(-1)) {
- if (ndl_buf != NULL) {
- zend_string_efree(ndl_buf);
- }
- if (errno == EINVAL) {
- return PHP_ICONV_ERR_WRONG_CHARSET;
- } else {
- return PHP_ICONV_ERR_CONVERTER;
- }
- }
- ndl_buf_p = ZSTR_VAL(ndl_buf);
- ndl_buf_left = ZSTR_LEN(ndl_buf);
- match_ofs = (size_t)-1;
- more = haystk_nbytes > 0;
- for (in_p = haystk, in_left = haystk_nbytes, cnt = 0; more; ++cnt) {
- out_p = buf;
- out_left = sizeof(buf);
- more = in_left > 0;
- iconv_ret = iconv(cd, more ? (char **)&in_p : NULL, more ? &in_left : NULL, (char **) &out_p, &out_left);
- if (out_left == sizeof(buf)) {
- break;
- }
- if (iconv_ret == (size_t)-1) {
- switch (errno) {
- case EINVAL:
- err = PHP_ICONV_ERR_ILLEGAL_CHAR;
- break;
- case EILSEQ:
- err = PHP_ICONV_ERR_ILLEGAL_SEQ;
- break;
- case E2BIG:
- break;
- default:
- err = PHP_ICONV_ERR_UNKNOWN;
- break;
- }
- }
- if (cnt >= offset) {
- if (_php_iconv_memequal(buf, ndl_buf_p, sizeof(buf))) {
- if (match_ofs == (size_t)-1) {
- match_ofs = cnt;
- }
- ndl_buf_p += GENERIC_SUPERSET_NBYTES;
- ndl_buf_left -= GENERIC_SUPERSET_NBYTES;
- if (ndl_buf_left == 0) {
- *pretval = match_ofs;
- if (reverse) {
- /* If searching backward, continue trying to find a later match. */
- ndl_buf_p = ZSTR_VAL(ndl_buf);
- ndl_buf_left = ZSTR_LEN(ndl_buf);
- match_ofs = -1;
- } else {
- /* If searching forward, stop at first match. */
- break;
- }
- }
- } else {
- size_t i, j, lim;
- i = 0;
- j = GENERIC_SUPERSET_NBYTES;
- lim = (size_t)(ndl_buf_p - ZSTR_VAL(ndl_buf));
- while (j < lim) {
- if (_php_iconv_memequal(&ZSTR_VAL(ndl_buf)[j], &ZSTR_VAL(ndl_buf)[i],
- GENERIC_SUPERSET_NBYTES)) {
- i += GENERIC_SUPERSET_NBYTES;
- } else {
- j -= i;
- i = 0;
- }
- j += GENERIC_SUPERSET_NBYTES;
- }
- if (_php_iconv_memequal(buf, &ZSTR_VAL(ndl_buf)[i], sizeof(buf))) {
- match_ofs += (lim - i) / GENERIC_SUPERSET_NBYTES;
- i += GENERIC_SUPERSET_NBYTES;
- ndl_buf_p = &ZSTR_VAL(ndl_buf)[i];
- ndl_buf_left = ZSTR_LEN(ndl_buf) - i;
- } else {
- match_ofs = (size_t)-1;
- ndl_buf_p = ZSTR_VAL(ndl_buf);
- ndl_buf_left = ZSTR_LEN(ndl_buf);
- }
- }
- }
- }
- if (ndl_buf) {
- zend_string_efree(ndl_buf);
- }
- iconv_close(cd);
- if (err == PHP_ICONV_ERR_SUCCESS && offset > cnt) {
- return PHP_ICONV_ERR_OUT_BY_BOUNDS;
- }
- return err;
- }
- /* }}} */
- /* {{{ _php_iconv_mime_encode() */
- static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fname, size_t fname_nbytes, const char *fval, size_t fval_nbytes, size_t max_line_len, const char *lfchars, php_iconv_enc_scheme_t enc_scheme, const char *out_charset, const char *enc)
- {
- php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
- iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1);
- size_t char_cnt = 0;
- size_t out_charset_len;
- size_t lfchars_len;
- char *buf = NULL;
- const char *in_p;
- size_t in_left;
- char *out_p;
- size_t out_left;
- zend_string *encoded = NULL;
- 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 */
- };
- out_charset_len = strlen(out_charset);
- lfchars_len = strlen(lfchars);
- if ((fname_nbytes + 2) >= max_line_len
- || (out_charset_len + 12) >= max_line_len) {
- /* field name is too long */
- err = PHP_ICONV_ERR_TOO_BIG;
- goto out;
- }
- cd_pl = iconv_open(ICONV_ASCII_ENCODING, enc);
- if (cd_pl == (iconv_t)(-1)) {
- if (errno == EINVAL) {
- err = PHP_ICONV_ERR_WRONG_CHARSET;
- } else {
- err = PHP_ICONV_ERR_CONVERTER;
- }
- goto out;
- }
- cd = iconv_open(out_charset, enc);
- if (cd == (iconv_t)(-1)) {
- if (errno == EINVAL) {
- err = PHP_ICONV_ERR_WRONG_CHARSET;
- } else {
- err = PHP_ICONV_ERR_CONVERTER;
- }
- goto out;
- }
- buf = safe_emalloc(1, max_line_len, 5);
- char_cnt = max_line_len;
- _php_iconv_appendl(pretval, fname, fname_nbytes, cd_pl);
- char_cnt -= fname_nbytes;
- smart_str_appendl(pretval, ": ", sizeof(": ") - 1);
- char_cnt -= 2;
- in_p = fval;
- in_left = fval_nbytes;
- do {
- size_t prev_in_left;
- size_t out_size;
- size_t encoded_word_min_len = sizeof("=\?\?X\?\?=")-1 + out_charset_len + (enc_scheme == PHP_ICONV_ENC_SCHEME_BASE64 ? 4 : 3);
- if (char_cnt < encoded_word_min_len + lfchars_len + 1) {
- /* lfchars must be encoded in ASCII here*/
- smart_str_appendl(pretval, lfchars, lfchars_len);
- smart_str_appendc(pretval, ' ');
- char_cnt = max_line_len - 1;
- }
- smart_str_appendl(pretval, "=?", sizeof("=?") - 1);
- char_cnt -= 2;
- smart_str_appendl(pretval, out_charset, out_charset_len);
- char_cnt -= out_charset_len;
- smart_str_appendc(pretval, '?');
- char_cnt --;
- switch (enc_scheme) {
- case PHP_ICONV_ENC_SCHEME_BASE64: {
- size_t ini_in_left;
- const char *ini_in_p;
- size_t out_reserved = 4;
- smart_str_appendc(pretval, 'B');
- char_cnt--;
- smart_str_appendc(pretval, '?');
- 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, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
- 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;
- }
- }
- out_left += out_reserved;
- if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
- if (errno != E2BIG) {
- err = PHP_ICONV_ERR_UNKNOWN;
- goto out;
- }
- } 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;
- encoded = php_base64_encode((unsigned char *) buf, (out_size - out_left));
- if (char_cnt < ZSTR_LEN(encoded)) {
- /* something went wrong! */
- err = PHP_ICONV_ERR_UNKNOWN;
- goto out;
- }
- smart_str_appendl(pretval, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
- char_cnt -= ZSTR_LEN(encoded);
- smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
- char_cnt -= 2;
- zend_string_release_ex(encoded, 0);
- encoded = NULL;
- } 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;
- smart_str_appendc(pretval, 'Q');
- char_cnt--;
- smart_str_appendc(pretval, '?');
- char_cnt--;
- prev_in_left = ini_in_left = in_left;
- ini_in_p = in_p;
- for (out_size = (char_cnt - 2); out_size > 0;) {
- nbytes_required = 0;
- out_p = buf;
- out_left = out_size;
- if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
- 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;
- }
- }
- if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
- if (errno != E2BIG) {
- err = PHP_ICONV_ERR_UNKNOWN;
- goto out;
- }
- }
- 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)) + 2) / 3;
- 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) {
- smart_str_appendc(pretval, *(char *)p);
- char_cnt--;
- } else {
- static const char qp_digits[] = "0123456789ABCDEF";
- smart_str_appendc(pretval, '=');
- smart_str_appendc(pretval, qp_digits[(*p >> 4) & 0x0f]);
- smart_str_appendc(pretval, qp_digits[(*p & 0x0f)]);
- char_cnt -= 3;
- }
- }
- smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
- 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);
- smart_str_0(pretval);
- out:
- if (cd != (iconv_t)(-1)) {
- iconv_close(cd);
- }
- if (cd_pl != (iconv_t)(-1)) {
- iconv_close(cd_pl);
- }
- if (encoded != NULL) {
- zend_string_release_ex(encoded, 0);
- }
- if (buf != NULL) {
- efree(buf);
- }
- return err;
- }
- /* }}} */
- /* {{{ _php_iconv_mime_decode() */
- static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *str, size_t str_nbytes, const char *enc, const char **next_pos, int mode)
- {
- php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
- iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1);
- const char *p1;
- size_t str_left;
- unsigned int scan_stat = 0;
- const char *csname = NULL;
- size_t csname_len;
- const char *encoded_text = NULL;
- size_t encoded_text_len = 0;
- const char *encoded_word = NULL;
- const char *spaces = NULL;
- php_iconv_enc_scheme_t enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64;
- if (next_pos != NULL) {
- *next_pos = NULL;
- }
- cd_pl = iconv_open(enc, ICONV_ASCII_ENCODING);
- if (cd_pl == (iconv_t)(-1)) {
- if (errno == EINVAL) {
- err = PHP_ICONV_ERR_WRONG_CHARSET;
- } else {
- err = PHP_ICONV_ERR_CONVERTER;
- }
- goto out;
- }
- p1 = str;
- for (str_left = str_nbytes; str_left > 0; str_left--, p1++) {
- int eos = 0;
- switch (scan_stat) {
- case 0: /* expecting any character */
- switch (*p1) {
- case '\r': /* part of an EOL sequence? */
- scan_stat = 7;
- break;
- case '\n':
- scan_stat = 8;
- break;
- case '=': /* first letter of an encoded chunk */
- encoded_word = p1;
- scan_stat = 1;
- break;
- case ' ': case '\t': /* a chunk of whitespaces */
- spaces = p1;
- scan_stat = 11;
- break;
- default: /* first letter of a non-encoded word */
- err = _php_iconv_appendc(pretval, *p1, cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- if (mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR) {
- err = PHP_ICONV_ERR_SUCCESS;
- } else {
- goto out;
- }
- }
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- }
- break;
- }
- break;
- case 1: /* expecting a delimiter */
- if (*p1 != '?') {
- if (*p1 == '\r' || *p1 == '\n') {
- --p1;
- }
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- goto out;
- }
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- } else {
- scan_stat = 0;
- }
- break;
- }
- csname = p1 + 1;
- scan_stat = 2;
- break;
- case 2: /* expecting a charset name */
- switch (*p1) {
- case '?': /* normal delimiter: encoding scheme follows */
- scan_stat = 3;
- break;
- case '*': /* new style delimiter: locale id follows */
- scan_stat = 10;
- break;
- case '\r': case '\n': /* not an encoded-word */
- --p1;
- _php_iconv_appendc(pretval, '=', cd_pl);
- _php_iconv_appendc(pretval, '?', cd_pl);
- err = _php_iconv_appendl(pretval, csname, (size_t)((p1 + 1) - csname), cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- goto out;
- }
- csname = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- }
- else {
- scan_stat = 0;
- }
- continue;
- }
- if (scan_stat != 2) {
- char tmpbuf[80];
- if (csname == NULL) {
- err = PHP_ICONV_ERR_MALFORMED;
- goto out;
- }
- csname_len = (size_t)(p1 - csname);
- if (csname_len > sizeof(tmpbuf) - 1) {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- goto out;
- }
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- } else {
- scan_stat = 0;
- }
- break;
- } else {
- err = PHP_ICONV_ERR_MALFORMED;
- goto out;
- }
- }
- memcpy(tmpbuf, csname, csname_len);
- tmpbuf[csname_len] = '\0';
- if (cd != (iconv_t)(-1)) {
- iconv_close(cd);
- }
- cd = iconv_open(enc, tmpbuf);
- if (cd == (iconv_t)(-1)) {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- /* Bad character set, but the user wants us to
- * press on. In this case, we'll just insert the
- * undecoded encoded word, since there isn't really
- * a more sensible behaviour available; the only
- * other options are to swallow the encoded word
- * entirely or decode it with an arbitrarily chosen
- * single byte encoding, both of which seem to have
- * a higher WTF factor than leaving it undecoded.
- *
- * Given this approach, we need to skip ahead to
- * the end of the encoded word. */
- int qmarks = 2;
- while (qmarks > 0 && str_left > 1) {
- if (*(++p1) == '?') {
- --qmarks;
- }
- --str_left;
- }
- /* Look ahead to check for the terminating = that
- * should be there as well; if it's there, we'll
- * also include that. If it's not, there isn't much
- * we can do at this point. */
- if (*(p1 + 1) == '=') {
- ++p1;
- if (str_left > 1) {
- --str_left;
- }
- }
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- goto out;
- }
- /* Let's go back and see if there are further
- * encoded words or bare content, and hope they
- * might actually have a valid character set. */
- scan_stat = 12;
- break;
- } else {
- if (errno == EINVAL) {
- err = PHP_ICONV_ERR_WRONG_CHARSET;
- } else {
- err = PHP_ICONV_ERR_CONVERTER;
- }
- goto out;
- }
- }
- }
- break;
- case 3: /* expecting a encoding scheme specifier */
- switch (*p1) {
- case 'b':
- case 'B':
- enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64;
- scan_stat = 4;
- break;
- case 'q':
- case 'Q':
- enc_scheme = PHP_ICONV_ENC_SCHEME_QPRINT;
- scan_stat = 4;
- break;
- default:
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- goto out;
- }
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- } else {
- scan_stat = 0;
- }
- break;
- } else {
- err = PHP_ICONV_ERR_MALFORMED;
- goto out;
- }
- }
- break;
- case 4: /* expecting a delimiter */
- if (*p1 != '?') {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- /* pass the entire chunk through the converter */
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- goto out;
- }
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- } else {
- scan_stat = 0;
- }
- break;
- } else {
- err = PHP_ICONV_ERR_MALFORMED;
- goto out;
- }
- }
- encoded_text = p1 + 1;
- scan_stat = 5;
- break;
- case 5: /* expecting an encoded portion */
- if (*p1 == '?') {
- encoded_text_len = (size_t)(p1 - encoded_text);
- scan_stat = 6;
- }
- break;
- case 7: /* expecting a "\n" character */
- if (*p1 == '\n') {
- scan_stat = 8;
- } else {
- /* bare CR */
- _php_iconv_appendc(pretval, '\r', cd_pl);
- _php_iconv_appendc(pretval, *p1, cd_pl);
- scan_stat = 0;
- }
- break;
- case 8: /* checking whether the following line is part of a
- folded header */
- if (*p1 != ' ' && *p1 != '\t') {
- --p1;
- str_left = 1; /* quit_loop */
- break;
- }
- if (encoded_word == NULL) {
- _php_iconv_appendc(pretval, ' ', cd_pl);
- }
- spaces = NULL;
- scan_stat = 11;
- break;
- case 6: /* expecting a End-Of-Chunk character "=" */
- if (*p1 != '=') {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- /* pass the entire chunk through the converter */
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- goto out;
- }
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- } else {
- scan_stat = 0;
- }
- break;
- } else {
- err = PHP_ICONV_ERR_MALFORMED;
- goto out;
- }
- }
- scan_stat = 9;
- if (str_left == 1) {
- eos = 1;
- } else {
- break;
- }
- /* TODO might want to rearrange logic so this is more obvious */
- ZEND_FALLTHROUGH;
- case 9: /* choice point, seeing what to do next.*/
- switch (*p1) {
- default:
- /* Handle non-RFC-compliant formats
- *
- * RFC2047 requires the character that comes right
- * after an encoded word (chunk) to be a whitespace,
- * while there are lots of broken implementations that
- * generate such malformed headers that don't fulfill
- * that requirement.
- */
- if (!eos) {
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- /* pass the entire chunk through the converter */
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- goto out;
- }
- scan_stat = 12;
- break;
- }
- }
- ZEND_FALLTHROUGH;
- case '\r': case '\n': case ' ': case '\t': {
- zend_string *decoded_text;
- switch (enc_scheme) {
- case PHP_ICONV_ENC_SCHEME_BASE64:
- decoded_text = php_base64_decode((unsigned char*)encoded_text, encoded_text_len);
- break;
- case PHP_ICONV_ENC_SCHEME_QPRINT:
- decoded_text = php_quot_print_decode((unsigned char*)encoded_text, encoded_text_len, 1);
- break;
- default:
- decoded_text = NULL;
- break;
- }
- if (decoded_text == NULL) {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- /* pass the entire chunk through the converter */
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- goto out;
- }
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- } else {
- scan_stat = 0;
- }
- break;
- } else {
- err = PHP_ICONV_ERR_UNKNOWN;
- goto out;
- }
- }
- err = _php_iconv_appendl(pretval, ZSTR_VAL(decoded_text), ZSTR_LEN(decoded_text), cd);
- if (err == PHP_ICONV_ERR_SUCCESS) {
- err = _php_iconv_appendl(pretval, NULL, 0, cd);
- }
- zend_string_release_ex(decoded_text, 0);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- /* pass the entire chunk through the converter */
- err = _php_iconv_appendl(pretval, encoded_word, (size_t)(p1 - encoded_word), cd_pl);
- encoded_word = NULL;
- if (err != PHP_ICONV_ERR_SUCCESS) {
- break;
- }
- } else {
- goto out;
- }
- }
- if (eos) { /* reached end-of-string. done. */
- scan_stat = 0;
- break;
- }
- switch (*p1) {
- case '\r': /* part of an EOL sequence? */
- scan_stat = 7;
- break;
- case '\n':
- scan_stat = 8;
- break;
- case '=': /* first letter of an encoded chunk */
- scan_stat = 1;
- break;
- case ' ': case '\t': /* medial whitespaces */
- spaces = p1;
- scan_stat = 11;
- break;
- default: /* first letter of a non-encoded word */
- _php_iconv_appendc(pretval, *p1, cd_pl);
- scan_stat = 12;
- break;
- }
- } break;
- }
- break;
- case 10: /* expects a language specifier. dismiss it for now */
- if (*p1 == '?') {
- scan_stat = 3;
- }
- break;
- case 11: /* expecting a chunk of whitespaces */
- switch (*p1) {
- case '\r': /* part of an EOL sequence? */
- scan_stat = 7;
- break;
- case '\n':
- scan_stat = 8;
- break;
- case '=': /* first letter of an encoded chunk */
- if (spaces != NULL && encoded_word == NULL) {
- _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
- spaces = NULL;
- }
- encoded_word = p1;
- scan_stat = 1;
- break;
- case ' ': case '\t':
- break;
- default: /* first letter of a non-encoded word */
- if (spaces != NULL) {
- _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
- spaces = NULL;
- }
- _php_iconv_appendc(pretval, *p1, cd_pl);
- encoded_word = NULL;
- if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- scan_stat = 12;
- } else {
- scan_stat = 0;
- }
- break;
- }
- break;
- case 12: /* expecting a non-encoded word */
- switch (*p1) {
- case '\r': /* part of an EOL sequence? */
- scan_stat = 7;
- break;
- case '\n':
- scan_stat = 8;
- break;
- case ' ': case '\t':
- spaces = p1;
- scan_stat = 11;
- break;
- case '=': /* first letter of an encoded chunk */
- if (!(mode & PHP_ICONV_MIME_DECODE_STRICT)) {
- encoded_word = p1;
- scan_stat = 1;
- break;
- }
- ZEND_FALLTHROUGH;
- default:
- _php_iconv_appendc(pretval, *p1, cd_pl);
- break;
- }
- break;
- }
- }
- switch (scan_stat) {
- case 0: case 8: case 11: case 12:
- break;
- default:
- if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
- if (scan_stat == 1) {
- _php_iconv_appendc(pretval, '=', cd_pl);
- }
- err = PHP_ICONV_ERR_SUCCESS;
- } else {
- err = PHP_ICONV_ERR_MALFORMED;
- goto out;
- }
- }
- if (next_pos != NULL) {
- *next_pos = p1;
- }
- smart_str_0(pretval);
- out:
- if (cd != (iconv_t)(-1)) {
- iconv_close(cd);
- }
- if (cd_pl != (iconv_t)(-1)) {
- iconv_close(cd_pl);
- }
- return err;
- }
- /* }}} */
- /* {{{ php_iconv_show_error() */
- static void _php_iconv_show_error(php_iconv_err_t err, const char *out_charset, const char *in_charset)
- {
- switch (err) {
- case PHP_ICONV_ERR_SUCCESS:
- break;
- case PHP_ICONV_ERR_CONVERTER:
- php_error_docref(NULL, E_WARNING, "Cannot open converter");
- break;
- case PHP_ICONV_ERR_WRONG_CHARSET:
- php_error_docref(NULL, E_WARNING, "Wrong encoding, conversion from \"%s\" to \"%s\" is not allowed",
- in_charset, out_charset);
- break;
- case PHP_ICONV_ERR_ILLEGAL_CHAR:
- php_error_docref(NULL, E_NOTICE, "Detected an incomplete multibyte character in input string");
- break;
- case PHP_ICONV_ERR_ILLEGAL_SEQ:
- php_error_docref(NULL, E_NOTICE, "Detected an illegal character in input string");
- break;
- case PHP_ICONV_ERR_TOO_BIG:
- /* should not happen */
- php_error_docref(NULL, E_WARNING, "Buffer length exceeded");
- break;
- case PHP_ICONV_ERR_MALFORMED:
- php_error_docref(NULL, E_WARNING, "Malformed string");
- break;
- case PHP_ICONV_ERR_OUT_BY_BOUNDS:
- zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
- break;
- default:
- /* other error */
- php_error_docref(NULL, E_NOTICE, "Unknown error (%d)", errno);
- break;
- }
- }
- /* }}} */
- /* {{{ Returns the character count of str */
- PHP_FUNCTION(iconv_strlen)
- {
- const char *charset = NULL;
- size_t charset_len;
- zend_string *str;
- php_iconv_err_t err;
- size_t retval;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|s!",
- &str, &charset, &charset_len) == FAILURE) {
- RETURN_THROWS();
- }
- if (charset == NULL) {
- charset = get_internal_encoding();
- } else if (charset_len >= ICONV_CSNMAXLEN) {
- php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
- RETURN_FALSE;
- }
- err = _php_iconv_strlen(&retval, ZSTR_VAL(str), ZSTR_LEN(str), charset);
- _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
- if (err == PHP_ICONV_ERR_SUCCESS) {
- RETVAL_LONG(retval);
- } else {
- RETVAL_FALSE;
- }
- }
- /* }}} */
- /* {{{ Returns specified part of a string */
- PHP_FUNCTION(iconv_substr)
- {
- const char *charset = NULL;
- size_t charset_len;
- zend_string *str;
- zend_long offset, length = 0;
- bool len_is_null = 1;
- php_iconv_err_t err;
- smart_str retval = {0};
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sl|l!s!",
- &str, &offset, &length, &len_is_null,
- &charset, &charset_len) == FAILURE) {
- RETURN_THROWS();
- }
- if (charset == NULL) {
- charset = get_internal_encoding();
- } else if (charset_len >= ICONV_CSNMAXLEN) {
- php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
- RETURN_FALSE;
- }
- if (len_is_null) {
- length = ZSTR_LEN(str);
- }
- err = _php_iconv_substr(&retval, ZSTR_VAL(str), ZSTR_LEN(str), offset, length, charset);
- _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
- if (err == PHP_ICONV_ERR_SUCCESS && retval.s != NULL) {
- RETURN_NEW_STR(retval.s);
- }
- smart_str_free(&retval);
- RETURN_FALSE;
- }
- /* }}} */
- /* {{{ Finds position of first occurrence of needle within part of haystack beginning with offset */
- PHP_FUNCTION(iconv_strpos)
- {
- const char *charset = NULL;
- size_t charset_len, haystk_len;
- zend_string *haystk;
- zend_string *ndl;
- zend_long offset = 0;
- php_iconv_err_t err;
- size_t retval;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|ls!",
- &haystk, &ndl,
- &offset, &charset, &charset_len) == FAILURE) {
- RETURN_THROWS();
- }
- if (charset == NULL) {
- charset = get_internal_encoding();
- } else if (charset_len >= ICONV_CSNMAXLEN) {
- php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
- RETURN_FALSE;
- }
- if (offset < 0) {
- /* Convert negative offset (counted from the end of string) */
- err = _php_iconv_strlen(&haystk_len, ZSTR_VAL(haystk), ZSTR_LEN(haystk), charset);
- if (err != PHP_ICONV_ERR_SUCCESS) {
- _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
- RETURN_FALSE;
- }
- offset += haystk_len;
- if (offset < 0) { /* If offset before start */
- zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
- RETURN_THROWS();
- }
- }
- if (ZSTR_LEN(ndl) < 1) {
- // TODO: Support empty needles!
- RETURN_FALSE;
- }
- err = _php_iconv_strpos(
- &retval, ZSTR_VAL(haystk), ZSTR_LEN(haystk), ZSTR_VAL(ndl), ZSTR_LEN(ndl),
- offset, charset, /* reverse */ false);
- _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
- if (err == PHP_ICONV_ERR_SUCCESS && retval != (size_t)-1) {
- RETVAL_LONG((zend_long)retval);
- } else {
- RETVAL_FALSE;
- }
- }
- /* }}} */
- /* {{{ Finds position of last occurrence of needle within part of haystack beginning with offset */
- PHP_FUNCTION(iconv_strrpos)
- {
- const char *charset = NULL;
- size_t charset_len;
- zend_string *haystk;
- zend_string *ndl;
- php_iconv_err_t err;
- size_t retval;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|s!",
- &haystk, &ndl,
- &charset, &charset_len) == FAILURE) {
- RETURN_THROWS();
- }
- if (ZSTR_LEN(ndl) < 1) {
- RETURN_FALSE;
- }
- if (charset == NULL) {
- charset = get_internal_encoding();
- } else if (charset_len >= ICONV_CSNMAXLEN) {
- php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
- RETURN_FALSE;
- }
- err = _php_iconv_strpos(
- &retval, ZSTR_VAL(haystk), ZSTR_LEN(haystk), ZSTR_VAL(ndl), ZSTR_LEN(ndl),
- /* offset */ 0, charset, /* reserve */ true);
- _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset);
- if (err == PHP_ICONV_ERR_SUCCESS && retval != (size_t)-1) {
- RETVAL_LONG((zend_long)retval);
- } else {
- RETVAL_FALSE;
- }
- }
- /* }}} */
- /* {{{ Composes a mime header field with field_name and field_value in a specified scheme */
- PHP_FUNCTION(iconv_mime_encode)
- {
- zend_string *field_name = NULL;
- zend_string *field_value = NULL;
- zend_string *tmp_str = NULL;
- zval *pref = NULL;
- smart_str retval = {0};
- php_iconv_err_t err;
- const char *in_charset = get_internal_encoding();
- const char *out_charset = in_charset;
- zend_long line_len = 76;
- const char *lfchars = "\r\n";
- php_iconv_enc_scheme_t scheme_id = PHP_ICONV_ENC_SCHEME_BASE64;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|a",
- &field_name, &field_value,
- &pref) == FAILURE) {
- RETURN_THROWS();
- }
- if (pref != NULL) {
- zval *pzval;
- if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "scheme", sizeof("scheme") - 1)) != NULL) {
- if (Z_TYPE_P(pzval) == IS_STRING && Z_STRLEN_P(pzval) > 0) {
- switch (Z_STRVAL_P(pzval)[0]) {
- case 'B': case 'b':
- scheme_id = PHP_ICONV_ENC_SCHEME_BASE64;
- break;
- case 'Q': case 'q':
- scheme_id = PHP_ICONV_ENC_SCHEME_QPRINT;
- break;
- }
- }
- }
- if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "input-charset", sizeof("input-charset") - 1)) != NULL && Z_TYPE_P(pzval) == IS_STRING) {
- if (Z_STRLEN_P(pzval) >= ICONV_CSNMAXLEN) {
- php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
- RETURN_FALSE;
- }
- if (Z_STRLEN_P(pzval) > 0) {
- in_charset = Z_STRVAL_P(pzval);
- }
- }
- if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "output-charset", sizeof("output-charset") - 1)) != NULL && Z_TYPE_P(pzval) == IS_STRING) {
- if (Z_STRLEN_P(pzval) >= ICONV_CSNMAXLEN) {
- php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
- RETURN_FALSE;
- }
- if (Z_STRLEN_P(pzval) > 0) {
- out_charset = Z_STRVAL_P(pzval);
- }
- }
- if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "line-length", sizeof("line-length") - 1)) != NULL) {
- line_len = zval_get_long(pzval);
- }
- if ((pzval = zend_hash_str_find_deref(Z_ARRVAL_P(pref), "line-break-chars", sizeof("line-break-chars") - 1)) != NULL) {
- if (Z_TYPE_P(pzval) != IS_STRING) {
- tmp_str = zval_try_get_string_func(pzval);
- if (UNEXPECTED(!tmp_str)) {
- return;
- }
- lfchars = ZSTR_VAL(tmp_str);
- } else {
- lfchars = Z_STRVAL_P(pzval);
- }
- }
- }
- err = _php_iconv_mime_encode(&retval, ZSTR_VAL(field_name), ZSTR_LEN(field_name),
- ZSTR_VAL(field_value), ZSTR_LEN(field_value), line_len, lfchars, scheme_id,
- out_charset, in_charset);
- _php_iconv_show_error(err, out_charset, in_charset);
- if (err == PHP_ICONV_ERR_SUCCESS) {
- if (retval.s != NULL) {
- RETVAL_STR(retval.s);
- } else {
- RETVAL_EMPTY_STRING();
- }
- } else {
- smart_str_free(&retval);
- RETVAL_FALSE;
- }
- if (tmp_str) {
- zend_string_release_ex(tmp_str, 0);
- }
- }
- /* }}} */
- /* {{{ Decodes a mime header field */
- PHP_FUNCTION(iconv_mime_decode)
- {
- zend_string *encoded_str;
- const char *charset = NULL;
- size_t charset_len;
- zend_long mode = 0;
- smart_str retval = {0};
- php_iconv_err_t err;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ls!",
- &encoded_str, &mode, &charset, &charset_len) == FAILURE) {
- RETURN_THROWS();
- }
- if (charset == NULL) {
- charset = get_internal_encoding();
- } else if (charset_len >= ICONV_CSNMAXLEN) {
- php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
- RETURN_FALSE;
- }
- err = _php_iconv_mime_decode(&retval, ZSTR_VAL(encoded_str), ZSTR_LEN(encoded_str), charset, NULL, (int)mode);
- _php_iconv_show_error(err, charset, "???");
- if (err == PHP_ICONV_ERR_SUCCESS) {
- if (retval.s != NULL) {
- RETVAL_STR(retval.s);
- } else {
- RETVAL_EMPTY_STRING();
- }
- } else {
- smart_str_free(&retval);
- RETVAL_FALSE;
- }
- }
- /* }}} */
- /* {{{ Decodes multiple mime header fields */
- PHP_FUNCTION(iconv_mime_decode_headers)
- {
- zend_string *encoded_str;
- const char *charset = NULL;
- size_t charset_len;
- zend_long mode = 0;
- char *enc_str_tmp;
- size_t enc_str_len_tmp;
- php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ls!",
- &encoded_str, &mode, &charset, &charset_len) == FAILURE) {
- RETURN_THROWS();
- }
- if (charset == NULL) {
- charset = get_internal_encoding();
- } else if (charset_len >= ICONV_CSNMAXLEN) {
- php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
- RETURN_FALSE;
- }
- array_init(return_value);
- enc_str_tmp = ZSTR_VAL(encoded_str);
- enc_str_len_tmp = ZSTR_LEN(encoded_str);
- while (enc_str_len_tmp > 0) {
- smart_str decoded_header = {0};
- char *header_name = NULL;
- size_t header_name_len = 0;
- char *header_value = NULL;
- size_t header_value_len = 0;
- char *p, *limit;
- const char *next_pos;
- if (PHP_ICONV_ERR_SUCCESS != (err = _php_iconv_mime_decode(&decoded_header, enc_str_tmp, enc_str_len_tmp, charset, &next_pos, (int)mode))) {
- smart_str_free(&decoded_header);
- break;
- }
- if (decoded_header.s == NULL) {
- break;
- }
- limit = ZSTR_VAL(decoded_header.s) + ZSTR_LEN(decoded_header.s);
- for (p = ZSTR_VAL(decoded_header.s); p < limit; p++) {
- if (*p == ':') {
- *p = '\0';
- header_name = ZSTR_VAL(decoded_header.s);
- header_name_len = p - ZSTR_VAL(decoded_header.s);
- while (++p < limit) {
- if (*p != ' ' && *p != '\t') {
- break;
- }
- }
- header_value = p;
- header_value_len = limit - p;
- break;
- }
- }
- if (header_name != NULL) {
- zval *elem;
- if ((elem = zend_hash_str_find(Z_ARRVAL_P(return_value), header_name, header_name_len)) != NULL) {
- if (Z_TYPE_P(elem) != IS_ARRAY) {
- zval new_elem;
- array_init(&new_elem);
- Z_ADDREF_P(elem);
- add_next_index_zval(&new_elem, elem);
- elem = zend_hash_str_update(Z_ARRVAL_P(return_value), header_name, header_name_len, &new_elem);
- }
- add_next_index_stringl(elem, header_value, header_value_len);
- } else {
- add_assoc_stringl_ex(return_value, header_name, header_name_len, header_value, header_value_len);
- }
- }
- enc_str_len_tmp -= next_pos - enc_str_tmp;
- enc_str_tmp = (char *)next_pos;
- smart_str_free(&decoded_header);
- }
- if (err != PHP_ICONV_ERR_SUCCESS) {
- _php_iconv_show_error(err, charset, "???");
- zend_array_destroy(Z_ARR_P(return_value));
- RETVAL_FALSE;
- }
- }
- /* }}} */
- /* {{{ Returns str converted to the out_charset character set */
- PHP_FUNCTION(iconv)
- {
- char *in_charset, *out_charset;
- zend_string *in_buffer;
- size_t in_charset_len = 0, out_charset_len = 0;
- php_iconv_err_t err;
- zend_string *out_buffer;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssS",
- &in_charset, &in_charset_len, &out_charset, &out_charset_len, &in_buffer) == FAILURE) {
- RETURN_THROWS();
- }
- if (in_charset_len >= ICONV_CSNMAXLEN || out_charset_len >= ICONV_CSNMAXLEN) {
- php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
- RETURN_FALSE;
- }
- err = php_iconv_string(ZSTR_VAL(in_buffer), (size_t)ZSTR_LEN(in_buffer), &out_buffer, out_charset, in_charset);
- _php_iconv_show_error(err, out_charset, in_charset);
- if (err == PHP_ICONV_ERR_SUCCESS && out_buffer != NULL) {
- RETVAL_NEW_STR(out_buffer);
- } else {
- if (out_buffer != NULL) {
- zend_string_efree(out_buffer);
- }
- RETURN_FALSE;
- }
- }
- /* }}} */
- /* {{{ Sets internal encoding and output encoding for ob_iconv_handler() */
- PHP_FUNCTION(iconv_set_encoding)
- {
- zend_string *type;
- zend_string *charset;
- zend_result retval;
- zend_string *name;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &type, &charset) == FAILURE) {
- RETURN_THROWS();
- }
- if (ZSTR_LEN(charset) >= ICONV_CSNMAXLEN) {
- php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
- RETURN_FALSE;
- }
- if(zend_string_equals_literal_ci(type, "input_encoding")) {
- name = zend_string_init("iconv.input_encoding", sizeof("iconv.input_encoding") - 1, 0);
- } else if(zend_string_equals_literal_ci(type, "output_encoding")) {
- name = zend_string_init("iconv.output_encoding", sizeof("iconv.output_encoding") - 1, 0);
- } else if(zend_string_equals_literal_ci(type, "internal_encoding")) {
- name = zend_string_init("iconv.internal_encoding", sizeof("iconv.internal_encoding") - 1, 0);
- } else {
- RETURN_FALSE;
- }
- retval = zend_alter_ini_entry(name, charset, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
- zend_string_release_ex(name, 0);
- if (retval == SUCCESS) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
- }
- /* }}} */
- /* {{{ Get internal encoding and output encoding for ob_iconv_handler() */
- PHP_FUNCTION(iconv_get_encoding)
- {
- zend_string *type = NULL;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &type) == FAILURE) {
- RETURN_THROWS();
- }
- if (!type || zend_string_equals_literal_ci(type, "all")) {
- array_init(return_value);
- add_assoc_string(return_value, "input_encoding", get_input_encoding());
- add_assoc_string(return_value, "output_encoding", get_output_encoding());
- add_assoc_string(return_value, "internal_encoding", get_internal_encoding());
- } else if (zend_string_equals_literal_ci(type, "input_encoding")) {
- RETVAL_STRING(get_input_encoding());
- } else if (zend_string_equals_literal_ci(type, "output_encoding")) {
- RETVAL_STRING(get_output_encoding());
- } else if (zend_string_equals_literal_ci(type, "internal_encoding")) {
- RETVAL_STRING(get_internal_encoding());
- } else {
- /* TODO Warning/ValueError? */
- RETURN_FALSE;
- }
- }
- /* }}} */
- /* {{{ iconv stream filter */
- typedef struct _php_iconv_stream_filter {
- iconv_t cd;
- int persistent;
- char *to_charset;
- size_t to_charset_len;
- char *from_charset;
- size_t from_charset_len;
- char stub[128];
- size_t stub_len;
- } php_iconv_stream_filter;
- /* }}} iconv stream filter */
- /* {{{ php_iconv_stream_filter_dtor */
- static void php_iconv_stream_filter_dtor(php_iconv_stream_filter *self)
- {
- iconv_close(self->cd);
- pefree(self->to_charset, self->persistent);
- pefree(self->from_charset, self->persistent);
- }
- /* }}} */
- /* {{{ php_iconv_stream_filter_ctor() */
- static php_iconv_err_t php_iconv_stream_filter_ctor(php_iconv_stream_filter *self,
- const char *to_charset, size_t to_charset_len,
- const char *from_charset, size_t from_charset_len, int persistent)
- {
- self->to_charset = pemalloc(to_charset_len + 1, persistent);
- self->to_charset_len = to_charset_len;
- self->from_charset = pemalloc(from_charset_len + 1, persistent);
- self->from_charset_len = from_charset_len;
- memcpy(self->to_charset, to_charset, to_charset_len);
- self->to_charset[to_charset_len] = '\0';
- memcpy(self->from_charset, from_charset, from_charset_len);
- self->from_charset[from_charset_len] = '\0';
- if ((iconv_t)-1 == (self->cd = iconv_open(self->to_charset, self->from_charset))) {
- pefree(self->from_charset, persistent);
- pefree(self->to_charset, persistent);
- return PHP_ICONV_ERR_UNKNOWN;
- }
- self->persistent = persistent;
- self->stub_len = 0;
- return PHP_ICONV_ERR_SUCCESS;
- }
- /* }}} */
- /* {{{ php_iconv_stream_filter_append_bucket */
- static int php_iconv_stream_filter_append_bucket(
- php_iconv_stream_filter *self,
- php_stream *stream, php_stream_filter *filter,
- php_stream_bucket_brigade *buckets_out,
- const char *ps, size_t buf_len, size_t *consumed,
- int persistent)
- {
- php_stream_bucket *new_bucket;
- char *out_buf = NULL;
- size_t out_buf_size;
- char *pd, *pt;
- size_t ocnt, prev_ocnt, icnt, tcnt;
- size_t initial_out_buf_size;
- if (ps == NULL) {
- initial_out_buf_size = 64;
- icnt = 1;
- } else {
- initial_out_buf_size = buf_len;
- icnt = buf_len;
- }
- out_buf_size = ocnt = prev_ocnt = initial_out_buf_size;
- out_buf = pemalloc(out_buf_size, persistent);
- pd = out_buf;
- if (self->stub_len > 0) {
- pt = self->stub;
- tcnt = self->stub_len;
- while (tcnt > 0) {
- if (iconv(self->cd, &pt, &tcnt, &pd, &ocnt) == (size_t)-1) {
- switch (errno) {
- case EILSEQ:
- php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
- goto out_failure;
- case EINVAL:
- if (ps != NULL) {
- if (icnt > 0) {
- if (self->stub_len >= sizeof(self->stub)) {
- php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
- goto out_failure;
- }
- self->stub[self->stub_len++] = *(ps++);
- icnt--;
- pt = self->stub;
- tcnt = self->stub_len;
- } else {
- tcnt = 0;
- break;
- }
- } else {
- php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
- goto out_failure;
- }
- break;
- case E2BIG: {
- char *new_out_buf;
- size_t new_out_buf_size;
- new_out_buf_size = out_buf_size << 1;
- if (new_out_buf_size < out_buf_size) {
- /* whoa! no bigger buckets are sold anywhere... */
- if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
- goto out_failure;
- }
- php_stream_bucket_append(buckets_out, new_bucket);
- out_buf_size = ocnt = initial_out_buf_size;
- out_buf = pemalloc(out_buf_size, persistent);
- pd = out_buf;
- } else {
- new_out_buf = perealloc(out_buf, new_out_buf_size, persistent);
- pd = new_out_buf + (pd - out_buf);
- ocnt += (new_out_buf_size - out_buf_size);
- out_buf = new_out_buf;
- out_buf_size = new_out_buf_size;
- }
- } break;
- default:
- php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
- goto out_failure;
- }
- }
- prev_ocnt = ocnt;
- }
- memmove(self->stub, pt, tcnt);
- self->stub_len = tcnt;
- }
- while (icnt > 0) {
- if ((ps == NULL ? iconv(self->cd, NULL, NULL, &pd, &ocnt):
- iconv(self->cd, (char **)&ps, &icnt, &pd, &ocnt)) == (size_t)-1) {
- switch (errno) {
- case EILSEQ:
- php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
- goto out_failure;
- case EINVAL:
- if (ps != NULL) {
- if (icnt > sizeof(self->stub)) {
- php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
- goto out_failure;
- }
- memcpy(self->stub, ps, icnt);
- self->stub_len = icnt;
- ps += icnt;
- icnt = 0;
- } else {
- php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unexpected octet values", self->from_charset, self->to_charset);
- goto out_failure;
- }
- break;
- case E2BIG: {
- char *new_out_buf;
- size_t new_out_buf_size;
- new_out_buf_size = out_buf_size << 1;
- if (new_out_buf_size < out_buf_size) {
- /* whoa! no bigger buckets are sold anywhere... */
- if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
- goto out_failure;
- }
- php_stream_bucket_append(buckets_out, new_bucket);
- out_buf_size = ocnt = initial_out_buf_size;
- out_buf = pemalloc(out_buf_size, persistent);
- pd = out_buf;
- } else {
- new_out_buf = perealloc(out_buf, new_out_buf_size, persistent);
- pd = new_out_buf + (pd - out_buf);
- ocnt += (new_out_buf_size - out_buf_size);
- out_buf = new_out_buf;
- out_buf_size = new_out_buf_size;
- }
- } break;
- default:
- php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
- goto out_failure;
- }
- } else {
- if (ps == NULL) {
- break;
- }
- }
- prev_ocnt = ocnt;
- }
- if (out_buf_size > ocnt) {
- if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
- goto out_failure;
- }
- php_stream_bucket_append(buckets_out, new_bucket);
- } else {
- pefree(out_buf, persistent);
- }
- *consumed += buf_len - icnt;
- return SUCCESS;
- out_failure:
- pefree(out_buf, persistent);
- return FAILURE;
- }
- /* }}} php_iconv_stream_filter_append_bucket */
- /* {{{ php_iconv_stream_filter_do_filter */
- static php_stream_filter_status_t php_iconv_stream_filter_do_filter(
- php_stream *stream, php_stream_filter *filter,
- php_stream_bucket_brigade *buckets_in,
- php_stream_bucket_brigade *buckets_out,
- size_t *bytes_consumed, int flags)
- {
- php_stream_bucket *bucket = NULL;
- size_t consumed = 0;
- php_iconv_stream_filter *self = (php_iconv_stream_filter *)Z_PTR(filter->abstract);
- while (buckets_in->head != NULL) {
- bucket = buckets_in->head;
- php_stream_bucket_unlink(bucket);
- if (php_iconv_stream_filter_append_bucket(self, stream, filter,
- buckets_out, bucket->buf, bucket->buflen, &consumed,
- php_stream_is_persistent(stream)) != SUCCESS) {
- goto out_failure;
- }
- php_stream_bucket_delref(bucket);
- }
- if (flags != PSFS_FLAG_NORMAL) {
- if (php_iconv_stream_filter_append_bucket(self, stream, filter,
- buckets_out, NULL, 0, &consumed,
- php_stream_is_persistent(stream)) != SUCCESS) {
- goto out_failure;
- }
- }
- if (bytes_consumed != NULL) {
- *bytes_consumed = consumed;
- }
- return PSFS_PASS_ON;
- out_failure:
- if (bucket != NULL) {
- php_stream_bucket_delref(bucket);
- }
- return PSFS_ERR_FATAL;
- }
- /* }}} */
- /* {{{ php_iconv_stream_filter_cleanup */
- static void php_iconv_stream_filter_cleanup(php_stream_filter *filter)
- {
- php_iconv_stream_filter_dtor((php_iconv_stream_filter *)Z_PTR(filter->abstract));
- pefree(Z_PTR(filter->abstract), ((php_iconv_stream_filter *)Z_PTR(filter->abstract))->persistent);
- }
- /* }}} */
- static const php_stream_filter_ops php_iconv_stream_filter_ops = {
- php_iconv_stream_filter_do_filter,
- php_iconv_stream_filter_cleanup,
- "convert.iconv.*"
- };
- /* {{{ php_iconv_stream_filter_create */
- static php_stream_filter *php_iconv_stream_filter_factory_create(const char *name, zval *params, uint8_t persistent)
- {
- php_stream_filter *retval = NULL;
- php_iconv_stream_filter *inst;
- char *from_charset = NULL, *to_charset = NULL;
- size_t from_charset_len, to_charset_len;
- if ((from_charset = strchr(name, '.')) == NULL) {
- return NULL;
- }
- ++from_charset;
- if ((from_charset = strchr(from_charset, '.')) == NULL) {
- return NULL;
- }
- ++from_charset;
- if ((to_charset = strpbrk(from_charset, "/.")) == NULL) {
- return NULL;
- }
- from_charset_len = to_charset - from_charset;
- ++to_charset;
- to_charset_len = strlen(to_charset);
- if (from_charset_len >= ICONV_CSNMAXLEN || to_charset_len >= ICONV_CSNMAXLEN) {
- return NULL;
- }
- inst = pemalloc(sizeof(php_iconv_stream_filter), persistent);
- if (php_iconv_stream_filter_ctor(inst, to_charset, to_charset_len, from_charset, from_charset_len, persistent) != PHP_ICONV_ERR_SUCCESS) {
- pefree(inst, persistent);
- return NULL;
- }
- if (NULL == (retval = php_stream_filter_alloc(&php_iconv_stream_filter_ops, inst, persistent))) {
- php_iconv_stream_filter_dtor(inst);
- pefree(inst, persistent);
- }
- return retval;
- }
- /* }}} */
- /* {{{ php_iconv_stream_register_factory */
- static php_iconv_err_t php_iconv_stream_filter_register_factory(void)
- {
- static const php_stream_filter_factory filter_factory = {
- php_iconv_stream_filter_factory_create
- };
- if (FAILURE == php_stream_filter_register_factory(
- php_iconv_stream_filter_ops.label,
- &filter_factory)) {
- return PHP_ICONV_ERR_UNKNOWN;
- }
- return PHP_ICONV_ERR_SUCCESS;
- }
- /* }}} */
- /* {{{ php_iconv_stream_unregister_factory */
- static php_iconv_err_t php_iconv_stream_filter_unregister_factory(void)
- {
- if (FAILURE == php_stream_filter_unregister_factory(
- php_iconv_stream_filter_ops.label)) {
- return PHP_ICONV_ERR_UNKNOWN;
- }
- return PHP_ICONV_ERR_SUCCESS;
- }
- /* }}} */
- /* }}} */
- #endif
|