1/*
2 * libjingle
3 * Copyright 2008, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/base/optionsfile.h"
29
30#include <ctype.h>
31
32#include "talk/base/logging.h"
33#include "talk/base/stream.h"
34#include "talk/base/stringencode.h"
35
36namespace talk_base {
37
38OptionsFile::OptionsFile(const std::string &path) : path_(path) {
39}
40
41bool OptionsFile::Load() {
42  options_.clear();
43  // Open file.
44  FileStream stream;
45  int err;
46  if (!stream.Open(path_, "r", &err)) {
47    LOG_F(LS_WARNING) << "Could not open file, err=" << err;
48    // We do not consider this an error because we expect there to be no file
49    // until the user saves a setting.
50    return true;
51  }
52  // Read in all its data.
53  std::string line;
54  StreamResult res;
55  for (;;) {
56    res = stream.ReadLine(&line);
57    if (res != SR_SUCCESS) {
58      break;
59    }
60    size_t equals_pos = line.find('=');
61    if (equals_pos == std::string::npos) {
62      // We do not consider this an error. Instead we ignore the line and
63      // keep going.
64      LOG_F(LS_WARNING) << "Ignoring malformed line in " << path_;
65      continue;
66    }
67    std::string key(line, 0, equals_pos);
68    std::string value(line, equals_pos + 1, line.length() - (equals_pos + 1));
69    options_[key] = value;
70  }
71  if (res != SR_EOS) {
72    LOG_F(LS_ERROR) << "Error when reading from file";
73    return false;
74  } else {
75    return true;
76  }
77}
78
79bool OptionsFile::Save() {
80  // Open file.
81  FileStream stream;
82  int err;
83  if (!stream.Open(path_, "w", &err)) {
84    LOG_F(LS_ERROR) << "Could not open file, err=" << err;
85    return false;
86  }
87  // Write out all the data.
88  StreamResult res = SR_SUCCESS;
89  size_t written;
90  int error;
91  for (OptionsMap::const_iterator i = options_.begin(); i != options_.end();
92       ++i) {
93    res = stream.WriteAll(i->first.c_str(), i->first.length(), &written,
94        &error);
95    if (res != SR_SUCCESS) {
96      break;
97    }
98    res = stream.WriteAll("=", 1, &written, &error);
99    if (res != SR_SUCCESS) {
100      break;
101    }
102    res = stream.WriteAll(i->second.c_str(), i->second.length(), &written,
103        &error);
104    if (res != SR_SUCCESS) {
105      break;
106    }
107    res = stream.WriteAll("\n", 1, &written, &error);
108    if (res != SR_SUCCESS) {
109      break;
110    }
111  }
112  if (res != SR_SUCCESS) {
113    LOG_F(LS_ERROR) << "Unable to write to file";
114    return false;
115  } else {
116    return true;
117  }
118}
119
120bool OptionsFile::IsLegalName(const std::string &name) {
121  for (size_t pos = 0; pos < name.length(); ++pos) {
122    if (name[pos] == '\n' || name[pos] == '\\' || name[pos] == '=') {
123      // Illegal character.
124      LOG(LS_WARNING) << "Ignoring operation for illegal option " << name;
125      return false;
126    }
127  }
128  return true;
129}
130
131bool OptionsFile::IsLegalValue(const std::string &value) {
132  for (size_t pos = 0; pos < value.length(); ++pos) {
133    if (value[pos] == '\n' || value[pos] == '\\') {
134      // Illegal character.
135      LOG(LS_WARNING) << "Ignoring operation for illegal value " << value;
136      return false;
137    }
138  }
139  return true;
140}
141
142bool OptionsFile::GetStringValue(const std::string& option,
143                                 std::string *out_val) const {
144  LOG(LS_VERBOSE) << "OptionsFile::GetStringValue "
145                  << option;
146  if (!IsLegalName(option)) {
147    return false;
148  }
149  OptionsMap::const_iterator i = options_.find(option);
150  if (i == options_.end()) {
151    return false;
152  }
153  *out_val = i->second;
154  return true;
155}
156
157bool OptionsFile::GetIntValue(const std::string& option,
158                              int *out_val) const {
159  LOG(LS_VERBOSE) << "OptionsFile::GetIntValue "
160                  << option;
161  if (!IsLegalName(option)) {
162    return false;
163  }
164  OptionsMap::const_iterator i = options_.find(option);
165  if (i == options_.end()) {
166    return false;
167  }
168  return FromString(i->second, out_val);
169}
170
171bool OptionsFile::SetStringValue(const std::string& option,
172                                 const std::string& value) {
173  LOG(LS_VERBOSE) << "OptionsFile::SetStringValue "
174                  << option << ":" << value;
175  if (!IsLegalName(option) || !IsLegalValue(value)) {
176    return false;
177  }
178  options_[option] = value;
179  return true;
180}
181
182bool OptionsFile::SetIntValue(const std::string& option,
183                              int value) {
184  LOG(LS_VERBOSE) << "OptionsFile::SetIntValue "
185                  << option << ":" << value;
186  if (!IsLegalName(option)) {
187    return false;
188  }
189  return ToString(value, &options_[option]);
190}
191
192bool OptionsFile::RemoveValue(const std::string& option) {
193  LOG(LS_VERBOSE) << "OptionsFile::RemoveValue " << option;
194  if (!IsLegalName(option)) {
195    return false;
196  }
197  options_.erase(option);
198  return true;
199}
200
201}  // namespace talk_base
202