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