1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "vcard_utils.h"
17 
18 #include <algorithm>
19 #include <array>
20 #include <cctype>
21 #include <ctime>
22 #include <fstream>
23 #include <iomanip>
24 #include <numeric>
25 #include <sstream>
26 
27 #include "glib.h"
28 #include "map"
29 #include "telephony_errors.h"
30 #include "telephony_log_wrapper.h"
31 #include "vcard_configuration.h"
32 #include "vcard_constant.h"
33 
34 namespace OHOS {
35 namespace Telephony {
36 namespace {
37 std::map<ImType, std::string> imLabelIdToType = { { ImType::IM_AIM, VCARD_TYPE_X_AIM },
38     { ImType::IM_MSN, VCARD_TYPE_X_MSN }, { ImType::IM_YAHOO, VCARD_TYPE_X_YAHOO },
39     { ImType::IM_ICQ, VCARD_TYPE_X_ICQ }, { ImType::IM_JABBER, VCARD_TYPE_X_JABBER },
40     { ImType::IM_SKYPE, VCARD_TYPE_X_SKYPE_USERNAME }, { ImType::IM_QQ, VCARD_TYPE_X_QQ },
41     { ImType::IM_HUANLIAO, VCARD_TYPE_X_HUANLIAO} };
42 std::map<PhoneVcType, std::string> phoneLabelIdToType = { { PhoneVcType::NUM_HOME, VCARD_PARAM_TYPE_CELL },
43     { PhoneVcType::NUM_MOBILE, VCARD_PARAM_TYPE_HOME }, { PhoneVcType::NUM_WORK, VCARD_PARAM_TYPE_WORK },
44     { PhoneVcType::NUM_PAGER, VCARD_PARAM_TYPE_PAGER }, { PhoneVcType::NUM_OTHER, VCARD_PARAM_TYPE_VOICE },
45     { PhoneVcType::NUM_CALLBACK, VCARD_PARAM_PHONE_EXTRA_TYPE_CALLBACK },
46     { PhoneVcType::NUM_CAR, VCARD_PARAM_TYPE_CAR }, { PhoneVcType::NUM_COMPANY_MAIN, VCARD_PARAM_TYPE_WORK },
47     { PhoneVcType::NUM_ISDN, VCARD_PARAM_TYPE_ISDN }, { PhoneVcType::NUM_OTHER_FAX, VCARD_PARAM_TYPE_FAX },
48     { PhoneVcType::NUM_RADIO, VCARD_PARAM_PHONE_EXTRA_TYPE_RADIO }, { PhoneVcType::NUM_TELEX, VCARD_PARAM_TYPE_TLX },
49     { PhoneVcType::NUM_TTY_TDD, VCARD_PARAM_PHONE_EXTRA_TYPE_TTY_TDD },
50     { PhoneVcType::NUM_ASSISTANT, VCARD_PARAM_PHONE_EXTRA_TYPE_ASSISTANT },
51     { PhoneVcType::NUM_MMS, VCARD_PARAM_TYPE_MSG }, { PhoneVcType::NUM_MAIN, VCARD_PARAM_TYPE_PREF }};
52 std::map<std::string, PhoneVcType> typeToPhoneTypeMap = { { VCARD_PARAM_TYPE_CAR, PhoneVcType::NUM_CAR },
53     { VCARD_PARAM_TYPE_PAGER, PhoneVcType::NUM_PAGER }, { VCARD_PARAM_TYPE_ISDN, PhoneVcType::NUM_ISDN },
54     { VCARD_PARAM_TYPE_HOME, PhoneVcType::NUM_MOBILE }, { VCARD_PARAM_TYPE_WORK, PhoneVcType::NUM_WORK },
55     { VCARD_PARAM_TYPE_CELL, PhoneVcType::NUM_HOME },
56     { VCARD_PARAM_PHONE_EXTRA_TYPE_CALLBACK, PhoneVcType::NUM_CALLBACK },
57     { VCARD_PARAM_PHONE_EXTRA_TYPE_RADIO, PhoneVcType::NUM_RADIO },
58     { VCARD_PARAM_PHONE_EXTRA_TYPE_TTY_TDD, PhoneVcType::NUM_TTY_TDD },
59     { VCARD_PARAM_PHONE_EXTRA_TYPE_ASSISTANT, PhoneVcType::NUM_ASSISTANT },
60     { VCARD_PARAM_TYPE_VOICE, PhoneVcType::NUM_OTHER } };
61 } // namespace
62 
EncodeBase64(const std::string & input)63 std::string VCardUtils::EncodeBase64(const std::string &input)
64 {
65     gchar *encodedData = g_base64_encode(reinterpret_cast<const guchar *>(input.c_str()), input.length());
66     std::string result(encodedData);
67     g_free(encodedData);
68     return result;
69 }
70 
DecodeBase64(const std::string & input)71 std::string VCardUtils::DecodeBase64(const std::string &input)
72 {
73     gsize outputLength;
74     guchar *decodedData = g_base64_decode(input.c_str(), &outputLength);
75     std::string result(reinterpret_cast<char *>(decodedData), outputLength);
76     g_free(decodedData);
77     return result;
78 }
79 
EqualsIgnoreCase(const std::string & str1,const std::string & str2)80 bool VCardUtils::EqualsIgnoreCase(const std::string &str1, const std::string &str2)
81 {
82     std::string copy1 = str1;
83     std::string copy2 = str2;
84 
85     std::transform(copy1.begin(), copy1.end(), copy1.begin(), ::tolower);
86     std::transform(copy2.begin(), copy2.end(), copy2.begin(), ::tolower);
87 
88     return copy1 == copy2;
89 }
90 
Split(const std::string & input,const std::string & delimiter)91 std::vector<std::string> VCardUtils::Split(const std::string &input, const std::string &delimiter)
92 {
93     std::vector<std::string> result;
94     std::size_t pos = 0;
95     std::size_t delimiterPos;
96 
97     while ((delimiterPos = input.find(delimiter, pos)) != std::string::npos) {
98         std::string token = input.substr(pos, delimiterPos - pos);
99         result.push_back(token);
100         pos = delimiterPos + delimiter.size();
101     }
102 
103     if (pos < input.size()) {
104         std::string token = input.substr(pos);
105         result.push_back(token);
106     }
107 
108     return result;
109 }
110 
Trim(std::string & str)111 std::string VCardUtils::Trim(std::string &str)
112 {
113     std::string::size_type pos1 = str.find_first_not_of(" \t\n\r\f\v");
114     std::string::size_type pos2 = str.find_last_not_of(" \t\n\r\f\v");
115     if (pos1 != std::string::npos && pos2 != std::string::npos) {
116         str = str.substr(pos1, pos2 - pos1 + 1);
117     } else {
118         str.clear();
119     }
120 
121     return str;
122 }
123 
ToUpper(const std::string & str)124 std::string VCardUtils::ToUpper(const std::string &str)
125 {
126     std::string temp = str;
127     for (char &c : temp) {
128         c = std::toupper(c);
129     }
130     return temp;
131 }
132 
StartWith(const std::string & str,const std::string & prefix)133 bool VCardUtils::StartWith(const std::string &str, const std::string &prefix)
134 {
135     if (str.length() < prefix.length()) {
136         return false;
137     }
138     return str.substr(0, prefix.length()) == prefix;
139 }
140 
EndWith(const std::string & fullString,const std::string & ending)141 bool VCardUtils::EndWith(const std::string &fullString, const std::string &ending)
142 {
143     if (fullString.length() < ending.length()) {
144         return false;
145     }
146 
147     std::string extractedEnding = fullString.substr(fullString.length() - ending.length());
148 
149     return extractedEnding == ending;
150 }
151 
ConvertCharset(const std::string & input,const std::string & fromCharset,const std::string & toCharset,int32_t & errorCode)152 std::string VCardUtils::ConvertCharset(
153     const std::string &input, const std::string &fromCharset, const std::string &toCharset, int32_t &errorCode)
154 {
155     GIConv converter = g_iconv_open(toCharset.c_str(), fromCharset.c_str());
156     if (converter == nullptr) {
157         TELEPHONY_LOGE("ConvertCharset open fail");
158         errorCode = TELEPHONY_ERR_VCARD_FILE_INVALID;
159         return "";
160     }
161 
162     size_t inBytes = input.size();
163     size_t outBytes = inBytes * 4; // Allocate enough space for the worst-case scenario
164     char *inBuf = const_cast<char *>(input.c_str());
165     char *outBuf = new char[outBytes];
166     char *outBufPtr = outBuf;
167 
168     if (g_iconv(converter, &inBuf, &inBytes, &outBufPtr, &outBytes) == (size_t)(-1)) {
169         TELEPHONY_LOGE("ConvertCharset open fail");
170         errorCode = TELEPHONY_ERR_VCARD_FILE_INVALID;
171         delete[] outBuf;
172         g_iconv_close(converter);
173         return "";
174     }
175 
176     std::string output(outBuf, outBufPtr - outBuf);
177     delete[] outBuf;
178     g_iconv_close(converter);
179     return output;
180 }
181 
CreateFileName()182 std::string VCardUtils::CreateFileName()
183 {
184     std::time_t now = std::time(nullptr);
185     std::tm *timeinfo = std::localtime(&now);
186     std::ostringstream oss;
187     oss << std::put_time(timeinfo, VCARD_TIME_FORMAT);
188     std::string fileName = oss.str() + ".vcf";
189     return fileName;
190 }
191 
SaveFile(const std::string & fileStr,const std::string & path)192 void VCardUtils::SaveFile(const std::string &fileStr, const std::string &path)
193 {
194     std::ofstream file(path, std::ios::trunc);
195     if (file.is_open()) {
196         std::stringstream ss(fileStr);
197         std::string line;
198 
199         while (std::getline(ss, line)) {
200             file << line << std::endl;
201         }
202         file.close();
203     }
204 }
205 
IsWrapPrintableAscii(std::vector<std::string> strs)206 bool VCardUtils::IsWrapPrintableAscii(std::vector<std::string> strs)
207 {
208     for (auto str : strs) {
209         for (char ch : str) {
210             if (!(IsPrintableAscii(ch) || ch == '\r' || ch == '\n')) {
211                 return false;
212             }
213         }
214     }
215     return true;
216 }
217 
IsPrintableAscii(std::vector<std::string> strs)218 bool VCardUtils::IsPrintableAscii(std::vector<std::string> strs)
219 {
220     for (auto it : strs) {
221         if (!IsPrintableAscii(it)) {
222             return false;
223         }
224     }
225     return true;
226 }
227 
IsPrintableAscii(const std::string & str)228 bool VCardUtils::IsPrintableAscii(const std::string &str)
229 {
230     for (char ch : str) {
231         if (!IsPrintableAscii(ch)) {
232             return false;
233         }
234     }
235     return true;
236 }
237 
IsPrintableAscii(char ch)238 bool VCardUtils::IsPrintableAscii(char ch)
239 {
240     return std::isprint(static_cast<unsigned char>(ch));
241 }
242 
IsNum(const std::string & str)243 bool VCardUtils::IsNum(const std::string &str)
244 {
245     if (str.empty()) {
246         return false;
247     }
248     for (char ch : str) {
249         if (!std::isdigit(ch)) {
250             return false;
251         }
252     }
253     return true;
254 }
255 
GetTypeFromImLabelId(std::string labelId)256 std::string VCardUtils::GetTypeFromImLabelId(std::string labelId)
257 {
258     if (!IsNum(labelId)) {
259         return "";
260     }
261     int32_t num = std::stoi(labelId);
262     auto it = imLabelIdToType.find(static_cast<ImType>(num));
263     if (it != imLabelIdToType.end()) {
264         return it->second;
265     }
266     return "";
267 }
268 
GetLabelIdFromImType(std::string type)269 int32_t VCardUtils::GetLabelIdFromImType(std::string type)
270 {
271     if (type.empty()) {
272         return static_cast<int32_t>(ImType::INVALID_LABEL_ID);
273     }
274     if (type == VCARD_TYPE_X_AIM) {
275         return static_cast<int32_t>(ImType::IM_AIM);
276     } else if (type == VCARD_TYPE_X_MSN) {
277         return static_cast<int32_t>(ImType::IM_MSN);
278     } else if (type == VCARD_TYPE_X_YAHOO) {
279         return static_cast<int32_t>(ImType::IM_YAHOO);
280     } else if (type == VCARD_TYPE_X_ICQ) {
281         return static_cast<int32_t>(ImType::IM_ICQ);
282     } else if (type == VCARD_TYPE_X_JABBER) {
283         return static_cast<int32_t>(ImType::IM_JABBER);
284     } else if (type == VCARD_TYPE_X_QQ) {
285         return static_cast<int32_t>(ImType::IM_QQ);
286     } else if (type == VCARD_TYPE_X_SKYPE_USERNAME) {
287         return static_cast<int32_t>(ImType::IM_SKYPE);
288     } else if (type == VCARD_TYPE_X_HUANLIAO) {
289         return static_cast<int32_t>(ImType::IM_HUANLIAO);
290     } else {
291         return static_cast<int32_t>(ImType::CUSTOM_LABEL);
292     }
293 }
294 
GetTypeFromPhoneLabelId(std::string labelId)295 std::vector<std::string> VCardUtils::GetTypeFromPhoneLabelId(std::string labelId)
296 {
297     std::vector<std::string> paramTypes = {};
298     if (!IsNum(labelId) || labelId.size() > INT_64_LENTGH) {
299         return paramTypes;
300     }
301     int64_t num = std::stoll(labelId);
302     auto phoneType = static_cast<PhoneVcType>(num);
303     auto it = phoneLabelIdToType.find(phoneType);
304     if (it != phoneLabelIdToType.end()) {
305         paramTypes.push_back(it->second);
306         return paramTypes;
307     }
308     switch (phoneType) {
309         case PhoneVcType::NUM_FAX_HOME: {
310             paramTypes.push_back(VCARD_PARAM_TYPE_HOME);
311             paramTypes.push_back(VCARD_PARAM_TYPE_FAX);
312             return paramTypes;
313         }
314         case PhoneVcType::NUM_FAX_WORK: {
315             paramTypes.push_back(VCARD_PARAM_TYPE_WORK);
316             paramTypes.push_back(VCARD_PARAM_TYPE_FAX);
317             return paramTypes;
318         }
319         case PhoneVcType::NUM_WORK_MOBILE: {
320             paramTypes.push_back(VCARD_PARAM_TYPE_WORK);
321             paramTypes.push_back(VCARD_PARAM_TYPE_CELL);
322             return paramTypes;
323         }
324         case PhoneVcType::NUM_WORK_PAGER: {
325             paramTypes.push_back(VCARD_PARAM_TYPE_WORK);
326             paramTypes.push_back(VCARD_PARAM_TYPE_PAGER);
327             return paramTypes;
328         }
329         default:
330             break;
331     }
332     return paramTypes;
333 }
334 
TrimListToString(const std::vector<std::string> & strs)335 std::string VCardUtils::TrimListToString(const std::vector<std::string> &strs)
336 {
337     int32_t size = static_cast<int32_t>(strs.size());
338     std::string result;
339     if (size > 1) {
340         std::string init = "";
341         result = std::accumulate(strs.begin(), strs.end(), init,
342             [](std::string &str, const std::string &element) { return str + element + ";"; });
343     } else if (size == 1) {
344         return strs[0];
345     } else {
346         return "";
347     }
348     return result;
349 }
350 
HandleCh(char nextCh,std::string vcardType)351 std::string VCardUtils::HandleCh(char nextCh, std::string vcardType)
352 {
353     std::string unescapedString = "";
354     if (vcardType == VERSION_40) {
355         if (nextCh == 'n' || nextCh == 'N') {
356             unescapedString = "\n";
357         } else {
358             unescapedString = nextCh;
359         }
360     } else if (vcardType == VERSION_30) {
361         if (nextCh == 'n' || nextCh == 'N') {
362             unescapedString = "\n";
363         } else {
364             unescapedString = nextCh;
365         }
366     } else {
367         if (nextCh == '\\' || nextCh == ';' || nextCh == ':' || nextCh == ',') {
368             unescapedString = nextCh;
369         } else {
370             unescapedString = "";
371         }
372     }
373     return unescapedString;
374 }
375 
ConstructListFromValue(const std::string & value,std::string vcardType)376 std::vector<std::string> VCardUtils::ConstructListFromValue(const std::string &value, std::string vcardType)
377 {
378     std::vector<std::string> result;
379     std::string builder;
380     int32_t length = static_cast<int32_t>(value.length());
381     for (int32_t i = 0; i < length; i++) {
382         char ch = value[i];
383         if (ch == '\\' && i < length - 1) {
384             char nextCh = value[i + 1];
385             std::string unescapedString = HandleCh(nextCh, vcardType);
386             if (!unescapedString.empty()) {
387                 builder += unescapedString;
388                 i++;
389             } else {
390                 builder += ch;
391             }
392         } else if (ch == ';') {
393             result.push_back(builder);
394         } else {
395             builder += ch;
396         }
397     }
398     result.push_back(builder);
399     return result;
400 }
401 
HandleTypeAndLabel(int32_t & type,std::string & label,std::string number,std::string labelCandidate)402 void VCardUtils::HandleTypeAndLabel(int32_t &type, std::string &label, std::string number, std::string labelCandidate)
403 {
404     std::map<std::string, PhoneVcType>::iterator iter = typeToPhoneTypeMap.find(labelCandidate);
405     if (iter != typeToPhoneTypeMap.end()) {
406         PhoneVcType phoneType = iter->second;
407         int32_t typeCandidate = static_cast<int32_t>(phoneType);
408 
409         std::size_t indexOfAt = -1;
410         std::size_t found = number.find("@");
411         if (found != std::string::npos) {
412             indexOfAt = found;
413         }
414 
415         if ((typeCandidate == static_cast<int32_t>(PhoneVcType::NUM_PAGER) && 0 < indexOfAt &&
416                 indexOfAt < number.length() - 1) ||
417             type < 0 || type == static_cast<int32_t>(PhoneVcType::CUSTOM_LABEL) ||
418             type == static_cast<int32_t>(PhoneVcType::NUM_OTHER)) {
419             type = typeCandidate;
420         }
421     } else if (type < 0) {
422         type = static_cast<int32_t>(PhoneVcType::CUSTOM_LABEL);
423         label = labelCandidate;
424     }
425 }
426 
GetPhoneTypeFromStrings(const std::vector<std::string> & types,std::string number,std::tuple<int32_t,std::string> & result)427 void VCardUtils::GetPhoneTypeFromStrings(
428     const std::vector<std::string> &types, std::string number, std::tuple<int32_t, std::string> &result)
429 {
430     int32_t type = -1;
431     std::string label;
432     bool isFax = false;
433     bool hasPref = false;
434 
435     for (std::string typeStringOrg : types) {
436         std::string typeStringUpperCase = ToUpper(typeStringOrg);
437         if (typeStringUpperCase == VCARD_PARAM_TYPE_PREF) {
438             hasPref = true;
439         } else if (typeStringUpperCase == VCARD_PARAM_TYPE_FAX) {
440             isFax = true;
441         } else {
442             std::string labelCandidate;
443             if (StartWith(typeStringUpperCase, "X-") && type < 0) {
444                 labelCandidate = typeStringOrg.substr(VALUE_INDEX_TWO);
445             } else {
446                 labelCandidate = typeStringOrg;
447                 label = labelCandidate;
448             }
449             if (labelCandidate.length() == 0) {
450                 continue;
451             }
452             HandleTypeAndLabel(type, label, number, labelCandidate);
453         }
454     }
455 
456     if (type < 0) {
457         if (hasPref) {
458             type = static_cast<int32_t>(PhoneVcType::NUM_MAIN);
459         } else {
460             type = static_cast<int32_t>(PhoneVcType::NUM_HOME);
461         }
462     }
463 
464     if (isFax) {
465         if (type == static_cast<int32_t>(PhoneVcType::NUM_MOBILE)) {
466             type = static_cast<int32_t>(PhoneVcType::NUM_FAX_HOME);
467         } else if (type == static_cast<int32_t>(PhoneVcType::NUM_WORK)) {
468             type = static_cast<int32_t>(PhoneVcType::NUM_FAX_WORK);
469         } else if (type == static_cast<int32_t>(PhoneVcType::NUM_OTHER)) {
470             type = static_cast<int32_t>(PhoneVcType::NUM_OTHER_FAX);
471         }
472     }
473 
474     if (type == static_cast<int32_t>(PhoneVcType::CUSTOM_LABEL)) {
475         std::get<0>(result) = -1;
476         std::get<1>(result) = label;
477     } else {
478         std::get<0>(result) = type;
479         std::get<1>(result) = "-1";
480     }
481 }
482 
VcardtypeToInt(const std::string & vcardType)483 int32_t VCardUtils::VcardtypeToInt(const std::string &vcardType)
484 {
485     if (vcardType == VERSION_21) {
486         return VERSION_21_NUM;
487     } else if (vcardType == VERSION_30) {
488         return VERSION_30_NUM;
489     } else if (vcardType == VERSION_40) {
490         return VERSION_40_NUM;
491     }
492     return -1;
493 }
494 
FormatNumber(std::string source)495 std::string VCardUtils::FormatNumber(std::string source)
496 {
497     return source;
498 }
499 
GetPhoneNumberFormat(const int32_t vcardType)500 int32_t VCardUtils::GetPhoneNumberFormat(const int32_t vcardType)
501 {
502     if (VCardConfiguration::IsJapaneseDevice(vcardType)) {
503         return VCARD_PHONE_NUM_FORMAT_JAPAN;
504     } else {
505         return VCARD_PHONE_NUM_FORMAT_NANP;
506     }
507 }
508 
GetImageType(std::string bytes)509 std::string VCardUtils::GetImageType(std::string bytes)
510 {
511     if (bytes.empty()) {
512         return "";
513     }
514     int32_t length = static_cast<int32_t>(bytes.length());
515     int32_t gifTypeLength = VALUE_LEN_THREE;
516     if (length >= gifTypeLength && bytes[VALUE_INDEX_ZERO] == 'G' && bytes[1] == 'I' && bytes[VALUE_INDEX_TWO] == 'F') {
517         return "GIF";
518     }
519     int32_t pngTypeLength = VALUE_LEN_FOUR;
520     if (length >= pngTypeLength && bytes[VALUE_INDEX_ZERO] == static_cast<char>(0x89) &&
521         bytes[VALUE_INDEX_ONE] == 'P' && bytes[VALUE_INDEX_TWO] == 'N' && bytes[VALUE_INDEX_THREE] == 'G') {
522         return "PNG";
523     }
524     int32_t jpgTypeLength = VALUE_LEN_TWO;
525     if (length >= jpgTypeLength && bytes[VALUE_INDEX_ZERO] == static_cast<char>(0xff) &&
526         bytes[VALUE_INDEX_ONE] == static_cast<char>(0xd8)) {
527         return "JPEG";
528     }
529     return "";
530 }
531 
IsAllEmpty(std::vector<std::string> values)532 bool VCardUtils::IsAllEmpty(std::vector<std::string> values)
533 {
534     for (auto value : values) {
535         if (!value.empty()) {
536             return false;
537         }
538     }
539     return true;
540 }
541 
542 } // namespace Telephony
543 } // namespace OHOS
544