1 /*
2  * Copyright (C) 2021-2022 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 #ifndef OHOS_WIFI_CONFIG_FILE_IMPL_H
17 #define OHOS_WIFI_CONFIG_FILE_IMPL_H
18 #include <fstream>
19 #include <sstream>
20 #include <string>
21 #include <unistd.h>
22 #include <vector>
23 #include <mutex>
24 #include "wifi_config_file_spec.h"
25 #include "wifi_log.h"
26 #ifdef FEATURE_ENCRYPTION_SUPPORT
27 #include "wifi_encryption_util.h"
28 #endif
29 
30 namespace OHOS {
31 namespace Wifi {
32 constexpr int WIFI_CONFIG_FILE_LINE_MAX_LENGTH = 4096;
33 /**
34  * @Description Remove head and tail space
35  *
36  * @param str - String
37  */
TrimString(std::string & str)38 static inline void TrimString(std::string &str)
39 {
40     int i = 0;
41     int j = static_cast<int>(str.length()) - 1;
42     while (i < static_cast<int>(str.length()) && str[i] == ' ') {
43         ++i;
44     }
45     while (j >= 0 && str[j] == ' ') {
46         --j;
47     }
48     str = ((i > j) ? "" : str.substr(i, j - i + 1));
49 }
50 
51 /**
52  * @Description Delete comment message begin with ; and #
53  *
54  * @param str - String
55  */
DelComment(std::string & str)56 static inline void DelComment(std::string &str)
57 {
58     std::string::size_type i = 0;
59     for (; i < str.length(); ++i) {
60         if (str[i] == ';' || str[i] == '#') {
61             str = str.substr(0, i);
62             break;
63         }
64     }
65     return;
66 }
67 
68 template<typename T>
69 class WifiConfigFileImpl {
70 public:
71     /**
72      * @Description Set the config file path
73      *
74      * @param fileName - file name
75      * @return int - 0 success
76      */
77     int SetConfigFilePath(const std::string &fileName);
78 
79     /**
80      * @Description Set the Encryption info and Encryption tag
81      *
82      * @param key - key
83      * @param iv - iv
84      * @return int - 0 success
85      */
86     int SetEncryptionInfo(const std::string &key, const std::string &iv);
87 
88     /**
89      * @Description Unset the Encryption info: delete the key loaded in hks
90      *
91      * @return int - 0 success
92      */
93     int UnsetEncryptionInfo();
94 
95     /**
96      * @Description read and parses the network section of ini config file, need call SetConfigFilePath first
97      *
98      * @return int - 0 Success; >0 parse failed
99      */
100     int ReadNetworkSection(T &item, std::istream &fs, std::string &line);
101 
102     /**
103      * @Description read and parses the networks of ini config file, need call SetConfigFilePath first
104      *
105      * @return int - 0 Success; >0 parse failed
106      */
107     int ReadNetwork(T &item, std::istream &fs, std::string &line);
108 
109     /**
110      * @Description read ini config file, need call SetConfigFilePath first
111      */
ReadFile(std::istream & fs)112     void ReadFile(std::istream &fs)
113     {
114         std::lock_guard<std::mutex> lock(valueMutex_);
115         mValues.clear();
116         T item;
117         std::string line;
118         int configError;
119         while (std::getline(fs, line)) {
120             TrimString(line);
121             if (line.empty()) {
122                 continue;
123             }
124             if (line[0] == '[' && line[line.length() - 1] == '{') {
125                 ClearTClass(item); /* template function, needing specialization */
126                 configError = ReadNetwork(item, fs, line);
127                 if (configError > 0) {
128                     LOGE("Parse network failed.");
129                     continue;
130                 }
131                 mValues.push_back(item);
132             }
133         }
134     }
135 
136     /**
137      * @Description read and parses the ini config file, need call SetConfigFilePath first
138      * need call SetEncryptionInfo first when load encrypted config file
139      *
140      * @return int - 0 Success; -1 file not exist
141      */
142     int LoadConfig();
143 
144     /**
145      * @Description Save config to file
146      * need call SetEncryptionInfo first when save encrypted config file
147      *
148      * @return int - 0 Success; -1 Failed
149      */
SaveConfig()150     int SaveConfig()
151     {
152         if (mFileName.empty()) {
153             LOGE("File name is empty.");
154             return -1;
155         }
156         FILE* fp = fopen(mFileName.c_str(), "w");
157         if (!fp) {
158             LOGE("Save config file: %{public}s, fopen() failed!", mFileName.c_str());
159             return -1;
160         }
161         std::string content;
162         std::lock_guard<std::mutex> lock(valueMutex_);
163         {
164             std::ostringstream ss;
165             LOGI("Save config:%{public}s size:%{public}d", GetTClassName<T>().c_str(),
166                 static_cast<int>(mValues.size()));
167             for (std::size_t i = 0; i < mValues.size(); ++i) {
168                 T &item = mValues[i];
169                 /*
170                 * here use template function GetTClassName OutTClassString, needing
171                 * specialization.
172                 */
173                 ss << "[" << GetTClassName<T>() << "_" << (i + 1) << "] {" << std::endl;
174                 ss << OutTClassString(item) << std::endl;
175                 ss << "}" << std::endl;
176             }
177             content = ss.str();
178         }
179 #ifdef FEATURE_ENCRYPTION_SUPPORT
180         if (mSetEncryption) {
181             WifiLoopEncrypt(mEncryptionInfo, content, mEncry);
182             std::fill(content.begin(), content.end(), 0);
183             content = mEncry.encryptedPassword;
184         }
185 #endif
186         size_t ret = fwrite(content.c_str(), 1, content.length(), fp);
187         if (ret != content.length()) {
188             LOGE("Save config file: %{public}s, fwrite() failed!", mFileName.c_str());
189         }
190         (void)fflush(fp);
191         (void)fsync(fileno(fp));
192         (void)fclose(fp);
193         mValues.clear(); /* clear values */
194         return 0;
195     }
196 
197     /**
198      * @Description Get config values
199      *
200      * @param results - output config values
201      * @return int - 0 Success, -1 Failed
202      */
GetValue(std::vector<T> & results)203     int GetValue(std::vector<T> &results)
204     {
205         std::lock_guard<std::mutex> lock(valueMutex_);
206         std::swap(results, mValues);
207         return 0;
208     }
209 
210     /**
211      * @Description Get config values
212      *
213      * @return config values
214      */
GetValue()215     const std::vector<T>& GetValue()
216     {
217         std::lock_guard<std::mutex> lock(valueMutex_);
218         return mValues;
219     }
220 
221     /**
222      * @Description Set the config value
223      *
224      * @param values - input config values
225      * @return int - 0 Success, -1 Failed
226      */
SetValue(const std::vector<T> & values)227     int SetValue(const std::vector<T> &values)
228     {
229         std::lock_guard<std::mutex> lock(valueMutex_);
230         mValues = values;
231         return 0;
232     }
233 
234 private:
235     std::string mFileName;
236     std::vector<T> mValues;
237     bool mSetEncryption;
238     std::mutex valueMutex_;
239 #ifdef FEATURE_ENCRYPTION_SUPPORT
240     WifiEncryptionInfo mEncryptionInfo;
241     EncryptedData mEncry;
242 #endif
243 };
244 
245 template<typename T>
SetConfigFilePath(const std::string & fileName)246 int WifiConfigFileImpl<T>::SetConfigFilePath(const std::string &fileName)
247 {
248     mFileName = fileName;
249     mSetEncryption = false;
250     return 0;
251 }
252 
253 template<typename T>
SetEncryptionInfo(const std::string & key,const std::string & iv)254 int WifiConfigFileImpl<T>::SetEncryptionInfo(const std::string &key, const std::string &iv)
255 {
256 #ifdef FEATURE_ENCRYPTION_SUPPORT
257     mSetEncryption = true;
258     mEncryptionInfo.SetFile(GetTClassName<T>());
259     if (!key.empty()) {
260         ImportKey(mEncryptionInfo, key);
261     }
262     mEncry.IV = iv;
263 #endif
264     return 0;
265 }
266 
267 template<typename T>
UnsetEncryptionInfo()268 int WifiConfigFileImpl<T>::UnsetEncryptionInfo()
269 {
270 #ifdef FEATURE_ENCRYPTION_SUPPORT
271     DeleteKey(mEncryptionInfo);
272 #endif
273     return 0;
274 }
275 
276 template<typename T>
ReadNetworkSection(T & item,std::istream & fs,std::string & line)277 int WifiConfigFileImpl<T>::ReadNetworkSection(T &item, std::istream &fs, std::string &line)
278 {
279     int sectionError = 0;
280     while (std::getline(fs, line)) {
281         TrimString(line);
282         if (line.empty()) {
283             continue;
284         }
285         if (line.length() > WIFI_CONFIG_FILE_LINE_MAX_LENGTH) {
286             LOGE("%{public}s %{public}s line length is too big.", __func__, GetTClassName<T>().c_str());
287             sectionError++;
288             break;
289         }
290         if (line[0] == '<' && line[line.length() - 1] == '>') {
291             return sectionError;
292         }
293         std::string::size_type npos = line.find("=");
294         if (npos == std::string::npos) {
295             LOGE("Invalid config line");
296             sectionError++;
297             continue;
298         }
299         std::string key = line.substr(0, npos);
300         std::string value = line.substr(npos + 1);
301         TrimString(key);
302         TrimString(value);
303         /* template function, needing specialization */
304         sectionError += SetTClassKeyValue(item, key, value);
305         std::fill(value.begin(), value.end(), 0);
306     }
307     LOGE("Section config not end correctly");
308     sectionError++;
309     return sectionError;
310 }
311 
312 template<typename T>
ReadNetwork(T & item,std::istream & fs,std::string & line)313 int WifiConfigFileImpl<T>::ReadNetwork(T &item, std::istream &fs, std::string &line)
314 {
315     int networkError = 0;
316     while (std::getline(fs, line)) {
317         TrimString(line);
318         if (line.empty()) {
319             continue;
320         }
321         if (line[0] == '<' && line[line.length() - 1] == '>') {
322             networkError += ReadNetworkSection(item, fs, line);
323         } else if (line.compare("}") == 0) {
324             return networkError;
325         } else {
326             LOGE("Invalid config line");
327             networkError++;
328         }
329     }
330     LOGE("Network config not end correctly");
331     networkError++;
332     return networkError;
333 }
334 
335 template<typename T>
LoadConfig()336 int WifiConfigFileImpl<T>::LoadConfig()
337 {
338     if (mFileName.empty()) {
339         LOGE("File name is empty.");
340         return -1;
341     }
342     std::ifstream fs(mFileName.c_str());
343     if (!fs.is_open()) {
344         LOGE("Loading config file: %{public}s, fs.is_open() failed!", mFileName.c_str());
345         return -1;
346     }
347 #ifdef FEATURE_ENCRYPTION_SUPPORT
348     if (mSetEncryption) {
349         std::string content((std::istreambuf_iterator<char>(fs)), std::istreambuf_iterator<char>());
350         mEncry.encryptedPassword = content;
351         WifiLoopDecrypt(mEncryptionInfo, mEncry, content);
352         std::stringstream strStream(content);
353         ReadFile(strStream);
354         std::fill(content.begin(), content.end(), 0);
355     } else {
356         ReadFile(fs);
357     }
358 #else
359     ReadFile(fs);
360 #endif
361     fs.close();
362     return 0;
363 }
364 }  // namespace Wifi
365 }  // namespace OHOS
366 #endif