00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #ifdef HAVE_CONFIG_H
00025 # include <config.h>
00026 #endif
00027
00028 #ifdef HAVE_SYS_TYPES_H
00029 # include <sys/types.h>
00030 #endif
00031 #include <ctype.h>
00032 #include <dirent.h>
00033 #include <glib.h>
00034 #include <glib/gstdio.h>
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include <sys/stat.h>
00038 #ifdef HAVE_SYS_TIMES_H
00039 # include <sys/times.h>
00040 #endif
00041 #include <time.h>
00042 #include <unistd.h>
00043 #include "qof.h"
00044 #include "md5.h"
00045
00046 # ifndef P_tmpdir
00047 # define P_tmpdir "/tmp"
00048 # endif
00049
00050
00051 #define DEBUG_GUID 0
00052 #define BLOCKSIZE 4096
00053 #define THRESHOLD (2 * BLOCKSIZE)
00054
00055
00056
00057 static gboolean guid_initialized = FALSE;
00058 static struct md5_ctx guid_context;
00059 #ifndef HAVE_GLIB29
00060 static GMemChunk *guid_memchunk = NULL;
00061 #endif
00062
00063
00064 static QofLogModule log_module = QOF_MOD_ENGINE;
00065
00072 G_CONST_RETURN GUID*
00073 gnc_value_get_guid (const GValue *value)
00074 {
00075 GUID *val;
00076
00077 g_return_val_if_fail (value && G_IS_VALUE (value), NULL);
00078 g_return_val_if_fail (GNC_VALUE_HOLDS_GUID (value), NULL);
00079
00080 val = (GUID*) g_value_get_boxed (value);
00081
00082 return val;
00083 }
00084
00085
00086
00087 #ifdef HAVE_GLIB29
00088 GUID *
00089 guid_malloc (void)
00090 {
00091 return g_slice_new(GUID);
00092 }
00093
00094 void
00095 guid_free (GUID *guid)
00096 {
00097 if (!guid)
00098 return;
00099
00100 g_slice_free(GUID, guid);
00101 }
00102 #else
00103
00104 static void
00105 guid_memchunk_init (void)
00106 {
00107 if (!guid_memchunk)
00108 guid_memchunk = g_mem_chunk_create (GUID, 512, G_ALLOC_AND_FREE);
00109 }
00110
00111 static void
00112 guid_memchunk_shutdown (void)
00113 {
00114 if (guid_memchunk)
00115 {
00116 g_mem_chunk_destroy (guid_memchunk);
00117 guid_memchunk = NULL;
00118 }
00119 }
00120
00121 GUID *
00122 guid_malloc (void)
00123 {
00124 if (!guid_memchunk) guid_memchunk_init();
00125 return g_chunk_new (GUID, guid_memchunk);
00126 }
00127
00128 void
00129 guid_free (GUID *guid)
00130 {
00131 if (!guid)
00132 return;
00133
00134 g_chunk_free (guid, guid_memchunk);
00135 }
00136 #endif
00137
00138
00139 GUID *
00140 guid_copy (const GUID *guid)
00141 {
00142 GUID *copy;
00143
00144 g_return_val_if_fail(guid, NULL);
00145 copy = guid_malloc();
00146 *copy = *guid;
00147 return copy;
00148 }
00149
00150 const GUID *
00151 guid_null(void)
00152 {
00153 static int null_inited = 0;
00154 static GUID null_guid;
00155
00156 if (!null_inited) {
00157 int i;
00158
00159 for (i = 0; i < GUID_DATA_SIZE; i++)
00160 null_guid.data[i] = '\0';
00161
00162 null_inited = 1;
00163 }
00164
00165 return &null_guid;
00166 }
00167
00168
00169
00170
00171 static size_t
00172 init_from_stream(FILE *stream, size_t max_size)
00173 {
00174 char buffer[BLOCKSIZE + 72];
00175 size_t sum, block_size, total;
00176
00177 if (max_size <= 0)
00178 return 0;
00179
00180 total = 0;
00181
00182
00183 while (1)
00184 {
00185
00186
00187
00188 size_t n;
00189 sum = 0;
00190
00191 if (max_size < BLOCKSIZE)
00192 block_size = max_size;
00193 else
00194 block_size = BLOCKSIZE;
00195
00196
00197 do
00198 {
00199 n = fread (buffer + sum, 1, block_size - sum, stream);
00200
00201 sum += n;
00202 }
00203 while (sum < block_size && n != 0);
00204
00205 max_size -= sum;
00206
00207 if (n == 0 && ferror (stream))
00208 return total;
00209
00210
00211 if ((n == 0) || (max_size == 0))
00212 break;
00213
00214
00215
00216 md5_process_block (buffer, BLOCKSIZE, &guid_context);
00217
00218 total += sum;
00219 }
00220
00221
00222 if (sum > 0)
00223 {
00224 md5_process_bytes (buffer, sum, &guid_context);
00225 total += sum;
00226 }
00227
00228 return total;
00229 }
00230
00231 static size_t
00232 init_from_file(const char *filename, size_t max_size)
00233 {
00234 struct stat stats;
00235 size_t total = 0;
00236 size_t file_bytes;
00237 FILE *fp;
00238
00239 memset(&stats, 0, sizeof(stats));
00240 if (g_stat(filename, &stats) != 0)
00241 return 0;
00242
00243 md5_process_bytes(&stats, sizeof(stats), &guid_context);
00244 total += sizeof(stats);
00245
00246 if (max_size <= 0)
00247 return total;
00248
00249 fp = g_fopen (filename, "r");
00250 if (fp == NULL)
00251 return total;
00252
00253 file_bytes = init_from_stream(fp, max_size);
00254
00255 PINFO ("guid_init got %llu bytes from %s", (unsigned long long int) file_bytes,
00256 filename);
00257
00258 total += file_bytes;
00259
00260 fclose(fp);
00261
00262 return total;
00263 }
00264
00265 static size_t
00266 init_from_dir(const char *dirname, unsigned int max_files)
00267 {
00268 char filename[1024];
00269 const gchar *de;
00270 struct stat stats;
00271 size_t total;
00272 int result;
00273 GDir *dir;
00274
00275 if (max_files <= 0)
00276 return 0;
00277
00278 dir = g_dir_open(dirname, 0, NULL);
00279 if (dir == NULL)
00280 return 0;
00281
00282 total = 0;
00283
00284 do
00285 {
00286 de = g_dir_read_name(dir);
00287 if (de == NULL)
00288 break;
00289
00290 md5_process_bytes(de, strlen(de), &guid_context);
00291 total += strlen(de);
00292
00293 result = snprintf(filename, sizeof(filename),
00294 "%s/%s", dirname, de);
00295 if ((result < 0) || (result >= (int)sizeof(filename)))
00296 continue;
00297
00298 memset(&stats, 0, sizeof(stats));
00299 if (g_stat(filename, &stats) != 0)
00300 continue;
00301 md5_process_bytes(&stats, sizeof(stats), &guid_context);
00302 total += sizeof(stats);
00303
00304 max_files--;
00305 } while (max_files > 0);
00306
00307 g_dir_close(dir);
00308
00309 return total;
00310 }
00311
00312 static size_t
00313 init_from_time(void)
00314 {
00315 size_t total;
00316 time_t t_time;
00317 #ifdef HAVE_SYS_TIMES_H
00318 clock_t clocks;
00319 struct tms tms_buf;
00320 #endif
00321
00322 total = 0;
00323
00324 t_time = time(NULL);
00325 md5_process_bytes(&t_time, sizeof(t_time), &guid_context);
00326 total += sizeof(t_time);
00327
00328 #ifdef HAVE_SYS_TIMES_H
00329 clocks = times(&tms_buf);
00330 md5_process_bytes(&clocks, sizeof(clocks), &guid_context);
00331 md5_process_bytes(&tms_buf, sizeof(tms_buf), &guid_context);
00332 total += sizeof(clocks) + sizeof(tms_buf);
00333 #endif
00334
00335 return total;
00336 }
00337
00338 static size_t
00339 init_from_int(int val)
00340 {
00341 md5_process_bytes(&val, sizeof(val), &guid_context);
00342 return sizeof(int);
00343 }
00344
00345 static size_t
00346 init_from_buff(unsigned char * buf, size_t buflen)
00347 {
00348 md5_process_bytes(buf, buflen, &guid_context);
00349 return buflen;
00350 }
00351
00352 void
00353 guid_init(void)
00354 {
00355 size_t bytes = 0;
00356
00357
00358
00359
00360 md5_init_ctx(&guid_context);
00361
00362
00363 bytes += init_from_file ("/dev/urandom", 512);
00364
00365
00366 {
00367 const char * files[] =
00368 { "/etc/passwd",
00369 "/proc/loadavg",
00370 "/proc/meminfo",
00371 "/proc/net/dev",
00372 "/proc/rtc",
00373 "/proc/self/environ",
00374 "/proc/self/stat",
00375 "/proc/stat",
00376 "/proc/uptime",
00377 NULL
00378 };
00379 int i;
00380
00381 for (i = 0; files[i] != NULL; i++)
00382 bytes += init_from_file(files[i], BLOCKSIZE);
00383 }
00384
00385
00386 {
00387 const char * dirname;
00388 const char * dirs[] =
00389 {
00390 "/proc",
00391 P_tmpdir,
00392 "/var/lock",
00393 "/var/log",
00394 "/var/mail",
00395 "/var/spool/mail",
00396 "/var/run",
00397 NULL
00398 };
00399 int i;
00400
00401 for (i = 0; dirs[i] != NULL; i++)
00402 bytes += init_from_dir(dirs[i], 32);
00403
00404 dirname = g_get_home_dir();
00405 if (dirname != NULL)
00406 bytes += init_from_dir(dirname, 32);
00407 }
00408
00409
00410 {
00411 pid_t pid;
00412
00413 pid = getpid();
00414 md5_process_bytes(&pid, sizeof(pid), &guid_context);
00415 bytes += sizeof(pid);
00416
00417 #ifdef HAVE_GETPPID
00418 pid = getppid();
00419 md5_process_bytes(&pid, sizeof(pid), &guid_context);
00420 bytes += sizeof(pid);
00421 #endif
00422 }
00423
00424
00425 {
00426 #ifdef HAVE_GETUID
00427 uid_t uid;
00428 gid_t gid;
00429 char *s;
00430
00431 s = getlogin();
00432 if (s != NULL)
00433 {
00434 md5_process_bytes(s, strlen(s), &guid_context);
00435 bytes += strlen(s);
00436 }
00437
00438 uid = getuid();
00439 md5_process_bytes(&uid, sizeof(uid), &guid_context);
00440 bytes += sizeof(uid);
00441
00442 gid = getgid();
00443 md5_process_bytes(&gid, sizeof(gid), &guid_context);
00444 bytes += sizeof(gid);
00445 #endif
00446 }
00447
00448
00449 {
00450 #ifdef HAVE_GETHOSTNAME
00451 char string[1024];
00452
00453 memset(string, 0, sizeof(string));
00454 gethostname(string, sizeof(string));
00455 md5_process_bytes(string, sizeof(string), &guid_context);
00456 bytes += sizeof(string);
00457 #endif
00458 }
00459
00460
00461 {
00462 int n, i;
00463
00464 srand((unsigned int) time(NULL));
00465
00466 for (i = 0; i < 32; i++)
00467 {
00468 n = rand();
00469
00470 md5_process_bytes(&n, sizeof(n), &guid_context);
00471 bytes += sizeof(n);
00472 }
00473 }
00474
00475
00476 bytes += init_from_time();
00477
00478 PINFO ("got %llu bytes", (unsigned long long int) bytes);
00479
00480 if (bytes < THRESHOLD)
00481 PWARN("only got %llu bytes.\n"
00482 "The identifiers might not be very random.\n",
00483 (unsigned long long int)bytes);
00484
00485 guid_initialized = TRUE;
00486 }
00487
00488 void
00489 guid_init_with_salt(const void *salt, size_t salt_len)
00490 {
00491 guid_init();
00492
00493 md5_process_bytes(salt, salt_len, &guid_context);
00494 }
00495
00496 void
00497 guid_init_only_salt(const void *salt, size_t salt_len)
00498 {
00499 md5_init_ctx(&guid_context);
00500
00501 md5_process_bytes(salt, salt_len, &guid_context);
00502
00503 guid_initialized = TRUE;
00504 }
00505
00506 void
00507 guid_shutdown (void)
00508 {
00509 #ifndef HAVE_GLIB29
00510 guid_memchunk_shutdown();
00511 #endif
00512 }
00513
00514 #define GUID_PERIOD 5000
00515
00516 void
00517 guid_new(GUID *guid)
00518 {
00519 static int counter = 0;
00520 struct md5_ctx ctx;
00521
00522 if (guid == NULL)
00523 return;
00524
00525 if (!guid_initialized)
00526 guid_init();
00527
00528
00529 ctx = guid_context;
00530 md5_finish_ctx(&ctx, guid->data);
00531
00532
00533 init_from_time();
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546 init_from_int (433781*counter);
00547 init_from_buff (guid->data, GUID_DATA_SIZE);
00548
00549 if (counter == 0)
00550 {
00551 FILE *fp;
00552
00553 fp = g_fopen ("/dev/urandom", "r");
00554 if (fp == NULL)
00555 return;
00556
00557 init_from_stream(fp, 32);
00558
00559 fclose(fp);
00560
00561 counter = GUID_PERIOD;
00562 }
00563
00564 counter--;
00565 }
00566
00567 GUID
00568 guid_new_return(void)
00569 {
00570 GUID guid;
00571
00572 guid_new (&guid);
00573
00574 return guid;
00575 }
00576
00577
00578 static void
00579 encode_md5_data(const unsigned char *data, char *buffer)
00580 {
00581 size_t count;
00582
00583 for (count = 0; count < GUID_DATA_SIZE; count++, buffer += 2)
00584 sprintf(buffer, "%02x", data[count]);
00585 }
00586
00587
00588
00589
00590 static gboolean
00591 decode_md5_string(const unsigned char *string, unsigned char *data)
00592 {
00593 unsigned char n1, n2;
00594 size_t count = -1;
00595 unsigned char c1, c2;
00596
00597 if (NULL == data) return FALSE;
00598 if (NULL == string) goto badstring;
00599
00600 for (count = 0; count < GUID_DATA_SIZE; count++)
00601 {
00602
00603 if ((0==string[2*count]) || (0==string[2*count+1])) goto badstring;
00604
00605 c1 = tolower(string[2 * count]);
00606 if (!isxdigit(c1)) goto badstring;
00607
00608 c2 = tolower(string[2 * count + 1]);
00609 if (!isxdigit(c2)) goto badstring;
00610
00611 if (isdigit(c1))
00612 n1 = c1 - '0';
00613 else
00614 n1 = c1 - 'a' + 10;
00615
00616 if (isdigit(c2))
00617 n2 = c2 - '0';
00618 else
00619 n2 = c2 - 'a' + 10;
00620
00621 data[count] = (n1 << 4) | n2;
00622 }
00623 return TRUE;
00624
00625 badstring:
00626 for (count = 0; count < GUID_DATA_SIZE; count++)
00627 {
00628 data[count] = 0;
00629 }
00630 return FALSE;
00631 }
00632
00633
00634
00635 const char *
00636 guid_to_string(const GUID * guid)
00637 {
00638 #ifdef G_THREADS_ENABLED
00639 static GStaticPrivate guid_buffer_key = G_STATIC_PRIVATE_INIT;
00640 gchar *string;
00641
00642 string = g_static_private_get (&guid_buffer_key);
00643 if (string == NULL) {
00644 string = malloc(GUID_ENCODING_LENGTH+1);
00645 g_static_private_set (&guid_buffer_key, string, g_free);
00646 }
00647 #else
00648 static char string[64];
00649 #endif
00650
00651 encode_md5_data(guid->data, string);
00652 string[GUID_ENCODING_LENGTH] = '\0';
00653
00654 return string;
00655 }
00656
00657 char *
00658 guid_to_string_buff(const GUID * guid, char *string)
00659 {
00660 if (!string || !guid) return NULL;
00661
00662 encode_md5_data(guid->data, string);
00663
00664 string[GUID_ENCODING_LENGTH] = '\0';
00665 return &string[GUID_ENCODING_LENGTH];
00666 }
00667
00668 gboolean
00669 string_to_guid(const char * string, GUID * guid)
00670 {
00671 return decode_md5_string(string, (guid != NULL) ? guid->data : NULL);
00672 }
00673
00674 gboolean
00675 guid_equal(const GUID *guid_1, const GUID *guid_2)
00676 {
00677 if (guid_1 && guid_2)
00678 return (memcmp(guid_1, guid_2, GUID_DATA_SIZE) == 0);
00679 else
00680 return FALSE;
00681 }
00682
00683 gint
00684 guid_compare(const GUID *guid_1, const GUID *guid_2)
00685 {
00686 if (guid_1 == guid_2)
00687 return 0;
00688
00689
00690 if (!guid_1 && guid_2)
00691 return -1;
00692
00693 if (guid_1 && !guid_2)
00694 return 1;
00695
00696 return memcmp (guid_1, guid_2, GUID_DATA_SIZE);
00697 }
00698
00699 guint
00700 guid_hash_to_guint (gconstpointer ptr)
00701 {
00702 const GUID *guid = ptr;
00703
00704 if (!guid)
00705 {
00706 PERR ("received NULL guid pointer.");
00707 return 0;
00708 }
00709
00710 if (sizeof(guint) <= sizeof(guid->data))
00711 {
00712 return (*((guint *) guid->data));
00713 }
00714 else
00715 {
00716 guint hash = 0;
00717 unsigned int i, j;
00718
00719 for (i = 0, j = 0; i < sizeof(guint); i++, j++) {
00720 if (j == GUID_DATA_SIZE) j = 0;
00721
00722 hash <<= 4;
00723 hash |= guid->data[j];
00724 }
00725
00726 return hash;
00727 }
00728 }
00729
00730 static gint
00731 guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
00732 {
00733 return guid_equal (guid_a, guid_b);
00734 }
00735
00736 GHashTable *
00737 guid_hash_table_new (void)
00738 {
00739 return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
00740 }
00741
00742
00743 static void
00744 gnc_string_to_guid (const GValue *src, GValue *dest)
00745 {
00746
00747 GUID *guid;
00748 const gchar *as_string;
00749
00750 g_return_if_fail (G_VALUE_HOLDS_STRING (src) &&
00751 GNC_VALUE_HOLDS_GUID (dest));
00752
00753 as_string = g_value_get_string (src);
00754
00755 guid = g_new0 (GUID, 1);
00756 string_to_guid(as_string, guid);
00757
00758 g_value_take_boxed (dest, guid);
00759 }
00760
00761 static void
00762 gnc_guid_to_string (const GValue *src, GValue *dest)
00763 {
00764 const gchar *str;
00765
00766 g_return_if_fail (G_VALUE_HOLDS_STRING (dest) &&
00767 GNC_VALUE_HOLDS_GUID (src));
00768
00769 str = guid_to_string(gnc_value_get_guid (src));
00770
00771 g_value_set_string (dest, str);
00772 }
00773
00774 GType
00775 gnc_guid_get_type (void)
00776 {
00777 static GType type = 0;
00778
00779 if (G_UNLIKELY (type == 0)) {
00780 type = g_boxed_type_register_static ("GUID",
00781 (GBoxedCopyFunc)guid_copy,
00782 (GBoxedFreeFunc)guid_free);
00783
00784 g_value_register_transform_func (G_TYPE_STRING,
00785 type,
00786 gnc_string_to_guid);
00787
00788 g_value_register_transform_func (type,
00789 G_TYPE_STRING,
00790 gnc_guid_to_string);
00791 }
00792
00793 return type;
00794 }