2 * shib-ini.h -- INI file handling
4 * Created By: Derek Atkins <derek@ihtfp.com>
9 #include "shib-target.h"
15 #include <sys/types.h>
18 #include <log4cpp/Category.hh>
21 using namespace shibtarget;
23 class HeaderIterator : public shibtarget::ShibINI::Iterator {
25 HeaderIterator (ShibINIPriv* ini);
26 ~HeaderIterator () { }
28 const string* begin();
32 map<string, map<string,string> >::const_iterator iter;
36 class TagIterator : public ShibINI::Iterator {
38 TagIterator (ShibINIPriv* ini, const string& header);
41 const string* begin();
46 map<string,string>::const_iterator iter;
50 class shibtarget::ShibINIPriv {
54 log4cpp::Category *log;
56 map<string, map<string, string> > table;
60 unsigned long modtime;
63 ShibINIPriv::ShibINIPriv()
65 string ctx = "shibtarget.ShibINI";
66 log = &(log4cpp::Category::getInstance(ctx));
69 static void trimline (string& s)
71 int end = s.size() - 1, start = 0;
73 // Trim stuff on right.
74 while (end > 0 && !isgraph(s[end])) end--;
76 // Trim stuff on left.
77 while (start < end && !isgraph(s[start])) start++;
80 s = s.substr(start, end - start + 1);
83 static void to_lowercase (string& s)
85 for (int i = 0, sz = s.size(); i < sz; i++)
93 void ShibINI::init (string& f, bool case_sensitive)
95 m_priv = new ShibINIPriv();
97 m_priv->cs = case_sensitive;
98 m_priv->log->info ("initializing INI file: %s (sensitive=%s)", f.c_str(),
99 (case_sensitive ? "true" : "false"));
103 void ShibINI::refresh(void)
105 saml::NDC ndc("refresh");
107 // check if we need to refresh
109 struct _stat stat_buf;
110 if (_stat (m_priv->file.c_str(), &stat_buf) < 0)
112 struct stat stat_buf;
113 if (stat (m_priv->file.c_str(), &stat_buf) < 0)
115 m_priv->log->error("stat failed: %s", m_priv->file.c_str());
117 if (m_priv->modtime == stat_buf.st_mtime)
120 m_priv->modtime = stat_buf.st_mtime;
122 // clear the existing maps
123 m_priv->table.clear();
125 m_priv->log->info("reading %s", m_priv->file.c_str());
130 ifstream infile (m_priv->file.c_str());
132 m_priv->log->warn("cannot open file: %s", m_priv->file.c_str());
136 const int MAXLEN = 1024;
137 char linebuffer[MAXLEN];
138 string current_header;
139 bool have_header = false;
142 infile.getline (linebuffer, MAXLEN);
143 string line (linebuffer);
145 if (line[0] == '#') continue;
148 if (line.size() <= 1) continue;
150 if (line[0] == '[') {
153 m_priv->log->debug("Found what appears to be a header line");
157 // find the end of the header
158 int endpos = line.find (']');
159 if (endpos == line.npos) {
160 m_priv->log->debug("Weird: no end found.. punting");
161 continue; // HUH? No end?
165 current_header = line.substr (1, endpos-1);
166 trimline (current_header);
168 if (!m_priv->cs) to_lowercase (current_header);
170 m_priv->table[current_header] = map<string,string>();
172 m_priv->log->debug("current header: \"%s\"", current_header.c_str());
174 } else if (have_header) {
177 m_priv->log->debug("Found what appears to be a tag line");
180 int mid = line.find ('=');
182 if (mid == line.npos) {
183 m_priv->log->debug("Weird: no '=' found.. punting");
184 continue; // Can't find the value's setting
187 tag = line.substr (0,mid);
188 setting = line.substr (mid+1, line.size()-mid);
193 if (!m_priv->cs) to_lowercase (tag);
195 // If it already exists, log an error and do not save it
196 if (exists (current_header, tag))
197 m_priv->log->error("Duplicate tag found in section %s: \"%s\"",
198 current_header.c_str(), tag.c_str());
200 (m_priv->table[current_header])[tag] = setting;
202 m_priv->log->debug("new tag: \"%s\" = \"%s\"",
203 tag.c_str(), setting.c_str());
207 } // until the file ends
210 // In case there are exceptions.
214 const std::string& ShibINI::get (const string& header, const string& tag)
218 static string empty = "";
228 if (!exists(h)) return empty;
230 map<string,string>::const_iterator i = m_priv->table[h].find(t);
231 if (i == m_priv->table[h].end())
236 bool ShibINI::exists(const std::string& header)
241 if (!m_priv->cs) to_lowercase (h);
243 return (m_priv->table.find(h) != m_priv->table.end());
246 bool ShibINI::exists(const std::string& header, const std::string& tag)
258 if (!exists(h)) return false;
259 return (m_priv->table[h].find(t) != m_priv->table[h].end());
262 bool ShibINI::get_tag (string& header, string& tag, bool try_general, string* result)
264 if (!result) return false;
268 if (exists (header, tag)) {
269 *result = get (header, tag);
272 if (try_general && exists (SHIBTARGET_GENERAL, tag)) {
273 *result = get (SHIBTARGET_GENERAL, tag);
280 void ShibINI::dump (ostream& os)
284 os << "File: " << m_priv->file << "\n";
285 os << "Case-Sensitive: " << ( m_priv->cs ? "Yes\n" : "No\n" );
286 os << "File Entries:\n";
288 for (map<string, map<string, string> >::const_iterator i = m_priv->table.begin();
289 i != m_priv->table.end(); i++) {
291 os << "[" << i->first << "]\n";
293 for (map<string,string>::const_iterator j=i->second.begin();
294 j != i->second.end(); j++) {
296 os << " " << j->first << " = " << j->second << "\n";
303 ShibINI::Iterator* ShibINI::header_iterator()
306 HeaderIterator* iter = new HeaderIterator(m_priv);
307 return (ShibINI::Iterator*) iter;
310 ShibINI::Iterator* ShibINI::tag_iterator(const std::string& header)
313 TagIterator* iter = new TagIterator(m_priv, header);
314 return (ShibINI::Iterator*) iter;
317 HeaderIterator::HeaderIterator (ShibINIPriv* inip)
323 const string* HeaderIterator::begin ()
325 iter = ini->table.begin();
326 if (iter == ini->table.end()) {
334 const string* HeaderIterator::next ()
339 if (iter == ini->table.end()) {
346 TagIterator::TagIterator (ShibINIPriv* inip, const string& headerp)
353 const string* TagIterator::begin ()
355 iter = ini->table[header].begin();
356 if (iter == ini->table[header].end()) {
364 const string* TagIterator::next ()
369 if (iter == ini->table[header].end()) {
376 bool ShibINI::boolean(string& value)
378 const char* v = value.c_str();
379 if (!strncasecmp (v, "on", 2) || !strncasecmp (v, "true", 4) || !strncmp(v, "1", 1))