1 /* gobby - A GTKmm driven libobby client
2  * Copyright (C) 2005, 2006 0x539 dev group
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public
15  * License along with this program; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 // For mkdir / CreateDirectory
20 #ifdef WIN32
21 #include <windows.h>
22 #else
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <cerrno>
26 #endif
27 
28 #include <cstring>
29 #include <stdexcept>
30 
31 #include <glibmm/miscutils.h>
32 #include <glibmm/fileutils.h>
33 #include <glibmm/exception.h>
34 #include <libxml++/parsers/domparser.h>
35 #include <libxml++/exceptions/exception.h>
36 
37 #include <glib/gstdio.h>
38 
39 #include "config.hpp"
40 
41 namespace
42 {
43 	// Creates a new directory
create_directory(const char * path)44 	void create_directory(const char* path)
45 	{
46 #ifdef WIN32
47 		if(CreateDirectoryA(path, NULL) == FALSE)
48 		{
49 			LPVOID msgbuf;
50 			DWORD err = GetLastError();
51 
52 			FormatMessageA(
53 				FORMAT_MESSAGE_ALLOCATE_BUFFER |
54 				FORMAT_MESSAGE_FROM_SYSTEM,
55 				NULL,
56 				err,
57 				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
58 				reinterpret_cast<LPSTR>(&msgbuf),
59 				0,
60 				NULL
61 			);
62 
63 			std::string error_message = static_cast<LPSTR>(msgbuf);
64 			LocalFree(msgbuf);
65 
66 			// TODO: Convert to UTF-8?
67 
68 			throw Gobby::Config::Error(
69 				Gobby::Config::Error::PATH_CREATION_FAILED,
70 				"Could not create directory " +
71 				std::string(path) + ": " + error_message
72 			);
73 		}
74 #else
75 		if(mkdir(path, 0755) == -1)
76 		{
77 			throw Gobby::Config::Error(
78 				Gobby::Config::Error::PATH_CREATION_FAILED,
79 				"Could not create directory " +
80 				std::string(path) + ": " + strerror(errno)
81 			);
82 		}
83 #endif
84 	}
85 
create_path_to(const std::string & to)86 	void create_path_to(const std::string& to)
87 	{
88 		// Directory exists, nothing to do
89 		if(Glib::file_test(to, Glib::FILE_TEST_IS_DIR) )
90 			return;
91 
92 		// Find path to the directory to create
93 		Glib::ustring path_to = Glib::path_get_dirname(to);
94 
95 		// Create this path, if it doesn't exists
96 		create_path_to(path_to);
97 
98 		// Create new directory
99 		create_directory(to.c_str() );
100 	}
101 
102 	template<typename Map>
ptrmap_find(const Map & map,const typename Map::key_type & key)103 	typename Map::mapped_type ptrmap_find(const Map& map,
104 	                                      const typename Map::key_type& key)
105 	{
106 		typename Map::const_iterator iter = map.find(key);
107 		if(iter == map.end() ) return NULL;
108 		return iter->second;
109 	}
110 }
111 
Error(Code error_code,const Glib::ustring & error_message)112 Gobby::Config::Error::Error(Code error_code,
113                             const Glib::ustring& error_message):
114 	Glib::Error(
115 		g_quark_from_static_string("GOBBY_CONFIG_ERROR"),
116 		static_cast<int>(error_code),
117 		error_message
118 	)
119 {
120 }
121 
code() const122 Gobby::Config::Error::Code Gobby::Config::Error::code() const
123 {
124 	return static_cast<Code>(gobject_->code);
125 }
126 
Entry(const Glib::ustring & name)127 Gobby::Config::Entry::Entry(const Glib::ustring& name):
128 	m_name(name)
129 {
130 }
131 
get_name() const132 const Glib::ustring& Gobby::Config::Entry::get_name() const
133 {
134 	return m_name;
135 }
136 
ParentEntry(const Glib::ustring & name)137 Gobby::Config::ParentEntry::ParentEntry(const Glib::ustring& name):
138 	Entry(name)
139 {
140 }
141 
ParentEntry(const xmlpp::Element & elem)142 Gobby::Config::ParentEntry::ParentEntry(const xmlpp::Element& elem):
143 	Entry(elem.get_name() )
144 {
145 	xmlpp::Node::NodeList list = elem.get_children();
146 	for(xmlpp::Node::NodeList::iterator iter = list.begin();
147 	    iter != list.end();
148 	    ++ iter)
149 	{
150 		xmlpp::Element* child = dynamic_cast<xmlpp::Element*>(*iter);
151 		if(child == NULL) continue;
152 
153 		if(child->get_child_text() &&
154 		   !child->get_child_text()->is_white_space())
155 		{
156 			ValueEntry* entry = new TypedValueEntry<Glib::ustring>(
157 				*child
158 			);
159 
160 			m_map[child->get_name()] = entry;
161 		}
162 		else
163 		{
164 			m_map[child->get_name()] = new ParentEntry(*child);
165 		}
166 	}
167 }
168 
~ParentEntry()169 Gobby::Config::ParentEntry::~ParentEntry()
170 {
171 	for(map_type::iterator iter = m_map.begin();
172 	    iter != m_map.end();
173 	    ++ iter)
174 	{
175 		delete iter->second;
176 	}
177 }
178 
save(xmlpp::Element & elem) const179 void Gobby::Config::ParentEntry::save(xmlpp::Element& elem) const
180 {
181 	for(map_type::const_iterator iter = m_map.begin();
182 	    iter != m_map.end();
183 	    ++ iter)
184 	{
185 		Entry* entry = iter->second;
186 		xmlpp::Element* child = elem.add_child(entry->get_name() );
187 		entry->save(*child);
188 	}
189 }
190 
191 Gobby::Config::Entry* Gobby::Config::ParentEntry::
get_child(const Glib::ustring & name)192 	get_child(const Glib::ustring& name)
193 {
194 	return ptrmap_find(m_map, name);
195 }
196 
197 const Gobby::Config::Entry* Gobby::Config::ParentEntry::
get_child(const Glib::ustring & name) const198 	get_child(const Glib::ustring& name) const
199 {
200 	return ptrmap_find(m_map, name);
201 }
202 
203 Gobby::Config::ParentEntry* Gobby::Config::ParentEntry::
get_parent_child(const Glib::ustring & name)204 	get_parent_child(const Glib::ustring& name)
205 {
206 	return dynamic_cast<ParentEntry*>(get_child(name) );
207 }
208 
209 const Gobby::Config::ParentEntry* Gobby::Config::ParentEntry::
get_parent_child(const Glib::ustring & name) const210 	get_parent_child(const Glib::ustring& name) const
211 {
212 	return dynamic_cast<const ParentEntry*>(get_child(name) );
213 }
214 
215 Gobby::Config::ValueEntry* Gobby::Config::ParentEntry::
get_value_child(const Glib::ustring & name)216 	get_value_child(const Glib::ustring& name)
217 {
218 	return dynamic_cast<ValueEntry*>(get_child(name) );
219 }
220 
221 const Gobby::Config::ValueEntry* Gobby::Config::ParentEntry::
get_value_child(const Glib::ustring & name) const222 	get_value_child(const Glib::ustring& name) const
223 {
224 	return dynamic_cast<const ValueEntry*>(get_child(name) );
225 }
226 
227 Gobby::Config::ParentEntry& Gobby::Config::ParentEntry::
operator [](const Glib::ustring & name)228 	operator[](const Glib::ustring& name)
229 {
230 	ParentEntry* entry = get_parent_child(name);
231 	if(entry != NULL) return *entry;
232 	return set_parent(name);
233 }
234 
235 Gobby::Config::ParentEntry& Gobby::Config::ParentEntry::
set_parent(const Glib::ustring & name)236 	set_parent(const Glib::ustring& name)
237 {
238 	Entry* entry = get_child(name);
239 	if(entry != NULL) delete entry;
240 
241 	ParentEntry* child = new ParentEntry(name);
242 	m_map[name] = child;
243 	return *child;
244 }
245 
begin()246 Gobby::Config::ParentEntry::iterator Gobby::Config::ParentEntry::begin()
247 {
248 	return iterator(m_map.begin() );
249 }
250 
251 Gobby::Config::ParentEntry::const_iterator Gobby::Config::ParentEntry::
begin() const252 	begin() const
253 {
254 	return const_iterator(m_map.begin() );
255 }
256 
end()257 Gobby::Config::ParentEntry::iterator Gobby::Config::ParentEntry::end()
258 {
259 	return iterator(m_map.end() );
260 }
261 
262 Gobby::Config::ParentEntry::const_iterator Gobby::Config::ParentEntry::
end() const263 	end() const
264 {
265 	return const_iterator(m_map.end() );
266 }
267 
Config(const Glib::ustring & file,const Glib::ustring & old_file)268 Gobby::Config::Config(const Glib::ustring& file,
269                       const Glib::ustring& old_file):
270 	m_filename(file)
271 {
272 	xmlpp::DomParser parser;
273 	const Glib::ustring* actual_file = &file;
274 
275 	if(!Glib::file_test(file, Glib::FILE_TEST_IS_REGULAR))
276 	{
277 		if (Glib::file_test(old_file, Glib::FILE_TEST_IS_REGULAR))
278 		{
279 			actual_file = &old_file;
280 		}
281 		else
282 		{
283 			m_root.reset(new ParentEntry("gobby_config") );
284 			return;
285 		}
286 	}
287 
288 	try
289 	{
290 		parser.parse_file(*actual_file);
291 		if (actual_file != &file)
292 			g_unlink(old_file.c_str());
293 	}
294 	catch(xmlpp::exception& e)
295 	{
296 		// Empty config
297 		m_root.reset(new ParentEntry("gobby_config") );
298 		return;
299 	}
300 
301 	xmlpp::Document* document = parser.get_document();
302 	if(document == NULL)
303 	{
304 		m_root.reset(new ParentEntry("gobby_config") );
305 		return;
306 	}
307 
308 	xmlpp::Element* root = document->get_root_node();
309 
310 	// Config is present, but contains no root node
311 	if(root == NULL)
312 	{
313 		m_root.reset(new ParentEntry("gobby_config") );
314 		return;
315 	}
316 
317 	m_root.reset(new ParentEntry(*root) );
318 }
319 
~Config()320 Gobby::Config::~Config()
321 {
322 	xmlpp::Document document;
323 	xmlpp::Element* root = document.create_root_node("gobby_config");
324 	m_root->save(*root);
325 
326 	try
327 	{
328 		Glib::ustring dirname = Glib::path_get_dirname(m_filename);
329 		create_path_to(dirname);
330 
331 		document.write_to_file_formatted(m_filename, "UTF-8");
332 	}
333 	catch(Glib::Exception& e)
334 	{
335 		g_warning("Could not write config file: %s", e.what().c_str() );
336 	}
337 	catch(std::exception& e)
338 	{
339 		g_warning("Could not write config file: %s", e.what() );
340 	}
341 }
342 
get_root()343 Gobby::Config::ParentEntry& Gobby::Config::get_root()
344 {
345 	return *m_root;
346 }
347 
get_root() const348 const Gobby::Config::ParentEntry& Gobby::Config::get_root() const
349 {
350 	return *m_root;
351 }
352 
353 std::string serialise::default_context_to<Gdk::Color>::
to_string(const data_type & from) const354 	to_string(const data_type& from) const
355 {
356 	unsigned int red = from.get_red() * 255 / 65535;
357 	unsigned int green = from.get_green() * 255 / 65535;
358 	unsigned int blue = from.get_blue() * 255 / 65535;
359 
360 	std::stringstream stream;
361 	stream << std::hex << ( (red << 16) | (green << 8) | blue);
362 	return stream.str();
363 }
364 
365 serialise::default_context_from<Gdk::Color>::data_type
366 serialise::default_context_from<Gdk::Color>::
from_string(const std::string & from) const367 	from_string(const std::string& from) const
368 {
369 	unsigned int rgb_color;
370 	std::stringstream stream(from);
371 	stream >> std::hex >> rgb_color;
372 
373 	if(stream.bad() )
374 	{
375 		throw conversion_error(
376 			from + " should be hexadecimal color triple"
377 		);
378 	}
379 
380 	Gdk::Color color;
381 	color.set_red( ((rgb_color >> 16) & 0xff) * 65535 / 255);
382 	color.set_green( ((rgb_color >> 8) & 0xff) * 65535 / 255);
383 	color.set_blue( ((rgb_color) & 0xff) * 65535 / 255);
384 	return color;
385 }
386 
387 std::string serialise::default_context_to<Glib::ustring>::
to_string(const data_type & from) const388 	to_string(const data_type& from) const
389 {
390 	return from;
391 }
392 
393 serialise::default_context_from<Glib::ustring>::data_type
394 serialise::default_context_from<Glib::ustring>::
from_string(const std::string & from) const395 	from_string(const std::string& from) const
396 {
397 	return from;
398 }
399