/* $Id$ * * This file is a part of Amp2p. Amp2p is Copyright 2004 Joseph * Curtis. jocurtis@gmail.com * * * Amp2p is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Amp2p is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Amp2p; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This prorgam contains some of the code from the hello world xmms * plugin by iso:crash. This is greatly appreciated because I had no * idea how to create an xmms plugin! All in xmms-amp2p.[ch] now. */ //#include #include #include #include #include #include #include #include #include #include #include #include #include #include /* need? */ #include #include #include #include #include #include "amp2p.h" #include "hashtable.h" #include "hashtable_itr.h" /* Global variables */ /* Nodes keep a list of nodes which they can connect to directly. * This is stored in this array of integers. The integers in the * array are ip addresses, both having a length of 4 bytes. Hopefully * this won't change in the future. There is a maximum of * MAX_NODELIST nodes in the nodelist. */ struct node nodelist[MAX_NODELIST] = {0}; /* IP address of this node */ unsigned int local_address=0; /* Must make sure all sockets are closed on cleanup of module! Here * is a possible solution... An array of global sockets and an index * to them. Index prob needs mutex. */ int sockets[MAX_SOCKETS]; /* socket index */ int sindex=0; struct hashtable *checksums; struct hashtable *reverse; /* reverse lookup for checksums */ /* Search results */ struct file * results; int resindex=0; int resmax=0; /* Array containing the five most recent queries received */ struct msg recent_queries[MAX_RECENT] = {0}; struct msg recent_queries_sent[MAX_RECENT] = {0}; int query_replies = 0; /* The server socket which listens for incoming data. For closing purposes. */ int data_sock; /* Array of chunks being currently downloaded. Only uniquely identified by checksum. From this you should be able to identify which parts of a file are missing (from starts and offsets). I sure pity the person who has to read/modify this code, it is horrible! */ struct file downloads[MAX_DOWNLOADS] = {0}; /* Index to the above array. Points to the next free spot in the array. */ int dindex = 0; void load_nodelist(char * file) { FILE * in; char * addr; int j; printf("Loading nodelist: %s\n", file); in = fopen(file, "r"); if(in == NULL) { fprintf(stderr, "Unable to load nodelist"); return; } j=0; addr = malloc(20); //TODO: Why 20? Because I said so! while(j < MAX_NODELIST && fgets(addr, 25, in) != NULL){ addr[strlen(addr)-1] = '\0'; // printf("addr: '%s'\n", addr); nodelist[j].addr = inet_addr(addr); j++; } fclose(in); return; } /* Prints errors to stderr then exits with a return value of -1 */ void error(char * error_string) { fprintf(stdout, "ERROR -- %s\n", error_string); exit(-1); } #ifdef DEBUG /* Prints debug statements to stderr */ void debug(char * debug_string) { fprintf(stderr, "%s\n", debug_string); } #endif /* Prints a message to stdout. */ void print_msg(struct msg m) { char type[15]; char * data; switch(m.type){ case QUERY: strcpy(type, "QUERY"); break; case REQUEST: strcpy(type, "REQUEST"); break; case DATA: strcpy(type, "DATA"); break; case PING: strcpy(type, "PING"); break; case PING_REPLY: strcpy(type, "PING_REPLY"); break; case QUERY_REPLY: strcpy(type, "QUERY_REPLY"); break; default: strcpy(type, "INVALID_TYPE"); break; } data = malloc(m.length+1); strncpy(data, m.data, m.length); data[m.length]='\0'; printf("Origin: %s, From: %s, To: %s, Type: %s, Length: %d, Data: %s\n", i2d(ntohl(m.origin)), i2d(ntohl(m.from)), i2d(ntohl(m.to)), type, m.length, data); return; } /* Takes a message in the format described above and converts it into * a message structure. */ struct msg decode_msg(unsigned char * buf) { struct msg m; int i=0; #ifdef DEBUG // debug("decode_msg: begin"); #endif /* Insert error checking here! */ /* There is most probably a much better and more elegant way to do this. I'll figure it out later. */ m.origin = (buf[0]&0xff)<<24|(buf[1]&0xff)<<16|(buf[2]&0xff)<<8|(buf[3]); m.from = (buf[4]&0xff)<<24|(buf[5]&0xff)<<16|(buf[6]&0xff)<<8|(buf[7]); m.to = (buf[8]&0xff)<<24|(buf[9]&0xff)<<16|(buf[10]&0xff)<<8|(buf[11]); m.type = (buf[12]&0xff)<<8|(buf[13]); m.length = (buf[14]&0xff)<<24|(buf[15]&0xff)<<16|(buf[16]&0xff)<<8|(buf[17]); m.data = malloc(m.length); for(i=0;i>((3-i)*8))&0xff; for(i=0;i<4;i++) buf[i+4]=(m.from>>((3-i)*8))&0xff; for(i=0;i<4;i++) buf[i+8]=(m.to>>((3-i)*8))&0xff; for(i=0;i<2;i++) buf[i+12]=(m.type>>((1-i)*8))&0xff; for(i=0;i<4;i++) buf[i+14]=(m.length>>((3-i)*8))&0xff; for(i=0;i 0){ // for(i=0;i0;j--){ recent_queries[j].origin = recent_queries[j-1].origin; recent_queries[j].from = recent_queries[j-1].from; recent_queries[j].to = recent_queries[j-1].to; recent_queries[j].type = recent_queries[j-1].type; recent_queries[j].length = recent_queries[j-1].length; if(recent_queries[j-1].data){ recent_queries[j].data = malloc(strlen(recent_queries[j-1].data)); strcpy(recent_queries[j].data, recent_queries[j-1].data); } } recent_queries[0].origin = m.origin; recent_queries[0].from = m.from; recent_queries[0].to = m.to; recent_queries[0].type = m.type; recent_queries[0].length = m.length; recent_queries[0].data = malloc(strlen(m.data)); strcpy(recent_queries[0].data, m.data); /* nicer way to do this? */ return; } add_recent_query_sent(struct msg m){ int j; /* Add the just received query to the recent queries */ for(j=MAX_RECENT-1;j>0;j--){ recent_queries_sent[j].origin = recent_queries_sent[j-1].origin; recent_queries_sent[j].from = recent_queries_sent[j-1].from; recent_queries_sent[j].to = recent_queries_sent[j-1].to; recent_queries_sent[j].type = recent_queries_sent[j-1].type; recent_queries_sent[j].length = recent_queries_sent[j-1].length; if(recent_queries_sent[j-1].data){ recent_queries_sent[j].data = malloc(strlen(recent_queries_sent[j-1].data)); strcpy(recent_queries_sent[j].data, recent_queries_sent[j-1].data); } } recent_queries_sent[0].origin = m.origin; recent_queries_sent[0].from = m.from; recent_queries_sent[0].to = m.to; recent_queries_sent[0].type = m.type; recent_queries_sent[0].length = m.length; recent_queries_sent[0].data = malloc(strlen(m.data)); strcpy(recent_queries_sent[0].data, m.data); /* nicer way to do this? */ return; } /* is the query represented by m in the recent queries list? */ int is_recent_query(struct msg m){ int i; printf("\n\nRECENT QUERY: %d, %s\n\n", m.length, m.data); for(i=0;i>((3-j)*8))&0xff; //printf("buf[%d] = %d ... %d\n", j, buf[j], file_len); } write(sock, buf, 4); /* write the length */ write(sock, m.data, file_len); /* write the metadata */ in = fopen(s, "r"); /* open from offset to offset + length - 1 */ fseek(in, req.offset, SEEK_SET); // printf("Sending chunks from %d to %d\n", req.offset, // req.offset + req.length - 1); printf("Sending to sock: %d", sock); while (ftell(in) < req.length + req.offset) { // printf(".", ftell(in), ftell(in) + DATA_SIZE); numchunks++; len = fread(buf, 1, BUFLEN, in); con_write(sock, buf, len); /* printf("Writing %d bytes to socket %d. File location is %dKB\n", len, sock, ftell(in)/1024); */ } //printf("%d chunks of %d bytes = %dKB\n", numchunks, // DATA_SIZE, (numchunks * DATA_SIZE) / 1024); fclose(in); close(sock); } } } while (hashtable_iterator_advance(i)); //TODO: Decode this. // printf("Request for %s, %dKB, %s\n", req.title, // req.length/1024, checksum_str(req.checksum)); // printf("cs: '%s', hs: %s\n", checksum_str(req.checksum), // hashtable_search(reverse, req.checksum)); if(hashtable_search(reverse, req.checksum) != NULL) printf("Found a match!\n"); #ifdef DEBUG debug("handle_file_request: end"); #endif return; } void handle_data(struct data * d) { unsigned char buf[BUFLEN] = {0}; char * file; struct file f; int c = 0; int b; int len; FILE * out; char * filename; char * path; int i; int listnum; int percent=0; int last_percent=0; #ifdef DEBUG debug("handle_data: begin"); #endif // TODO: Fix double download file fuxing /* Read in the first four bytes which correspond to an integer. This integer represents the length of the file metadata in the stream. */ b = con_read(d->sock, buf, 4); if(b != 4) { printf("Bad data\n"); return; /* bad data */ } // for(i=0;i<4;i++) // printf("buf[%d] = %d\n", i, buf[i]); // printf("\n"); len = (buf[0]&0xff)<<24|(buf[1]&0xff)<<16|(buf[2]&0xff)<<8|(buf[3]); file = malloc(len); if(len > BUFLEN) { printf("Buffer too small!\n"); return; } //printf("Len: %d\n", len); read(d->sock, file, len); f = decode_file(file); printf("Handling data from sock: %d (offset: %d, length: %d)\n", d->sock, f.offset, f.length); /* Add the chunk to the downloads array */ if (dindex < MAX_DOWNLOADS) { downloads[dindex]. } else { return; /* Whoops, max downloads. There is probably a better way to handle this! */ } // TODO: This is dodgy as, seperation of concerns man! if (!is_in_list(f, DLIST)) { listnum = list_append(f, DLIST); } else { // File is already downloading.... what do we do?? Check if we // already have this chunk... if not then add it to the file, // although the file will probably have a lock on it, well I guess // we wait. If we already have this chunk then we do nothing I // guess. /* How am I going to keep track of which parts of a file have been downloaded??? Really need a proper list of current downloads, not this clist crapulence! */ } path = malloc(100); strcpy(path, "/tmp/"); filename = malloc(strlen(f.name)+1); strcpy(filename, f.name); reverse_str(filename); strcpy(filename, strtok(filename, "/")); reverse_str(filename); path = realloc(path, strlen(path) + strlen(filename) + 2); //printf("Foo\n"); strcat(path, filename); //printf("Filename: '%s'", path); // return; out = fopen(path, "r+"); if (out == NULL) { out = fopen(path, "w"); } fseek(out, f.offset, SEEK_SET); printf("Retrieving socket %d\n", d->sock); while(b = read(d->sock, buf, BUFLEN)) { fwrite(buf, 1, b, out); c+=b; //printf("."); percent = (100 * c) / f.length; if(percent != last_percent){ update_download(listnum, last_percent); // fprintf(stderr, "%s: %d%%, %d%%\n", path, percent, last_percent); } last_percent = percent; } printf("\nTransfer of %s complete, %dKB total\n", f.title, c/1024); fclose(out); /* Calculate the file checksum, if it matches the checksum then yay, the file is done. If not then we need to figure out which chunks we need and aquire them pronto. Aquiring will be done via some file requests. Checking which parts of the file we need is another matter, some thought is required. */ #ifdef DEBUG debug("handle_data: end"); #endif return; } void handle_ping(struct msg m) { struct msg reply; #ifdef DEBUG debug("handle_ping: begin"); #endif reply.origin = m.origin; reply.from = local_addr(); reply.to = m.from; reply.type = PING_REPLY; reply.length = m.length; reply.data = m.data; send_msg(reply); #ifdef DEBUG debug("handle_ping: end"); #endif return; } void handle_ping_reply(struct msg m) { int diff; /* Round trip time in milliseconds. */ struct timeb * tp; int secs, millisecs; #ifdef DEBUG debug("handle_ping_reply: begin"); #endif /* w00t, we have a ping reply. Now, check the timestamps. Get the round trip time by subtracting timestamp in data from current timestamp. If ping is smaller than largest ping in nodelist then kick that node off! */ tp = malloc(sizeof(struct timeb)); ftime(tp); /* get current time */ sscanf(m.data, "%d:%d", &secs, &millisecs); /* get timestamp from ping. */ diff = ((tp->time-secs)*1000) + (tp->millitm-millisecs); #ifdef DEBUG fprintf(stderr, "Ping reply from '%s', %d milliseconds.\n", i2d(ntohl(m.origin)), diff); debug("handle_ping_reply: end"); #endif return; } /* Send a ping message to the node with ip address to. */ void ping(unsigned int to) { struct msg m; struct timeb * tp; char t[20]; tp = malloc(sizeof(struct timeb)); ftime(tp); sprintf(t, "%d:%d", (int)tp->time, tp->millitm); m.origin=m.from=local_addr(); //printf("%s\n", t); m.to=to; m.type=PING; m.data = malloc(strlen(t)); strcpy(m.data, t); /* Storing this as a string probably is not as efficient as I'd like */ m.length=(strlen(t)); send_msg(m); } /* return the ip address identifying the local host. */ unsigned int local_addr() { char hostname[100]; /* is this number large enough? */ struct hostent * h; struct in_addr addr; #ifdef DEBUG debug("local_addr: begin"); #endif /* This function does not work if there is no entry in /etc/hosts. This is bad!! This is a definite must fix! */ /* This whole business of setting a variable to the local address and using that is probably not so good for the situation where the address changes during execution. Will this ever be a problem?? */ if(!local_address){ gethostname(hostname, sizeof(hostname)); h = gethostbyname(hostname); memcpy(&addr, h->h_addr, sizeof(struct in_addr)); printf("local host name: '%s', addr: %s\n", hostname, inet_ntoa(addr)); local_address=addr.s_addr; #ifdef DEBUG debug("local_addr: end"); #endif return(addr.s_addr); }else { #ifdef DEBUG debug("local_addr: end"); #endif return(local_address); } } /* Data was originally going to be sent using UDP as well however on * closer inspection I realised that I would pretty much have to do * something TCP like to get it to work. SO I am just going to go * with TCP for data transfer. The TCP connection is going to be * between the origin of the file request and the node which is * sending the file. * * Whenever a file chunk is being received a new TCP connection will * be active. The data received will be written to the file by the * method write_file which makes sure only one thread is writing to * the file at once. */ void listen_for_data() { int sock, s; struct sockaddr_in serv_addr, cli_addr; int len; struct data d; sock = data_sock = socket(AF_INET, SOCK_STREAM, 0); if(sock < 0) error("Opening socket"); bzero((char *)&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(LISTEN_PORT); if(bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) error("Binding socket"); listen(sock, MAX_DOWNLOADS); while(1) { len = sizeof(cli_addr); s = accept(sock, (struct sockaddr *) &cli_addr, &len); if(s < 0) error("Accepting"); d.sock = s; bzero((char *)&d.addr, sizeof(d.addr)); memcpy(&d.addr, &cli_addr, sizeof(cli_addr)); create_thread((void *)&handle_data, (void *)&d); } } /* This is what the child process does all the time. Listen on the * correct port and do whatever is required of you. Service * search/file requests. */ void listen_for_requests() { struct msg m; char * testmsg; #ifdef DEBUG debug("listen_for_requests: begin"); #endif /* Initialise things before listening */ initial(); while (1) { /* Spawn a thread to handle the incoming data when it accepts or * whatever. That thread will then receive the data and maybe spawn * a different thread to handle that specific request. Or is that * redundant...? */ /* How am I going to handle the re-creation of threads after they * have finished handling a request. How am I going to decide * what i is? -- Hehe, you is stoopid... */ m = rcv_msg(); // print_msg(m); testmsg = encode_msg(m); //pthread_create(&request_handler[i++], NULL, (void *)&handle_request, &m); create_thread((void *)&handle_request, (void *)testmsg); usleep(1000); //handle_request(testmsg); } #ifdef DEBUG debug("listen_for_requests: end"); #endif exit_thread(); } /* Handle a request coming in over sock. Decodes the message into a * message structure, checks the type and invokes the appropriate * handler function. * * There have to be some definite routing rules in here!!! There are * generic cases when requests should be ignored. I think it is * correct to have the routing rules in here because they don't really * belong in with the individual request handlers. */ void handle_request(char * buf) { struct msg m; #ifdef DEBUG debug("handle_request: begin"); #endif m = decode_msg(buf); // print_msg(m); switch(m.type){ case QUERY: handle_query(m); break; case REQUEST: handle_file_request(m); break; case DATA: // handle_data(m); break; case PING: handle_ping(m); break; case PING_REPLY: handle_ping_reply(m); break; case QUERY_REPLY: handle_query_reply(m); break; default: /* It really doesn't seem to like this!! */ // error("handle_request: unknown message type!"); break; } #ifdef DEBUG debug("handle_request: end"); #endif exit_thread(); return; } /* Returns a string representation of the IP address in dotted quad * format. Whoops, use inet_ntoa instead. */ char * i2d(unsigned int address) { unsigned char quads[4]={0}; unsigned char * buf; int i=0; for(i=0;i<4;i++) quads[i]=(address>>((3-i)*8))&0xff; buf = malloc(16); sprintf(buf, "%u.%u.%u.%u", quads[0], quads[1], quads[2], quads[3]); return(buf); } /** * Send a message with all info specified in attributes, not a * structure. */ void _send_msg(unsigned int origin, unsigned int from, unsigned int to, short int type, int length, char * data) { struct msg m = {origin, from, to, type, length, 0}; #ifdef DEBUG debug("_send_msg: start"); #endif m.data = malloc(length); memcpy(m.data, data, length); send_msg(m); #ifdef DEBUG debug("_send_msg: end"); #endif return; } /** * Send out a file request for target. This gets tricky too in a * minute. We have to try and figure out how many people have replied * to us with a search result. This is done by counting the unique * number (say n) search results for this particular file. This data * is stored elsewhere (TODO: figure out where). We then divide the * file up into n regions and send out n file requests. Sounds good * eh? */ void download(struct file target, int count){ int i; int j = 0; int c; int offset=0; int length=0; c = 10; // TODO: Proper chunking /* Suppose we send out a file request for each chunk out to a different node. This should be propagated accross rhe network. When we are handling the incoming data then we can send out the rest of the file requests... Or we could just sent out one file request for the first chunk and take it from there. The possibilities are endless. */ length = target.length / c; printf("\nDividing into %d chunks of %d bytes...total: %d\n", c, length, target.length); for(i=0;i>((3-i)*8))&0xff; for(i=0;i<4;i++) data[i+4+j] = (f.length>>((3-i)*8))&0xff; *d = malloc(data_length); memcpy(*d, data, data_length); #ifdef DEBUG debug("encode_file: end"); #endif return(data_length); } /* Send a message, specifying all details of that message, including * origin and from. Low-level, should not be used directly. */ void send_msg(struct msg m) { int sd, rc; struct sockaddr_in cliAddr, remoteServAddr; struct hostent *h; unsigned char * buf; #ifdef DEBUG debug("send_msg: start"); #endif buf = malloc(m.length + HEADER_SIZE); memcpy(buf, encode_msg(m), m.length + HEADER_SIZE); h = gethostbyname("localhost"); /* printf("Sending data to '%s' (IP : %s) \n", h->h_name, inet_ntoa(*(struct in_addr *)h->h_addr_list[0])); */ remoteServAddr.sin_family = h->h_addrtype; /* memcpy((char *) &remoteServAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length); */ remoteServAddr.sin_addr.s_addr = m.to; remoteServAddr.sin_port = htons(LISTEN_PORT); /* socket creation */ sd = socket(AF_INET,SOCK_DGRAM,0); if(sd<0) { printf("cannot open socket \n"); exit(1); } /* bind any port */ cliAddr.sin_family = AF_INET; cliAddr.sin_addr.s_addr = htonl(INADDR_ANY); cliAddr.sin_port = htons(0); rc = bind(sd, (struct sockaddr *) &cliAddr, sizeof(cliAddr)); if(rc<0) { printf("cannot bind port\n"); exit(1); } rc = sendto(sd, buf ,m.length+18, 0, (struct sockaddr *) &remoteServAddr, sizeof(remoteServAddr)); if(rc<0) { printf("cannot send data\n"); close(sd); // exit(1); } #ifdef DEBUG debug("send_msg: end"); #endif return; } /* Create a UDP socket on LISTEN_PORT. Listen for a message, receive * it and return it. */ struct msg rcv_msg() { int sock_index=inc_sindex(1); /* get the index of the next socket. */ size_t len; struct sockaddr_in serv_name; char buf[PACK_SIZE]; struct msg m; #ifdef DEBUG debug("rcv_msg: start"); #endif bzero(&serv_name, sizeof(serv_name)); serv_name.sin_family = AF_INET; serv_name.sin_port = htons(LISTEN_PORT); if((sockets[sock_index] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) error("rcv_msg: socket opening"); //printf("%d, %p, %d\n", sockets[sock_index], serv_name, sizeof(serv_name)); if(bind(sockets[sock_index], (struct sockaddr *)&serv_name, sizeof(serv_name)) < 0) error("rcv_msg: binding socket"); len = sizeof(serv_name); recvfrom(sockets[sock_index], &buf, PACK_SIZE, 0, (struct sockaddr *)&serv_name, &len); close(sockets[sock_index]); inc_sindex(-1); /* release the socket index */ m = decode_msg(buf); // print_msg(m); #ifdef DEBUG debug("rcv_msg: end"); #endif return(m); } /* Convert a string ip address in dotted quad format to a 32 bit * integer. You had better make sure it is well formed cos there aint * no error checking! */ int dotted_to_int(char * dotted) { int address=0; return(address); } /* This is a stub because it is meant to be run as a plugin for winamp * and xmms. The abstraction for the interface to the plugins will * be done later, after the p2p stuff works properly. */ void amp2p_thread(void *args) { /* Have to do several things. First spawn a thread to listen for * requests. Then handle user interface stuff and user search/file * requests. */ char * to; int quit = 0; #ifdef DEBUG debug("amp2p_thread: begin"); #endif local_addr(); /* get the local address of this node */ /* do something now, like send a message!!*/ while(!quit){ // scanf("%s", to); // ping(inet_addr(to)); // printf("origin: %u\n", ntohl(inet_addr("192.168.0.1"))); // printf("origin: %u\n", ntohl(inet_addr("192.168.0.255"))); } #ifdef DEBUG debug("amp2p_thread: end"); #endif /* or return some other value depending on things */ return; } void initial(void) { int i; unsigned char s[MD5_DIGEST_LENGTH]; /* Initialise the hashtable of checksums */ checksums = create_hashtable(16, sdbm, equalkeys); reverse = create_hashtable(16, sdbm, equalkeys); // search_results = create_hashtable(16, sdbm, equalkeys); read_checksums(CHECKSUMS_FILE); load_nodelist(NODELIST); } void end(void) { write_checksums(CHECKSUMS_FILE); hashtable_destroy(checksums, 0); } /** * Read the checksums from a file into a hashtable. */ void read_checksums(char * file) { FILE * in; char * key; char * k; unsigned char val[MD5_DIGEST_LENGTH]; unsigned char * v; unsigned char all[216]; int end; char t; printf("Reading checksums: %s\n", file); in = fopen(file, "r"); if(in == NULL) { fprintf(stderr, "Unable to load checksums\n"); return; } fseek(in, 0, SEEK_END); end = ftell(in); rewind(in); printf("Generating reverse lookup."); while(ftell(in) < end){ key = malloc(1024); fgets(key, 1024, in); key[strlen(key)-1]='\0'; fread(val, MD5_DIGEST_LENGTH, 1, in); v = malloc(MD5_DIGEST_LENGTH); memcpy(v, val, MD5_DIGEST_LENGTH); hashtable_insert(checksums, key, v); hashtable_insert(reverse, v, key); putchar('.'); } fclose(in); printf(".done\n"); return; } /** * Write the checksums to a file. */ void write_checksums(char * file) { struct hashtable_itr * i; FILE * o; int j; unsigned char *temp; char * key; int length; /* Write the hashtable to disk. Is this wise? Yes! */ i = hashtable_iterator(checksums); if(hashtable_count(checksums)>0){ o = fopen(file, "w"); if(o == NULL) { fprintf(stderr, "Unable to write checksums\n"); return; } do { key = malloc(length = strlen(hashtable_iterator_key(i))); memcpy(key, hashtable_iterator_key(i), length); fwrite(key, length, 1, o); fwrite("\n", 1, 1, o); temp = malloc(MD5_DIGEST_LENGTH); memcpy(temp, hashtable_iterator_value(i), MD5_DIGEST_LENGTH); fwrite(temp, MD5_DIGEST_LENGTH, 1, o); }while(hashtable_iterator_advance(i)); fclose(o); } } /** * Print to stdout the md5 checksum represented by c. */ void print_checksum(unsigned char * c) { int j; unsigned char s[MD5_DIGEST_LENGTH]; memcpy(s, c, MD5_DIGEST_LENGTH); for(j=0;j.@cl.cam.ac.uk) whose hashtable code this is * (see hashtable.c, hashtable.h and hashtable_private.h). */ static unsigned long sdbm(unsigned char *str) { unsigned long hash = 0; int c; while (c = *str++) hash = c + (hash << 6) + (hash << 16) - hash; return hash; } static int equalkeys(void *k1, void *k2) { return (0 == memcmp(k1,k2,strlen(k1))); } /* Thx to K&R */ void reverse_str(char s[]) { int c, i, j; for (i=0, j=strlen(s)-1; i < j;i++, j--) { c = s[i]; s[i] = s [j]; s[j] = c; } }