/* * Copyright (c) 2013, The Linux Foundation. All rights reserved. * * wpa_supplicant/hostapd / common helper functions, etc. * Copyright (c) 2002-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #define LOG_TAG "wifi_gbk2utf" #include "jni.h" #include "com_android_server_wifi_Gbk2Utf.h" #define BUF_SIZE 256 #define CONVERT_LINE_LEN 2048 #define CHARSET_CN ("gbk") namespace android { static jint DBG = false; struct accessPointObjectItem *g_pItemList = NULL; struct accessPointObjectItem *g_pLastNode = NULL; pthread_mutex_t *g_pItemListMutex = NULL; static void addAPObjectItem(const char *ssid, const char *ssid_utf8) { if (NULL == ssid || NULL == ssid_utf8) { ALOGE("ssid or ssid_utf8 is NULL"); return; } struct accessPointObjectItem *pTmpItemNode = NULL; struct accessPointObjectItem *pItemNode = NULL; bool foundItem = false; pthread_mutex_lock(g_pItemListMutex); pTmpItemNode = g_pItemList; while (pTmpItemNode) { if (pTmpItemNode->ssid && (*(pTmpItemNode->ssid) == ssid)) { foundItem = true; break; } pTmpItemNode = pTmpItemNode->pNext; } if (foundItem) { if (DBG) ALOGD("Found AP %s", pTmpItemNode->ssid->string()); } else { pItemNode = new struct accessPointObjectItem(); if (NULL == pItemNode) { ALOGE("Failed to allocate memory for new item!"); goto EXIT; } memset(pItemNode, 0, sizeof(accessPointObjectItem)); pItemNode->ssid_utf8 = new String8(ssid_utf8); if (NULL == pItemNode->ssid_utf8) { ALOGE("Failed to allocate memory for new ssid_utf8!"); delete pItemNode; goto EXIT; } pItemNode->ssid = new String8(ssid); if (NULL == pItemNode->ssid) { ALOGE("Failed to allocate memory for new ssid!"); delete pItemNode; goto EXIT; } pItemNode->pNext = NULL; if (DBG) ALOGD("AP doesn't exist, new one for %s", ssid); if (NULL == g_pItemList) { g_pItemList = pItemNode; g_pLastNode = g_pItemList; } else { g_pLastNode->pNext = pItemNode; g_pLastNode = pItemNode; } } EXIT: pthread_mutex_unlock(g_pItemListMutex); } static int hex2num(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } static int hex2byte(const char *hex) { int a, b; a = hex2num(*hex++); if (a < 0) return -1; b = hex2num(*hex++); if (b < 0) return -1; return (a << 4) | b; } /* parse SSID string encoded from wpa_supplicant to normal string */ static size_t ssid_decode(char *buf, size_t maxlen, const char *str) { const char *pos = str; size_t len = 0; int val; while (*pos) { if (len == maxlen) break; switch (*pos) { case '\\': pos++; switch (*pos) { case '\\': buf[len++] = '\\'; pos++; break; case '"': buf[len++] = '"'; pos++; break; case 'n': buf[len++] = '\n'; pos++; break; case 'r': buf[len++] = '\r'; pos++; break; case 't': buf[len++] = '\t'; pos++; break; case 'e': buf[len++] = '\e'; pos++; break; case 'x': pos++; val = hex2byte(pos); if (val < 0) { val = hex2num(*pos); if (val < 0) break; buf[len++] = val; pos++; } else { buf[len++] = val; pos += 2; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': val = *pos++ - '0'; if (*pos >= '0' && *pos <= '7') val = val * 8 + (*pos++ - '0'); if (*pos >= '0' && *pos <= '7') val = val * 8 + (*pos++ - '0'); buf[len++] = val; break; default: break; } break; default: buf[len++] = *pos++; break; } } return len; } /* This function can be used to convert SSIDs into printable form. Since wifi * framework layer needs to parse printable form string. */ static void ssid_encode(char *txt, size_t maxlen, const char *data, unsigned int len) { char *end = txt + maxlen; size_t i; for (i = 0; i < len; i++) { if (txt + 4 > end) break; switch (data[i]) { case '\"': *txt++ = '\\'; *txt++ = '\"'; break; case '\\': *txt++ = '\\'; *txt++ = '\\'; break; case '\e': *txt++ = '\\'; *txt++ = 'e'; break; case '\n': *txt++ = '\\'; *txt++ = 'n'; break; case '\r': *txt++ = '\\'; *txt++ = 'r'; break; case '\t': *txt++ = '\\'; *txt++ = 't'; break; default: if (data[i] >= 32 && data[i] <= 127) { *txt++ = data[i]; } else { txt += snprintf(txt, end - txt, "\\x%02x", data[i]); } break; } } *txt = '\0'; } /* check if the SSID string is UTF coded */ static bool isUTF8String(const char* str, int length) { unsigned int nBytes = 0; unsigned char chr; bool bAllAscii = true; for (int i = 0; i < length; i++) { chr = *(str+i); if ((chr & 0x80) != 0) { bAllAscii = false; } if (0 == nBytes) { if (chr >= 0x80) { if (chr >= 0xFC && chr <= 0xFD) { nBytes = 6; } else if (chr >= 0xF8) { nBytes = 5; } else if (chr >= 0xF0) { nBytes = 4; } else if (chr >= 0xE0) { nBytes = 3; } else if (chr >= 0xC0) { nBytes = 2; } else { return false; } nBytes--; } } else { if ((chr & 0xC0) != 0x80) { return false; } nBytes--; } } if (nBytes > 0 || bAllAscii) { return false; } return true; } /* * https://en.wikipedia.org/wiki/GBK * * GBK character is encoded as 1 or 2 bytes. * - A single byte with range 0x00-0x7f is ASCII. * - A byte with the high bit set indicates that it is * the first of 2 bytes. * byte1: (0x81-0xFE) * byte2: (0x40-0xFE) except 0x7F * * This function only returns true only it is GBK string * but not all character is ASCII. */ static bool isGBKString(const char *str, int length) { unsigned char byte1; unsigned char byte2; bool isAllASCII = true; for (int i = 0; i < length; i ++) { byte1 = *(str+i); if (byte1 >= 0x81 && byte1 < 0xFF && (i+1) < length) { byte2 = *(str+i+1); if (byte2 >= 0x40 && byte2 < 0xFF && byte2 != 0x7F) { // GBK isAllASCII = false; i ++; continue; } else { return false; } } else if (byte1 < 0x80){ // ASCII continue; } else { return false; } } if (isAllASCII) return false; return true; } static void createFromHex(char *buf, int maxlen, const char *str) { const char *pos = str; int len = 0; int val; while(*pos){ if (len == maxlen) break; val = hex2byte(pos); if (val < 0) { val = hex2num(*pos); if (val < 0) break; buf[len++] = val; } else { buf[len++] = val; pos += 2; } } } static size_t createToHex(char *buf, size_t buf_size, const char *str, unsigned int len) { size_t i; char *pos = buf, *end = buf + buf_size; int ret; if (buf_size == 0) return 0; for (i = 0; i < len; i++) { ret = snprintf(pos, end - pos, "%02x", str[i]); if (ret < 0 || ret >= end - pos) { end[-1] = '\0'; return pos - buf; } pos += ret; } end[-1] = '\0'; return pos - buf; } void parseScanResults(String16& str, const char *reply) { unsigned int lineBeg = 0, lineEnd = 0; size_t replyLen = strlen(reply); char ssid[BUF_SIZE] = {0}; char ssid_utf8[BUF_SIZE] = {0}; char ssid_txt[BUF_SIZE] = {0}; bool isUTF8 = false, isCh = false; char buf[BUF_SIZE] = {0}; String8 line; UConverterType conType = UCNV_UTF8; char dest[CONVERT_LINE_LEN] = {0}; UErrorCode err = U_ZERO_ERROR; UConverter* pConverter = ucnv_open(CHARSET_CN, &err); if (U_FAILURE(err)) { ALOGE("ucnv_open error"); return; } /* Parse every line of the reply to construct accessPointObjectItem list */ for (lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) { if (lineEnd == replyLen || '\n' == reply[lineEnd]) { line.setTo(reply + lineBeg, lineEnd - lineBeg + 1); if (DBG) ALOGD("%s, line=%s ", __FUNCTION__, line.string()); if (strncmp(line.string(), "ssid=", 5) == 0) { sscanf(line.string() + 5, "%[^\n]", ssid); ssid_decode(buf,BUF_SIZE,ssid); isUTF8 = isUTF8String(buf,sizeof(buf)); isCh = isGBKString(buf, sizeof(buf)); if (DBG) ALOGD("%s, ssid = %s, buf = %s,isUTF8= %d, isCh = %d", __FUNCTION__, ssid, buf ,isUTF8, isCh); if (!isUTF8 && isCh) { ucnv_toAlgorithmic(conType, pConverter, dest, CONVERT_LINE_LEN, buf, strlen(buf), &err); if (U_FAILURE(err)) { ALOGE("ucnv_toUChars error"); goto EXIT; } ssid_encode(ssid_txt, BUF_SIZE, dest, strlen(dest)); if (DBG) ALOGD("%s, ssid_txt = %s", __FUNCTION__,ssid_txt); str += String16("ssid="); str += String16(ssid_txt); str += String16("\n"); memset(ssid_utf8, 0, BUF_SIZE); strlcpy(ssid_utf8, dest, BUF_SIZE); memset(dest, 0, CONVERT_LINE_LEN); memset(ssid_txt, 0, BUF_SIZE); } else { memset(buf, 0, BUF_SIZE); str += String16(line.string()); } } else if (strncmp(line.string(), "====", 4) == 0) { if (DBG) ALOGD("After sscanf,ssid:%s, isCh:%d", ssid, isCh); if( !isUTF8 && isCh){ addAPObjectItem(buf, ssid_utf8); memset(buf, 0, BUF_SIZE); } } if (strncmp(line.string(), "ssid=", 5) != 0) str += String16(line.string()); lineBeg = lineEnd + 1; } } EXIT: ucnv_close(pConverter); } void constructSsid(String16& str, const char *reply) { size_t replyLen = strlen(reply); char ssid[BUF_SIZE] = {0}; char buf[BUF_SIZE] = {0}; char ssid_txt[BUF_SIZE] ={0}; bool isUTF8 = false, isCh = false; char dest[CONVERT_LINE_LEN] = {0}; UConverterType conType = UCNV_UTF8; UErrorCode err = U_ZERO_ERROR; UConverter* pConverter = ucnv_open(CHARSET_CN, &err); if (U_FAILURE(err)) { ALOGE("ucnv_open error"); return; } sscanf(reply, "%[^\n]", ssid); if (DBG) ALOGD("%s, ssid = %s", __FUNCTION__, ssid); createFromHex(buf, BUF_SIZE, ssid); isUTF8 = isUTF8String(buf, sizeof(buf)); isCh = isGBKString(buf, sizeof(buf)); if (!isUTF8 && isCh) { ucnv_toAlgorithmic(conType, pConverter, dest, CONVERT_LINE_LEN, buf, strlen(buf), &err); if (U_FAILURE(err)) { ALOGE("ucnv_toUChars error"); goto EXIT; } createToHex(ssid_txt, strlen(dest)*2 + 1, dest, strlen(dest)); if (DBG) ALOGD("%s, ssid_txt = %s, dest = %s \n" , __FUNCTION__, ssid_txt, dest); str += String16(ssid_txt); str += String16("\n"); memset(dest, 0, CONVERT_LINE_LEN); memset(buf, 0, BUF_SIZE); memset(ssid_txt, 0, BUF_SIZE); } else { memset(buf, 0, BUF_SIZE); str += String16(reply); } EXIT: ucnv_close(pConverter); } jboolean setNetworkVariable(char *buf) { struct accessPointObjectItem *pTmpItemNode = NULL; char pos[BUF_SIZE] = {0}; bool isCh = false; bool gbk_found = false; int i; unsigned int netId; char name[BUF_SIZE] = {0}; char value[BUF_SIZE] = {0}; char interface[BUF_SIZE] = {0}; char dummy[BUF_SIZE] = {0}; char ssid[BUF_SIZE] = {0}; size_t utf8_len = 0; if (strnlen(buf, BUF_SIZE) == BUF_SIZE) { ALOGE("setNetworkVariable failed due to invalid length"); return JNI_FALSE; } /* parse SET_NETWORK command*/ sscanf(buf, "%s %s %d %s %s", interface, dummy, &netId, name, value); /* L Framework will convert string to HEX, so we convert it back here for comparation */ if (0 == strncmp(name, "ssid", 4)) { createFromHex(ssid, BUF_SIZE, value); } if (DBG) ALOGD("parse SET_NETWORK command success, netId = %d, name = %s, value =%s, length=%d", netId, name, value, strlen(value)); if (NULL == g_pItemListMutex) { /* Driver is unloaded, g_pItemList, g_pItemListMutex are NULL */ return JNI_TRUE; } pthread_mutex_lock(g_pItemListMutex); pTmpItemNode = g_pItemList; if (NULL == pTmpItemNode) { ALOGE("g_pItemList is NULL"); } while (pTmpItemNode) { if (pTmpItemNode->ssid_utf8) { ALOGD("ssid_utf8 = %s, length=%d, value =%s, length=%d", pTmpItemNode->ssid_utf8->string(),strlen(pTmpItemNode->ssid_utf8->string()), ssid, strlen(ssid)); if (0 == strcmp(pTmpItemNode->ssid_utf8->string(), ssid)) { gbk_found = true; break; } } pTmpItemNode = pTmpItemNode->pNext; } if (0 == strncmp(name, "ssid", 4) && gbk_found) { snprintf(buf, BUF_SIZE, "%s SET_NETWORK %d ssid \"%s\"", interface, netId, pTmpItemNode->ssid->string()); if (DBG) ALOGD("new SET_NETWORK command is: %s", buf); } pthread_mutex_unlock(g_pItemListMutex); return JNI_TRUE; } void constructEventSsid(char *eventstr) { char *tmp = NULL; char ssid[BUF_SIZE] = {0}; char ssid_txt[BUF_SIZE] = {0}; char buf[BUF_SIZE] = {0}; bool isUTF8 = false, isCh = false; UConverterType conType = UCNV_UTF8; char dest[CONVERT_LINE_LEN] = {0}; UErrorCode err = U_ZERO_ERROR; UConverter* pConverter = ucnv_open(CHARSET_CN, &err); if (U_FAILURE(err)) { ALOGE("ucnv_open error"); return; } tmp = strstr(eventstr, " SSID"); if (tmp&&(strlen(tmp) > 6 )) { if(!strstr(tmp,"=")) sscanf(tmp + 7, "%[^\']", ssid); else sscanf(tmp + 6, "%s", ssid); if (DBG) ALOGD("%s, SSID = %s", __FUNCTION__, ssid); ssid_decode(buf,BUF_SIZE,ssid); isUTF8 = isUTF8String(buf,sizeof(buf)); isCh = isGBKString(buf, sizeof(buf)); if (!isUTF8 && isCh) { ucnv_toAlgorithmic(conType, pConverter, dest, CONVERT_LINE_LEN, buf, strlen(buf), &err); if (U_FAILURE(err)) { ALOGE("ucnv_toUChars error"); goto EXIT; } ssid_encode(ssid_txt, BUF_SIZE, dest, strlen(dest)); if (!strstr(tmp,"=")) snprintf(eventstr + (strlen(eventstr) - strlen(tmp)), strlen(eventstr), " SSID \'%s\'", ssid_txt); else snprintf(eventstr + (strlen(eventstr) - strlen(tmp)), strlen(eventstr), " SSID=%s", ssid_txt); if (DBG) ALOGD("%s, ssid_txt = %s, eventsrt = %s", __FUNCTION__, ssid_txt, eventstr); } } EXIT: ucnv_close(pConverter); } } //namespace android