/* -*- Mode: c; c-basic-offset: 2 -*- * * rdf_digest.c - RDF Digest Factory / Digest implementation * * Copyright (C) 2000-2008, David Beckett http://www.dajobe.org/ * Copyright (C) 2000-2005, Copyright (C) 2000-2006, Copyright (C) 2000-2006, University of Bristol, UK http://www.bristol.ac.uk/ * * This package is Free Software and part of Redland http://librdf.org/ * * It is licensed under the following three licenses as alternatives: * 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version * 2. GNU General Public License (GPL) V2 or any newer version * 3. Apache License, V2.0 or any newer version * * You may not use this file except in compliance with at least one of * the above three licenses. * * See LICENSE.html or LICENSE.txt at the top of this package for the * complete terms and further detail along with the license texts for * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively. * * */ #ifdef HAVE_CONFIG_H #include #endif #ifdef WIN32 #include #endif #include #include #include #ifdef HAVE_STDLIB_H #include /* for abort() as used in errors */ #endif #include #ifndef STANDALONE /* prototypes for helper functions */ static void librdf_delete_digest_factories(librdf_world* world); /* helper functions */ static void librdf_delete_digest_factories(librdf_world *world) { librdf_digest_factory *factory, *next; for(factory=world->digests; factory; factory=next) { next=factory->next; LIBRDF_FREE(librdf_digest_factory, factory->name); LIBRDF_FREE(librdf_digest_factory, factory); } world->digests=NULL; } /** * librdf_digest_register_factory: * @world: redland world object * @name: the name of the hash * @factory: function to be called to register the factory parameters * * Register a hash factory. * **/ void librdf_digest_register_factory(librdf_world *world, const char *name, void (*factory) (librdf_digest_factory*)) { librdf_digest_factory *digest; librdf_world_open(world); #if defined(LIBRDF_DEBUG) && LIBRDF_DEBUG > 1 LIBRDF_DEBUG2("Received registration for digest %s\n", name); #endif for(digest = world->digests; digest; digest = digest->next ) { if(!strcmp(digest->name, name)) { librdf_log(world, 0, LIBRDF_LOG_ERROR, LIBRDF_FROM_DIGEST, NULL, "digest %s already registered", digest->name); return; } } digest=(librdf_digest_factory*)LIBRDF_CALLOC(librdf_digest_factory, 1, sizeof(librdf_digest_factory)); if(!digest) goto oom; digest->name=(char*)LIBRDF_MALLOC(cstring, strlen(name)+1); if(!digest->name) goto oom_tidy; strcpy(digest->name, name); digest->next = world->digests; world->digests = digest; /* Call the digest registration function on the new object */ (*factory)(digest); #if defined(LIBRDF_DEBUG) && LIBRDF_DEBUG > 1 LIBRDF_DEBUG4("%s has context size %d and digest size %d\n", name, digest->context_length, digest->digest_length); #endif return; oom_tidy: LIBRDF_FREE(librdf_digest, digest); oom: LIBRDF_FATAL1(world, LIBRDF_FROM_DIGEST, "Out of memory"); } /** * librdf_get_digest_factory: * @world: redland world object * @name: the name of the factory * * Get a digest factory. * * Return value: the factory or NULL if not found **/ librdf_digest_factory* librdf_get_digest_factory(librdf_world *world, const char *name) { librdf_digest_factory *factory; librdf_world_open(world); /* return 1st digest if no particular one wanted - why? */ if(!name) { factory=world->digests; if(!factory) { LIBRDF_DEBUG1("No digests available\n"); return NULL; } } else { for(factory=world->digests; factory; factory=factory->next) { if(!strcmp(factory->name, name)) break; } /* else FACTORY with name digest_name not found -> return NULL */ } return factory; } /** * librdf_new_digest: * @world: redland world object * @name: the digest name to use to create this digest * * Constructor - create a new #librdf_digest object. * * After construction, data should be added to the digest using * #librdf_digest_update or #librdf_digest_update_string with * #librdf_digest_final to signify finishing. Then the digest * value can be returned directly with #librdf_digest_get_digest * of #librdf_digest_get_digest_length bytes or as a hex encoded * string with #librdf_digest_to_string. The digest can be * re-initialised for new data with #librdf_digest_init. * * Return value: new #librdf_digest object or NULL **/ librdf_digest* librdf_new_digest(librdf_world *world, const char *name) { librdf_digest_factory* factory; librdf_world_open(world); factory=librdf_get_digest_factory(world, name); if(!factory) return NULL; return librdf_new_digest_from_factory(world, factory); } /** * librdf_new_digest_from_factory: * @world: redland world object * @factory: the digest factory to use to create this digest * * Constructor - create a new #librdf_digest object. * * After construction, data should be added to the digest using * #librdf_digest_update or #librdf_digest_update_string with * #librdf_digest_final to signify finishing. Then the digest * value can be returned directly with #librdf_digest_get_digest * of #librdf_digest_get_digest_length bytes or as a hex encoded * string with #librdf_digest_to_string. The digest can be * re-initialised for new data with #librdf_digest_init. * * Return value: new #librdf_digest object or NULL **/ librdf_digest* librdf_new_digest_from_factory(librdf_world *world, librdf_digest_factory *factory) { librdf_digest* d; librdf_world_open(world); d=(librdf_digest*)LIBRDF_CALLOC(librdf_digest, 1, sizeof(librdf_digest)); if(!d) return NULL; d->world=world; d->context=(char*)LIBRDF_CALLOC(digest_context, 1, factory->context_length); if(!d->context) { librdf_free_digest(d); return NULL; } d->digest=(unsigned char*)LIBRDF_CALLOC(digest_digest, 1, factory->digest_length); if(!d->digest) { librdf_free_digest(d); return NULL; } d->factory=factory; d->factory->init(d->context); return d; } /** * librdf_free_digest: * @digest: the digest * * Destructor- destroy a #librdf_digest object. * **/ void librdf_free_digest(librdf_digest *digest) { if(digest->context) LIBRDF_FREE(digest_context, digest->context); if(digest->digest) LIBRDF_FREE(digest_digest, digest->digest); LIBRDF_FREE(librdf_digest, digest); } /* methods */ /** * librdf_digest_init: * @digest: the digest * * (Re)initialise the librdf_digest object. * * This is automatically called on construction but can be used to * re-initialise the digest to the initial state for digesting new * data. **/ void librdf_digest_init(librdf_digest* digest) { digest->factory->init(digest->context); } /** * librdf_digest_update: * @digest: the digest * @buf: the data buffer * @length: the length of the data * * Add more data to the librdf_digest object. * **/ void librdf_digest_update(librdf_digest* digest, const unsigned char *buf, size_t length) { digest->factory->update(digest->context, buf, length); } /** * librdf_digest_update_string: * @digest: the digest * @string: string to add * * Add a string to the librdf_digest object. * **/ void librdf_digest_update_string(librdf_digest* digest, const unsigned char *string) { digest->factory->update(digest->context, string, strlen((const char*)string)); } /** * librdf_digest_final: * @digest: the digest * * Finish the digesting of data. * * The digest can now be returned via librdf_digest_get_digest(). **/ void librdf_digest_final(librdf_digest* digest) { void* digest_data; digest->factory->final(digest->context); digest_data=(*(digest->factory->get_digest))(digest->context); memcpy(digest->digest, digest_data, digest->factory->digest_length); } /** * librdf_digest_get_digest: * @digest: the digest * * Get the calculated digested value. * * Return value: pointer to the memory containing the digest. It will * be #librdf_digest_get_digest_length bytes in length. * **/ void* librdf_digest_get_digest(librdf_digest* digest) { return digest->digest; } /** * librdf_digest_get_digest_length: * @digest: the digest * * Get length of the calculated digested. * * Return value: size of the digest in bytes * **/ size_t librdf_digest_get_digest_length(librdf_digest* digest) { return digest->factory->digest_length; } /** * librdf_digest_to_string: * @digest: the digest * * Get a string representation of the digest object. * * Return value: a newly allocated string that represents the digest. * This must be released by the caller using free() **/ char * librdf_digest_to_string(librdf_digest* digest) { unsigned char* data=digest->digest; int mdlen=digest->factory->digest_length; char* b; int i; b=(char*)LIBRDF_MALLOC(cstring, 1+(mdlen<<1)); if(!b) LIBRDF_FATAL1(digest->world, LIBRDF_FROM_DIGEST, "Out of memory"); for(i=0; idigest_factory=librdf_get_digest_factory(world, world->digest_factory_name); } /** * librdf_finish_digest: * @world: redland world object * * INTERNAL - Terminate the digest module. * **/ void librdf_finish_digest(librdf_world *world) { librdf_delete_digest_factories(world); } #endif #ifdef STANDALONE /* one more prototype */ int main(int argc, char *argv[]); struct t { const char *type; const char *result; }; int main(int argc, char *argv[]) { librdf_digest* d; const char *test_data="http://purl.org/net/dajobe/"; struct t test_data_answers[]={ {"MD5", "80b52def747e8748199c1a0cf66cb35c"}, {"SHA1", "67d6a7b73504ce5c6b47fd6db9b7c4939bfe5174"}, {"RIPEMD160", "83ce259f0c23642a95fc92fade583e6d8e15784d"}, {NULL, NULL}, }; int failures=0; int i; struct t *answer=NULL; const char *program=librdf_basename((const char*)argv[0]); librdf_world *world; world=librdf_new_world(); for(i=0; ((answer= &test_data_answers[i]) && answer->type != NULL) ; i++) { char *s; fprintf(stdout, "%s: Trying to create new %s digest\n", program, answer->type); d=librdf_new_digest(world, answer->type); if(!d) { fprintf(stderr, "%s: Failed to create new digest type %s\n", program, answer->type); continue; } fprintf(stdout, "%s: Initialising digest type %s\n", program, answer->type); librdf_digest_init(d); fprintf(stdout, "%s: Writing data into digest\n", program); librdf_digest_update(d, (unsigned char*)test_data, strlen(test_data)); fprintf(stdout, "%s: Finishing digesting\n", program); librdf_digest_final(d); fprintf(stdout, "%s: %s digest of data is: ", program, answer->type); librdf_digest_print(d, stdout); fputc('\n', stdout); s=librdf_digest_to_string(d); if(strcmp(s, answer->result)) { fprintf(stderr, "%s: %s digest is wrong - expected: %s\n", program, answer->type, answer->result); failures++; } else fprintf(stderr, "%s: %s digest is correct\n", program, answer->type); LIBRDF_FREE(cstring, s); fprintf(stdout, "%s: Freeing digest\n", program); librdf_free_digest(d); } librdf_free_world(world); /* keep gcc -Wall happy */ return failures; } #endif