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 Write to temp file, then rename to ini config file
138 *
139 * @param content - write data
140 * @return int - 0 Success
141 */
WriteFile(const std::string & content)142 int WriteFile(const std::string &content)
143 {
144 if (mFileName.empty()) {
145 LOGE("File name is empty.");
146 return -1;
147 }
148 std::string tempFileName = mFileName + ".temp";
149 FILE* fp = fopen(tempFileName.c_str(), "w");
150 if (!fp) {
151 LOGE("Temp config file: %{public}s, fopen() failed!", tempFileName.c_str());
152 return -1;
153 }
154 size_t ret = fwrite(content.c_str(), 1, content.length(), fp);
155 if (ret != content.length()) {
156 LOGE("Temp config file: %{public}s, fwrite() failed!", tempFileName.c_str());
157 (void)fclose(fp);
158 (void)remove(tempFileName.c_str());
159 return -1;
160 }
161 (void)fflush(fp);
162 (void)fsync(fileno(fp));
163 (void)fclose(fp);
164
165 if (rename(tempFileName.c_str(), mFileName.c_str()) != 0) {
166 LOGE("Save config file: %{public}s, rename() failed!", mFileName.c_str());
167 (void)remove(tempFileName.c_str());
168 return -1;
169 }
170 return 0;
171 }
172
173 /**
174 * @Description read and parses the ini config file, need call SetConfigFilePath first
175 * need call SetEncryptionInfo first when load encrypted config file
176 *
177 * @return int - 0 Success; -1 file not exist
178 */
179 int LoadConfig();
180
181 /**
182 * @Description Save config to file
183 * need call SetEncryptionInfo first when save encrypted config file
184 *
185 * @return int - 0 Success; -1 Failed
186 */
SaveConfig()187 int SaveConfig()
188 {
189 std::string content;
190 std::lock_guard<std::mutex> lock(valueMutex_);
191 {
192 std::ostringstream ss;
193 LOGI("Save config:%{public}s size:%{public}d", GetTClassName<T>().c_str(),
194 static_cast<int>(mValues.size()));
195 for (std::size_t i = 0; i < mValues.size(); ++i) {
196 T &item = mValues[i];
197 /*
198 * here use template function GetTClassName OutTClassString, needing
199 * specialization.
200 */
201 ss << "[" << GetTClassName<T>() << "_" << (i + 1) << "] {" << std::endl;
202 ss << OutTClassString(item) << std::endl;
203 ss << "}" << std::endl;
204 }
205 content = ss.str();
206 }
207 #ifdef FEATURE_ENCRYPTION_SUPPORT
208 if (mSetEncryption) {
209 WifiLoopEncrypt(mEncryptionInfo, content, mEncry);
210 std::fill(content.begin(), content.end(), 0);
211 content = mEncry.encryptedPassword;
212 }
213 #endif
214 mValues.clear(); /* clear values */
215 if (WriteFile(content) != 0) {
216 return -1;
217 }
218 return 0;
219 }
220
221 /**
222 * @Description Get config values
223 *
224 * @param results - output config values
225 * @return int - 0 Success, -1 Failed
226 */
GetValue(std::vector<T> & results)227 int GetValue(std::vector<T> &results)
228 {
229 std::lock_guard<std::mutex> lock(valueMutex_);
230 std::swap(results, mValues);
231 return 0;
232 }
233
234 /**
235 * @Description Get config values
236 *
237 * @return config values
238 */
GetValue()239 const std::vector<T>& GetValue()
240 {
241 std::lock_guard<std::mutex> lock(valueMutex_);
242 return mValues;
243 }
244
245 /**
246 * @Description Set the config value
247 *
248 * @param values - input config values
249 * @return int - 0 Success, -1 Failed
250 */
SetValue(const std::vector<T> & values)251 int SetValue(const std::vector<T> &values)
252 {
253 std::lock_guard<std::mutex> lock(valueMutex_);
254 mValues = values;
255 return 0;
256 }
257
258 private:
259 std::string mFileName;
260 std::vector<T> mValues;
261 bool mSetEncryption;
262 std::mutex valueMutex_;
263 #ifdef FEATURE_ENCRYPTION_SUPPORT
264 WifiEncryptionInfo mEncryptionInfo;
265 EncryptedData mEncry;
266 #endif
267 };
268
269 template<typename T>
SetConfigFilePath(const std::string & fileName)270 int WifiConfigFileImpl<T>::SetConfigFilePath(const std::string &fileName)
271 {
272 mFileName = fileName;
273 mSetEncryption = false;
274 return 0;
275 }
276
277 template<typename T>
SetEncryptionInfo(const std::string & key,const std::string & iv)278 int WifiConfigFileImpl<T>::SetEncryptionInfo(const std::string &key, const std::string &iv)
279 {
280 #ifdef FEATURE_ENCRYPTION_SUPPORT
281 mSetEncryption = true;
282 mEncryptionInfo.SetFile(GetTClassName<T>());
283 if (!key.empty()) {
284 ImportKey(mEncryptionInfo, key);
285 }
286 mEncry.IV = iv;
287 #endif
288 return 0;
289 }
290
291 template<typename T>
UnsetEncryptionInfo()292 int WifiConfigFileImpl<T>::UnsetEncryptionInfo()
293 {
294 #ifdef FEATURE_ENCRYPTION_SUPPORT
295 DeleteKey(mEncryptionInfo);
296 #endif
297 return 0;
298 }
299
300 template<typename T>
ReadNetworkSection(T & item,std::istream & fs,std::string & line)301 int WifiConfigFileImpl<T>::ReadNetworkSection(T &item, std::istream &fs, std::string &line)
302 {
303 int sectionError = 0;
304 while (std::getline(fs, line)) {
305 TrimString(line);
306 if (line.empty()) {
307 continue;
308 }
309 if (line.length() > WIFI_CONFIG_FILE_LINE_MAX_LENGTH) {
310 LOGE("%{public}s %{public}s line length is too big.", __func__, GetTClassName<T>().c_str());
311 sectionError++;
312 break;
313 }
314 if (line[0] == '<' && line[line.length() - 1] == '>') {
315 return sectionError;
316 }
317 std::string::size_type npos = line.find("=");
318 if (npos == std::string::npos) {
319 LOGE("Invalid config line");
320 sectionError++;
321 continue;
322 }
323 std::string key = line.substr(0, npos);
324 std::string value = line.substr(npos + 1);
325 TrimString(key);
326 TrimString(value);
327 /* template function, needing specialization */
328 sectionError += SetTClassKeyValue(item, key, value);
329 std::fill(value.begin(), value.end(), 0);
330 }
331 LOGE("Section config not end correctly");
332 sectionError++;
333 return sectionError;
334 }
335
336 template<typename T>
ReadNetwork(T & item,std::istream & fs,std::string & line)337 int WifiConfigFileImpl<T>::ReadNetwork(T &item, std::istream &fs, std::string &line)
338 {
339 int networkError = 0;
340 while (std::getline(fs, line)) {
341 TrimString(line);
342 if (line.empty()) {
343 continue;
344 }
345 if (line[0] == '<' && line[line.length() - 1] == '>') {
346 networkError += ReadNetworkSection(item, fs, line);
347 } else if (line.compare("}") == 0) {
348 return networkError;
349 } else {
350 LOGE("Invalid config line");
351 networkError++;
352 }
353 }
354 LOGE("Network config not end correctly");
355 networkError++;
356 return networkError;
357 }
358
359 template<typename T>
LoadConfig()360 int WifiConfigFileImpl<T>::LoadConfig()
361 {
362 if (mFileName.empty()) {
363 LOGE("File name is empty.");
364 return -1;
365 }
366 std::ifstream fs(mFileName.c_str());
367 if (!fs.is_open()) {
368 LOGE("Loading config file: %{public}s, fs.is_open() failed!", mFileName.c_str());
369 return -1;
370 }
371 #ifdef FEATURE_ENCRYPTION_SUPPORT
372 if (mSetEncryption) {
373 std::string content((std::istreambuf_iterator<char>(fs)), std::istreambuf_iterator<char>());
374 mEncry.encryptedPassword = content;
375 WifiLoopDecrypt(mEncryptionInfo, mEncry, content);
376 std::stringstream strStream(content);
377 ReadFile(strStream);
378 std::fill(content.begin(), content.end(), 0);
379 } else {
380 ReadFile(fs);
381 }
382 #else
383 ReadFile(fs);
384 #endif
385 fs.close();
386 return 0;
387 }
388 } // namespace Wifi
389 } // namespace OHOS
390 #endif