#include "unicode/utypes.h"
#if !UCONFIG_NO_IDNA
#include "unicode/uidna.h"
#include "unicode/ustring.h"
#include "unicode/usprep.h"
#include "punycode.h"
#include "ustr_imp.h"
#include "cmemory.h"
#include "uassert.h"
#include "sprpimpl.h"
static const char16_t ACE_PREFIX[] ={ 0x0078,0x006E,0x002d,0x002d } ;
#define ACE_PREFIX_LENGTH …
#define MAX_LABEL_LENGTH …
#define MAX_LABEL_BUFFER_SIZE …
#define MAX_DOMAIN_NAME_LENGTH …
#define MAX_IDN_BUFFER_SIZE …
#define LOWER_CASE_DELTA …
#define HYPHEN …
#define FULL_STOP …
#define CAPITAL_A …
#define CAPITAL_Z …
inline static char16_t
toASCIILower(char16_t ch){
if(CAPITAL_A <= ch && ch <= CAPITAL_Z){
return ch + LOWER_CASE_DELTA;
}
return ch;
}
inline static UBool
startsWithPrefix(const char16_t* src , int32_t srcLength){
if(srcLength < ACE_PREFIX_LENGTH){
return false;
}
for(int8_t i=0; i< ACE_PREFIX_LENGTH; i++){
if(toASCIILower(src[i]) != ACE_PREFIX[i]){
return false;
}
}
return true;
}
inline static int32_t
compareCaseInsensitiveASCII(const char16_t* s1, int32_t s1Len,
const char16_t* s2, int32_t s2Len){
int32_t minLength;
int32_t lengthResult;
if(s1Len != s2Len) {
if(s1Len < s2Len) {
minLength = s1Len;
lengthResult = -1;
} else {
minLength = s2Len;
lengthResult = 1;
}
} else {
minLength = s1Len;
lengthResult = 0;
}
char16_t c1,c2;
int32_t rc;
for(int32_t i =0;;i++) {
if(i == minLength) {
return lengthResult;
}
c1 = s1[i];
c2 = s2[i];
if(c1!=c2) {
rc = static_cast<int32_t>(toASCIILower(c1)) - static_cast<int32_t>(toASCIILower(c2));
if(rc!=0) {
lengthResult=rc;
break;
}
}
}
return lengthResult;
}
static inline UBool isLabelSeparator(char16_t ch){
switch(ch){
case 0x002e:
case 0x3002:
case 0xFF0E:
case 0xFF61:
return true;
default:
return false;
}
}
static inline int32_t
getNextSeparator(char16_t *src, int32_t srcLength,
char16_t **limit, UBool *done){
if(srcLength == -1){
int32_t i;
for(i=0 ; ;i++){
if(src[i] == 0){
*limit = src + i;
*done = true;
return i;
}
if(isLabelSeparator(src[i])){
*limit = src + (i+1);
return i;
}
}
}else{
int32_t i;
for(i=0;i<srcLength;i++){
if(isLabelSeparator(src[i])){
*limit = src + (i+1);
return i;
}
}
*limit = src+srcLength;
*done = true;
return i;
}
}
static inline UBool isLDHChar(char16_t ch){
if(ch>0x007A){
return false;
}
if( (ch==0x002D) ||
(0x0030 <= ch && ch <= 0x0039) ||
(0x0041 <= ch && ch <= 0x005A) ||
(0x0061 <= ch && ch <= 0x007A)
){
return true;
}
return false;
}
static int32_t
_internal_toASCII(const char16_t* src, int32_t srcLength,
char16_t* dest, int32_t destCapacity,
int32_t options,
UStringPrepProfile* nameprep,
UParseError* parseError,
UErrorCode* status)
{
char16_t b1Stack[MAX_LABEL_BUFFER_SIZE], b2Stack[MAX_LABEL_BUFFER_SIZE];
char16_t *b1 = b1Stack, *b2 = b2Stack;
int32_t b1Len=0, b2Len,
b1Capacity = MAX_LABEL_BUFFER_SIZE,
b2Capacity = MAX_LABEL_BUFFER_SIZE ,
reqLength=0;
int32_t namePrepOptions = ((options & UIDNA_ALLOW_UNASSIGNED) != 0) ? USPREP_ALLOW_UNASSIGNED: 0;
UBool* caseFlags = nullptr;
UBool srcIsASCII = true;
UBool srcIsLDH = true;
int32_t j=0;
UBool useSTD3ASCIIRules = static_cast<UBool>((options & UIDNA_USE_STD3_RULES) != 0);
int32_t failPos = -1;
if(srcLength == -1){
srcLength = u_strlen(src);
}
if(srcLength > b1Capacity){
b1 = static_cast<char16_t*>(uprv_malloc(srcLength * U_SIZEOF_UCHAR));
if(b1==nullptr){
*status = U_MEMORY_ALLOCATION_ERROR;
goto CLEANUP;
}
b1Capacity = srcLength;
}
for( j=0;j<srcLength;j++){
if(src[j] > 0x7F){
srcIsASCII = false;
}
b1[b1Len++] = src[j];
}
if(srcIsASCII == false){
b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Capacity, namePrepOptions, parseError, status);
if(*status == U_BUFFER_OVERFLOW_ERROR){
if(b1 != b1Stack){
uprv_free(b1);
}
b1 = static_cast<char16_t*>(uprv_malloc(b1Len * U_SIZEOF_UCHAR));
if(b1==nullptr){
*status = U_MEMORY_ALLOCATION_ERROR;
goto CLEANUP;
}
*status = U_ZERO_ERROR;
b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Len, namePrepOptions, parseError, status);
}
}
if(U_FAILURE(*status)){
goto CLEANUP;
}
if(b1Len == 0){
*status = U_IDNA_ZERO_LENGTH_LABEL_ERROR;
goto CLEANUP;
}
srcIsASCII = true;
for( j=0;j<b1Len;j++){
if(b1[j] > 0x7F){
srcIsASCII = false;
}else if(isLDHChar(b1[j])==false){
srcIsLDH = false;
failPos = j;
}
}
if(useSTD3ASCIIRules){
if( srcIsLDH == false
|| b1[0] == HYPHEN || b1[b1Len-1] == HYPHEN){
*status = U_IDNA_STD3_ASCII_RULES_ERROR;
if(srcIsLDH==false){
uprv_syntaxError(b1,failPos, b1Len,parseError);
}else if(b1[0] == HYPHEN){
uprv_syntaxError(b1,0,b1Len,parseError);
}else{
uprv_syntaxError(b1, (b1Len>0) ? b1Len-1 : b1Len, b1Len,parseError);
}
goto CLEANUP;
}
}
if(srcIsASCII){
if(b1Len <= destCapacity){
u_memmove(dest, b1, b1Len);
reqLength = b1Len;
}else{
reqLength = b1Len;
goto CLEANUP;
}
}else{
if(!startsWithPrefix(b1,b1Len)){
b2Len = u_strToPunycode(b1,b1Len,b2,b2Capacity,caseFlags, status);
if(*status == U_BUFFER_OVERFLOW_ERROR){
b2 = static_cast<char16_t*>(uprv_malloc(b2Len * U_SIZEOF_UCHAR));
if(b2 == nullptr){
*status = U_MEMORY_ALLOCATION_ERROR;
goto CLEANUP;
}
*status = U_ZERO_ERROR;
b2Len = u_strToPunycode(b1,b1Len,b2,b2Len,caseFlags, status);
}
if(U_FAILURE(*status)){
goto CLEANUP;
}
reqLength = b2Len+ACE_PREFIX_LENGTH;
if(reqLength > destCapacity){
*status = U_BUFFER_OVERFLOW_ERROR;
goto CLEANUP;
}
u_memcpy(dest, ACE_PREFIX, ACE_PREFIX_LENGTH);
u_memcpy(dest+ACE_PREFIX_LENGTH, b2, b2Len);
}else{
*status = U_IDNA_ACE_PREFIX_ERROR;
uprv_syntaxError(b1,0,b1Len,parseError);
goto CLEANUP;
}
}
if(reqLength > MAX_LABEL_LENGTH){
*status = U_IDNA_LABEL_TOO_LONG_ERROR;
}
CLEANUP:
if(b1 != b1Stack){
uprv_free(b1);
}
if(b2 != b2Stack){
uprv_free(b2);
}
uprv_free(caseFlags);
return u_terminateUChars(dest, destCapacity, reqLength, status);
}
static int32_t
_internal_toUnicode(const char16_t* src, int32_t srcLength,
char16_t* dest, int32_t destCapacity,
int32_t options,
UStringPrepProfile* nameprep,
UParseError* parseError,
UErrorCode* status)
{
int32_t namePrepOptions = ((options & UIDNA_ALLOW_UNASSIGNED) != 0) ? USPREP_ALLOW_UNASSIGNED: 0;
char16_t b1Stack[MAX_LABEL_BUFFER_SIZE], b2Stack[MAX_LABEL_BUFFER_SIZE], b3Stack[MAX_LABEL_BUFFER_SIZE];
char16_t *b1 = b1Stack, *b2 = b2Stack, *b1Prime=nullptr, *b3=b3Stack;
int32_t b1Len = 0, b2Len, b1PrimeLen, b3Len,
b1Capacity = MAX_LABEL_BUFFER_SIZE,
b2Capacity = MAX_LABEL_BUFFER_SIZE,
b3Capacity = MAX_LABEL_BUFFER_SIZE,
reqLength=0;
UBool* caseFlags = nullptr;
UBool srcIsASCII = true;
if(srcLength==-1){
srcLength = 0;
for(;src[srcLength]!=0;){
if(src[srcLength]> 0x7f){
srcIsASCII = false;
}
srcLength++;
}
}else if(srcLength > 0){
for(int32_t j=0; j<srcLength; j++){
if(src[j]> 0x7f){
srcIsASCII = false;
break;
}
}
}else{
return 0;
}
if(srcIsASCII == false){
b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Capacity, namePrepOptions, parseError, status);
if(*status == U_BUFFER_OVERFLOW_ERROR){
b1 = static_cast<char16_t*>(uprv_malloc(b1Len * U_SIZEOF_UCHAR));
if(b1==nullptr){
*status = U_MEMORY_ALLOCATION_ERROR;
goto CLEANUP;
}
*status = U_ZERO_ERROR;
b1Len = usprep_prepare(nameprep, src, srcLength, b1, b1Len, namePrepOptions, parseError, status);
}
if(U_FAILURE(*status)){
goto CLEANUP;
}
}else{
b1 = const_cast<char16_t*>(src);
b1Len = srcLength;
}
if(startsWithPrefix(b1,b1Len)){
b1Prime = b1 + ACE_PREFIX_LENGTH;
b1PrimeLen = b1Len - ACE_PREFIX_LENGTH;
b2Len = u_strFromPunycode(b1Prime, b1PrimeLen, b2, b2Capacity, caseFlags,status);
if(*status == U_BUFFER_OVERFLOW_ERROR){
b2 = static_cast<char16_t*>(uprv_malloc(b2Len * U_SIZEOF_UCHAR));
if(b2==nullptr){
*status = U_MEMORY_ALLOCATION_ERROR;
goto CLEANUP;
}
*status = U_ZERO_ERROR;
b2Len = u_strFromPunycode(b1Prime, b1PrimeLen, b2, b2Len, caseFlags, status);
}
b3Len = uidna_toASCII(b2, b2Len, b3, b3Capacity, options, parseError, status);
if(*status == U_BUFFER_OVERFLOW_ERROR){
b3 = static_cast<char16_t*>(uprv_malloc(b3Len * U_SIZEOF_UCHAR));
if(b3==nullptr){
*status = U_MEMORY_ALLOCATION_ERROR;
goto CLEANUP;
}
*status = U_ZERO_ERROR;
b3Len = uidna_toASCII(b2,b2Len,b3,b3Len,options,parseError, status);
}
if(U_FAILURE(*status)){
goto CLEANUP;
}
if(compareCaseInsensitiveASCII(b1, b1Len, b3, b3Len) !=0){
*status = U_IDNA_VERIFICATION_ERROR;
goto CLEANUP;
}
reqLength = b2Len;
if(b2Len <= destCapacity) {
u_memmove(dest, b2, b2Len);
}
}
else{
if(srcLength <= destCapacity){
u_memmove(dest, src, srcLength);
}
reqLength = srcLength;
}
CLEANUP:
if(b1 != b1Stack && b1!=src){
uprv_free(b1);
}
if(b2 != b2Stack){
uprv_free(b2);
}
if(b3 != b3Stack){
uprv_free(b3);
}
uprv_free(caseFlags);
if(U_FAILURE(*status)){
if(dest && srcLength <= destCapacity){
U_ASSERT(srcLength >= 0);
u_memmove(dest, src, srcLength);
}
reqLength = srcLength;
*status = U_ZERO_ERROR;
}
return u_terminateUChars(dest, destCapacity, reqLength, status);
}
U_CAPI int32_t U_EXPORT2
uidna_toASCII(const char16_t* src, int32_t srcLength,
char16_t* dest, int32_t destCapacity,
int32_t options,
UParseError* parseError,
UErrorCode* status){
if(status == nullptr || U_FAILURE(*status)){
return 0;
}
if((src==nullptr) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){
*status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status);
if(U_FAILURE(*status)){
return -1;
}
int32_t retLen = _internal_toASCII(src, srcLength, dest, destCapacity, options, nameprep, parseError, status);
usprep_close(nameprep);
return retLen;
}
U_CAPI int32_t U_EXPORT2
uidna_toUnicode(const char16_t* src, int32_t srcLength,
char16_t* dest, int32_t destCapacity,
int32_t options,
UParseError* parseError,
UErrorCode* status){
if(status == nullptr || U_FAILURE(*status)){
return 0;
}
if( (src==nullptr) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){
*status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status);
if(U_FAILURE(*status)){
return -1;
}
int32_t retLen = _internal_toUnicode(src, srcLength, dest, destCapacity, options, nameprep, parseError, status);
usprep_close(nameprep);
return retLen;
}
U_CAPI int32_t U_EXPORT2
uidna_IDNToASCII( const char16_t *src, int32_t srcLength,
char16_t* dest, int32_t destCapacity,
int32_t options,
UParseError *parseError,
UErrorCode *status){
if(status == nullptr || U_FAILURE(*status)){
return 0;
}
if((src==nullptr) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){
*status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
int32_t reqLength = 0;
UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status);
if(U_FAILURE(*status)){
return 0;
}
char16_t* delimiter = const_cast<char16_t*>(src);
char16_t* labelStart = const_cast<char16_t*>(src);
char16_t* currentDest = dest;
int32_t remainingLen = srcLength;
int32_t remainingDestCapacity = destCapacity;
int32_t labelLen = 0, labelReqLength = 0;
UBool done = false;
for(;;){
labelLen = getNextSeparator(labelStart,remainingLen, &delimiter,&done);
labelReqLength = 0;
if(!(labelLen==0 && done)){
labelReqLength = _internal_toASCII( labelStart, labelLen,
currentDest, remainingDestCapacity,
options, nameprep,
parseError, status);
if(*status == U_BUFFER_OVERFLOW_ERROR){
*status = U_ZERO_ERROR;
remainingDestCapacity = 0;
}
}
if(U_FAILURE(*status)){
break;
}
reqLength +=labelReqLength;
if(labelReqLength < remainingDestCapacity){
currentDest = currentDest + labelReqLength;
remainingDestCapacity -= labelReqLength;
}else{
remainingDestCapacity = 0;
}
if(done){
break;
}
if(remainingDestCapacity > 0){
*currentDest++ = FULL_STOP;
remainingDestCapacity--;
}
reqLength++;
labelStart = delimiter;
if(remainingLen >0 ){
remainingLen = (int32_t)(srcLength - (delimiter - src));
}
}
if(reqLength > MAX_DOMAIN_NAME_LENGTH){
*status = U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR;
}
usprep_close(nameprep);
return u_terminateUChars(dest, destCapacity, reqLength, status);
}
U_CAPI int32_t U_EXPORT2
uidna_IDNToUnicode( const char16_t* src, int32_t srcLength,
char16_t* dest, int32_t destCapacity,
int32_t options,
UParseError* parseError,
UErrorCode* status){
if(status == nullptr || U_FAILURE(*status)){
return 0;
}
if((src==nullptr) || (srcLength < -1) || (destCapacity<0) || (!dest && destCapacity > 0)){
*status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
int32_t reqLength = 0;
UStringPrepProfile* nameprep = usprep_openByType(USPREP_RFC3491_NAMEPREP, status);
if(U_FAILURE(*status)){
return 0;
}
char16_t* delimiter = const_cast<char16_t*>(src);
char16_t* labelStart = const_cast<char16_t*>(src);
char16_t* currentDest = dest;
int32_t remainingLen = srcLength;
int32_t remainingDestCapacity = destCapacity;
int32_t labelLen = 0, labelReqLength = 0;
UBool done = false;
for(;;){
labelLen = getNextSeparator(labelStart,remainingLen, &delimiter,&done);
labelReqLength = _internal_toUnicode(labelStart, labelLen,
currentDest, remainingDestCapacity,
options, nameprep,
parseError, status);
if(*status == U_BUFFER_OVERFLOW_ERROR){
*status = U_ZERO_ERROR;
remainingDestCapacity = 0;
}
if(U_FAILURE(*status)){
break;
}
reqLength +=labelReqLength;
if(labelReqLength < remainingDestCapacity){
currentDest = currentDest + labelReqLength;
remainingDestCapacity -= labelReqLength;
}else{
remainingDestCapacity = 0;
}
if(done){
break;
}
if(remainingDestCapacity > 0){
*currentDest++ = *(labelStart + labelLen);
remainingDestCapacity--;
}
reqLength++;
labelStart = delimiter;
if(remainingLen >0 ){
remainingLen = (int32_t)(srcLength - (delimiter - src));
}
}
if(reqLength > MAX_DOMAIN_NAME_LENGTH){
*status = U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR;
}
usprep_close(nameprep);
return u_terminateUChars(dest, destCapacity, reqLength, status);
}
U_CAPI int32_t U_EXPORT2
uidna_compare( const char16_t *s1, int32_t length1,
const char16_t *s2, int32_t length2,
int32_t options,
UErrorCode* status){
if(status == nullptr || U_FAILURE(*status)){
return -1;
}
char16_t b1Stack[MAX_IDN_BUFFER_SIZE], b2Stack[MAX_IDN_BUFFER_SIZE];
char16_t *b1 = b1Stack, *b2 = b2Stack;
int32_t b1Len, b2Len, b1Capacity = MAX_IDN_BUFFER_SIZE, b2Capacity = MAX_IDN_BUFFER_SIZE;
int32_t result=-1;
UParseError parseError;
b1Len = uidna_IDNToASCII(s1, length1, b1, b1Capacity, options, &parseError, status);
if(*status == U_BUFFER_OVERFLOW_ERROR){
b1 = (char16_t*) uprv_malloc(b1Len * U_SIZEOF_UCHAR);
if(b1==nullptr){
*status = U_MEMORY_ALLOCATION_ERROR;
goto CLEANUP;
}
*status = U_ZERO_ERROR;
b1Len = uidna_IDNToASCII(s1,length1,b1,b1Len, options, &parseError, status);
}
b2Len = uidna_IDNToASCII(s2,length2, b2,b2Capacity, options, &parseError, status);
if(*status == U_BUFFER_OVERFLOW_ERROR){
b2 = (char16_t*) uprv_malloc(b2Len * U_SIZEOF_UCHAR);
if(b2==nullptr){
*status = U_MEMORY_ALLOCATION_ERROR;
goto CLEANUP;
}
*status = U_ZERO_ERROR;
b2Len = uidna_IDNToASCII(s2, length2, b2, b2Len, options, &parseError, status);
}
result = compareCaseInsensitiveASCII(b1,b1Len,b2,b2Len);
CLEANUP:
if(b1 != b1Stack){
uprv_free(b1);
}
if(b2 != b2Stack){
uprv_free(b2);
}
return result;
}
#endif