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