1 /********************************************************************\
2 * guid.c -- globally unique ID implementation *
3 * Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.edu> *
4 * Copyright (C) 2014 Aaron Laws <dartmetrash@gmail.com> *
5 * *
6 * This program is free software; you can redistribute it and/or *
7 * modify it under the terms of the GNU General Public License as *
8 * published by the Free Software Foundation; either version 2 of *
9 * the License, or (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License*
17 * along with this program; if not, contact: *
18 * *
19 * Free Software Foundation Voice: +1-617-542-5942 *
20 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21 * Boston, MA 02110-1301, USA gnu@gnu.org *
22 * *
23 \********************************************************************/
24
25 #include "guid.hpp"
26 extern "C"
27 {
28
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
35 #endif
36 #include <ctype.h>
37 #include <stdint.h>
38 #ifdef HAVE_DIRENT_H
39 # include <dirent.h>
40 #endif
41 #include <glib.h>
42 #include <glib/gstdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/stat.h>
46 #ifdef HAVE_SYS_TIMES_H
47 # include <sys/times.h>
48 #endif
49 #include <time.h>
50 #ifdef HAVE_UNISTD_H
51 # include <unistd.h>
52 #endif
53 #include "qof.h"
54
55 }
56 #include <boost/uuid/uuid.hpp>
57 #include <boost/uuid/uuid_generators.hpp>
58 #include <boost/uuid/uuid_io.hpp>
59 #include <sstream>
60 #include <string>
61
62 /* This static indicates the debugging module that this .o belongs to. */
63 static QofLogModule log_module = QOF_MOD_ENGINE;
64
65 /**
66 * gnc_value_get_guid
67 *
68 * @param value a @c GValue whose value we want to get.
69 *
70 * @return the value stored in @a value
71 */
72 const GncGUID*
gnc_value_get_guid(const GValue * value)73 gnc_value_get_guid (const GValue *value)
74 {
75 if (!value) return nullptr;
76 GncGUID *val;
77
78 g_return_val_if_fail (value && G_IS_VALUE (value), NULL);
79 g_return_val_if_fail (GNC_VALUE_HOLDS_GUID (value), NULL);
80
81 val = (GncGUID*) g_value_get_boxed (value);
82
83 return val;
84 }
85
86 GncGUID * guid_convert_create (gnc::GUID const &);
87
88 static gnc::GUID s_null_guid {boost::uuids::uuid { {0}}};
89 static GncGUID * s_null_gncguid {guid_convert_create (s_null_guid)};
90
91 /* Memory management routines ***************************************/
92
93 /**
94 * Allocates and returns a new GncGUID containing the same value as the
95 * gnc::GUID passed in.
96 */
97 GncGUID *
guid_convert_create(gnc::GUID const & guid)98 guid_convert_create (gnc::GUID const & guid)
99 {
100 GncGUID temp = guid;
101 return guid_copy (&temp);
102 }
103
104 GncGUID *
guid_malloc(void)105 guid_malloc (void)
106 {
107 return new GncGUID;
108 }
109
110 void
guid_free(GncGUID * guid)111 guid_free (GncGUID *guid)
112 {
113 if (!guid) return;
114 if (guid == s_null_gncguid)
115 /* Don't delete that! */
116 return;
117 delete guid;
118 }
119
120 GncGUID *
guid_copy(const GncGUID * guid)121 guid_copy (const GncGUID *guid)
122 {
123 if (!guid) return nullptr;
124 auto ret = guid_malloc ();
125 memcpy (ret, guid, sizeof (GncGUID));
126 return ret;
127 }
128
129 /*It looks like we are expected to provide the same pointer every time from this function*/
130 const GncGUID *
guid_null(void)131 guid_null (void)
132 {
133 return s_null_gncguid;
134 }
135
136 static void
guid_assign(GncGUID & target,gnc::GUID const & source)137 guid_assign (GncGUID & target, gnc::GUID const & source)
138 {
139 memcpy (&target, &source, sizeof (GncGUID));
140 }
141
142 /*Takes an allocated guid pointer and constructs it in place*/
143 void
guid_replace(GncGUID * guid)144 guid_replace (GncGUID *guid)
145 {
146 if (!guid) return;
147 gnc::GUID temp_random {gnc::GUID::create_random ()};
148 guid_assign (*guid, temp_random);
149 }
150
151 GncGUID *
guid_new(void)152 guid_new (void)
153 {
154 auto ret = guid_new_return ();
155 return guid_copy (&ret);
156 }
157
158 GncGUID
guid_new_return(void)159 guid_new_return (void)
160 {
161 return gnc::GUID::create_random ();
162 }
163
164 gchar *
guid_to_string(const GncGUID * guid)165 guid_to_string (const GncGUID * guid)
166 {
167 if (!guid) return nullptr;
168 gnc::GUID temp {*guid};
169 auto temp_str = temp.to_string ();
170 return g_strdup (temp_str.c_str ());
171 }
172
173 gchar *
guid_to_string_buff(const GncGUID * guid,gchar * str)174 guid_to_string_buff (const GncGUID * guid, gchar *str)
175 {
176 if (!str || !guid) return NULL;
177
178 gnc::GUID temp {*guid};
179 auto val = temp.to_string ();
180 /*We need to be sure to copy the terminating null character.
181 * The standard guarantees that std::basic_string::c_str ()
182 * returns with a terminating null character, too.*/
183 std::copy (val.c_str (), val.c_str () + val.size () + 1, str);
184 return str + val.size ();
185 }
186
187 gboolean
string_to_guid(const char * str,GncGUID * guid)188 string_to_guid (const char * str, GncGUID * guid)
189 {
190 if (!guid || !str) return false;
191
192 try
193 {
194 guid_assign (*guid, gnc::GUID::from_string (str));
195 }
196 catch (...)
197 {
198 return false;
199 }
200 return true;
201 }
202
203 gboolean
guid_equal(const GncGUID * guid_1,const GncGUID * guid_2)204 guid_equal (const GncGUID *guid_1, const GncGUID *guid_2)
205 {
206 if (!guid_1 || !guid_2)
207 return !guid_1 && !guid_2;
208 gnc::GUID temp1 {*guid_1};
209 gnc::GUID temp2 {*guid_2};
210 return temp1 == temp2;
211 }
212
213 gint
guid_compare(const GncGUID * guid_1,const GncGUID * guid_2)214 guid_compare (const GncGUID *guid_1, const GncGUID *guid_2)
215 {
216 if (!guid_1 || !guid_2)
217 return !guid_1 && !guid_2;
218 gnc::GUID temp1 {*guid_1};
219 gnc::GUID temp2 {*guid_2};
220 if (temp1 < temp2)
221 return -1;
222 if (temp1 == temp2)
223 return 0;
224 return 1;
225 }
226
227 guint
guid_hash_to_guint(gconstpointer ptr)228 guid_hash_to_guint (gconstpointer ptr)
229 {
230 if (!ptr)
231 {
232 PERR ("received NULL guid pointer.");
233 return 0;
234 }
235 GncGUID const & guid = * reinterpret_cast <GncGUID const *> (ptr);
236 gnc::GUID const & temp {guid};
237
238 guint hash {0};
239 unsigned retspot {0};
240 std::for_each (temp.begin (), temp.end (), [&hash] (unsigned char a) {
241 hash <<=4;
242 hash |= a;
243 });
244 return hash;
245 }
246
247 gint
guid_g_hash_table_equal(gconstpointer guid_a,gconstpointer guid_b)248 guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
249 {
250 return guid_equal (reinterpret_cast<const GncGUID*> (guid_a),
251 reinterpret_cast<const GncGUID*> (guid_b));
252 }
253
254 GHashTable *
guid_hash_table_new(void)255 guid_hash_table_new (void)
256 {
257 return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
258 }
259
260 /***************************/
261 static void
gnc_string_to_guid(const GValue * src,GValue * dest)262 gnc_string_to_guid (const GValue *src, GValue *dest)
263 {
264 /* FIXME: add more checks*/
265 GncGUID *guid;
266 const gchar *as_string;
267
268 g_return_if_fail (G_VALUE_HOLDS_STRING (src) &&
269 GNC_VALUE_HOLDS_GUID (dest));
270
271 as_string = g_value_get_string (src);
272
273 guid = g_new0 (GncGUID, 1);
274 string_to_guid (as_string, guid);
275
276 g_value_take_boxed (dest, guid);
277 }
278
279 static void
gnc_guid_to_string(const GValue * src,GValue * dest)280 gnc_guid_to_string (const GValue *src, GValue *dest)
281 {
282 const gchar *str;
283
284 g_return_if_fail (G_VALUE_HOLDS_STRING (dest) &&
285 GNC_VALUE_HOLDS_GUID (src));
286
287 str = guid_to_string (gnc_value_get_guid (src));
288
289 g_value_set_string (dest, str);
290 }
291
292 GType
gnc_guid_get_type(void)293 gnc_guid_get_type (void)
294 {
295 static GType type = 0;
296
297 if (G_UNLIKELY (type == 0))
298 {
299 type = g_boxed_type_register_static ("GncGUID",
300 (GBoxedCopyFunc)guid_copy,
301 (GBoxedFreeFunc)guid_free);
302
303 g_value_register_transform_func (G_TYPE_STRING,
304 type,
305 gnc_string_to_guid);
306
307 g_value_register_transform_func (type,
308 G_TYPE_STRING,
309 gnc_guid_to_string);
310 }
311
312 return type;
313 }
314
315 namespace gnc
316 {
317
318 GUID
create_random()319 GUID::create_random () noexcept
320 {
321 static boost::uuids::random_generator gen;
322 return {gen ()};
323 }
324
GUID(boost::uuids::uuid const & other)325 GUID::GUID (boost::uuids::uuid const & other) noexcept
326 : implementation (other)
327 {
328 }
329
330 GUID const &
null_guid()331 GUID::null_guid () noexcept
332 {
333 return s_null_guid;
334 }
335
336 std::string
to_string() const337 GUID::to_string () const noexcept
338 {
339 auto const & val = boost::uuids::to_string (implementation);
340 std::string ret;
341 std::for_each (val.begin (), val.end (), [&ret] (char a) {
342 if (a != '-') ret.push_back (a);
343 });
344 return ret;
345 }
346
347 GUID
from_string(std::string const & str)348 GUID::from_string (std::string const & str)
349 {
350 try
351 {
352 static boost::uuids::string_generator strgen;
353 return strgen (str);
354 }
355 catch (...)
356 {
357 throw guid_syntax_exception {};
358 }
359 }
360
361 bool
is_valid_guid(std::string const & str)362 GUID::is_valid_guid (std::string const & str)
363 {
364 try
365 {
366 static boost::uuids::string_generator strgen;
367 auto a = strgen (str);
368 return true;
369 }
370 catch (...)
371 {
372 return false;
373 }
374 }
375
guid_syntax_exception()376 guid_syntax_exception::guid_syntax_exception () noexcept
377 : invalid_argument {"Invalid syntax for guid."}
378 {
379 }
380
GUID(GncGUID const & other)381 GUID::GUID (GncGUID const & other) noexcept
382 : implementation {{other.reserved[0] , other.reserved[1]
383 , other.reserved[2], other.reserved[3]
384 , other.reserved[4], other.reserved[5]
385 , other.reserved[6], other.reserved[7]
386 , other.reserved[8], other.reserved[9]
387 , other.reserved[10], other.reserved[11]
388 , other.reserved[12], other.reserved[13]
389 , other.reserved[14], other.reserved[15]}
390 }
391 {
392
393 }
394
395 auto
end() const396 GUID::end () const noexcept -> decltype (implementation.end ())
397 {
398 return implementation.end ();
399 }
400
401 auto
begin() const402 GUID::begin () const noexcept -> decltype (implementation.begin ())
403 {
404 return implementation.begin ();
405 }
406
407 bool
operator <(GUID const & other)408 GUID::operator < (GUID const & other) noexcept
409 {
410 return implementation < other.implementation;
411 }
412
operator ==(GUID const & lhs,GncGUID const & rhs)413 bool operator == (GUID const & lhs, GncGUID const & rhs) noexcept
414 {
415 return lhs.implementation == GUID(rhs).implementation;
416 }
417
418 bool
operator !=(GUID const & one,GUID const & two)419 operator != (GUID const & one, GUID const & two) noexcept
420 {
421 return one.implementation != two.implementation;
422 }
423
operator =(GUID && other)424 GUID & GUID::operator = (GUID && other) noexcept
425 {
426 boost::uuids::swap (other.implementation, implementation);
427 return *this;
428 }
429
operator GncGUID() const430 GUID::operator GncGUID () const noexcept
431 {
432 GncGUID ret;
433 guid_assign (ret, *this);
434 return ret;
435 }
436
437 } // namespace gnc
438