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