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