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