3bc8137fed39da69a91c8853bd41df6d84c689b4
[oweals/gnunet.git] / src / util / pseudonym.c
1 /*
2      This file is part of GNUnet
3      (C) 2003, 2004, 2005, 2006, 2007, 2008 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file util/pseudonym.c
23  * @brief helper functions
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_container_lib.h"
30 #include "gnunet_disk_lib.h"
31 #include "gnunet_pseudonym_lib.h"
32 #include "gnunet_bio_lib.h"
33
34 /** 
35  * FIXME
36  */
37 #define PS_METADATA_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms/metadata" DIR_SEPARATOR_STR
38
39 /** 
40  * FIXME
41  */
42 #define PS_NAMES_DIR    DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms/names"    DIR_SEPARATOR_STR
43
44
45 /** 
46  * FIXME
47  */
48 struct DiscoveryCallback
49 {
50
51   /** 
52    * FIXME
53    */
54   struct DiscoveryCallback *next;
55
56   /** 
57    * FIXME
58    */
59   GNUNET_PSEUDONYM_Iterator callback;
60
61   /** 
62    * FIXME
63    */
64   void *closure;
65 };
66
67
68 /** 
69  * FIXME
70  */
71 static struct DiscoveryCallback *head;
72
73 /**
74  * Internal notification about new tracked URI.
75  * 
76  * FIXME
77  */
78 static void
79 internal_notify (const GNUNET_HashCode * id,
80                  const struct GNUNET_CONTAINER_MetaData *md, int rating)
81 {
82   struct DiscoveryCallback *pos;
83
84   pos = head;
85   while (pos != NULL)
86     {
87       pos->callback (pos->closure, id, md, rating);
88       pos = pos->next;
89     }
90 }
91
92 /**
93  * Register callback to be invoked whenever we discover
94  * a new pseudonym.
95  * 
96  * FIXME
97  */
98 int
99 GNUNET_PSEUDONYM_discovery_callback_register (const struct
100                                               GNUNET_CONFIGURATION_Handle
101                                               *cfg,
102                                               GNUNET_PSEUDONYM_Iterator
103                                               iterator, void *closure)
104 {
105   struct DiscoveryCallback *list;
106
107   list = GNUNET_malloc (sizeof (struct DiscoveryCallback));
108   list->callback = iterator;
109   list->closure = closure;
110   list->next = head;
111   head = list;
112   GNUNET_PSEUDONYM_list_all (cfg, iterator, closure);
113   return GNUNET_OK;
114 }
115
116 /**
117  * Unregister pseudonym discovery callback.
118  * 
119  * FIXME
120  */
121 int
122 GNUNET_PSEUDONYM_discovery_callback_unregister (GNUNET_PSEUDONYM_Iterator
123                                                 iterator, void *closure)
124 {
125   struct DiscoveryCallback *prev;
126   struct DiscoveryCallback *pos;
127
128   prev = NULL;
129   pos = head;
130   while ((pos != NULL) &&
131          ((pos->callback != iterator) || (pos->closure != closure)))
132     {
133       prev = pos;
134       pos = pos->next;
135     }
136   if (pos == NULL)
137     return GNUNET_SYSERR;
138   if (prev == NULL)
139     head = pos->next;
140   else
141     prev->next = pos->next;
142   GNUNET_free (pos);
143   return GNUNET_OK;
144 }
145
146
147 /**
148  * Get the filename (or directory name) for the given
149  * pseudonym identifier and directory prefix.
150  * 
151  * FIXME
152  */
153 static char *
154 get_data_filename (const struct GNUNET_CONFIGURATION_Handle
155                    *cfg, const char *prefix, const GNUNET_HashCode * psid)
156 {
157   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
158
159   if (psid != NULL)
160     GNUNET_CRYPTO_hash_to_enc (psid, &enc);
161   return GNUNET_DISK_get_home_filename (cfg,
162                                         GNUNET_CLIENT_SERVICE_NAME,
163                                         prefix,
164                                         (psid ==
165                                          NULL) ? NULL : (const char *) &enc,
166                                         NULL);
167 }
168
169
170 /**
171  * FIXME
172  */
173 static void
174 write_pseudonym_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
175                       const GNUNET_HashCode * nsid,
176                       const struct GNUNET_CONTAINER_MetaData *meta,
177                       int32_t ranking, const char *ns_name)
178 {
179   char *fn;
180   struct GNUNET_BIO_WriteHandle *fileW;
181
182   fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
183   GNUNET_assert (fn != NULL);
184   fileW = GNUNET_BIO_write_open(fn);
185   if (NULL != fileW) 
186     {
187       if ( (GNUNET_OK != GNUNET_BIO_write_int32(fileW, ranking)) ||
188            (GNUNET_OK != GNUNET_BIO_write_string(fileW, ns_name)) ||
189            (GNUNET_OK != GNUNET_BIO_write_meta_data(fileW, meta)) )
190         {
191           GNUNET_BIO_write_close(fileW);
192           GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fileW));
193           GNUNET_free (fn);
194           return;
195         }
196       if (GNUNET_OK != GNUNET_BIO_write_close(fileW))
197         {
198           GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fileW));
199           GNUNET_free (fn);
200           return;
201         }
202     }
203   GNUNET_free (fn);
204   /* create entry for pseudonym name in names */
205   GNUNET_free_non_null (GNUNET_PSEUDONYM_id_to_name (cfg, nsid));
206 }
207
208
209 /**
210  * FIXME
211  */
212 static int
213 read_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
214            const GNUNET_HashCode * nsid,
215            struct GNUNET_CONTAINER_MetaData **meta,
216            int32_t * ranking, char **ns_name)
217 {
218   char *fn;
219   char *emsg;
220   int ret;
221   fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
222   GNUNET_assert (fn != NULL);
223   struct GNUNET_BIO_ReadHandle *fileR;
224   fileR = GNUNET_BIO_read_open(fn);
225   if((NULL != fileR)&&
226      (GNUNET_OK == GNUNET_BIO_read_int32__(fileR, "Read int32 error!", ranking))&&
227      (GNUNET_OK == GNUNET_BIO_read_string(fileR, "Read string error!", ns_name, 200))&&
228      (GNUNET_OK == GNUNET_BIO_read_meta_data(fileR, "Read meta data error!", meta))&&
229      (GNUNET_OK == GNUNET_BIO_read_close(fileR, &emsg)))
230   ret = GNUNET_OK;
231   else
232   ret = GNUNET_SYSERR;
233   GNUNET_free (fn);
234   return ret;
235 }
236
237
238
239 /**
240  * Return the unique, human readable name for the given namespace.
241  *
242  * @param cfg configuration 
243  * @param nsid cryptographic ID of the namespace
244  * @return NULL on failure (should never happen)
245  */
246 char *
247 GNUNET_PSEUDONYM_id_to_name (const struct GNUNET_CONFIGURATION_Handle *cfg,
248                              const GNUNET_HashCode * nsid)
249 {
250   struct GNUNET_CONTAINER_MetaData *meta;
251   char *name;
252   GNUNET_HashCode nh;
253   char *fn;
254   uint64_t len;
255   struct GNUNET_DISK_FileHandle *fh;
256   unsigned int i;
257   unsigned int idx;
258   char *ret;
259   struct stat sbuf;
260   int32_t temp = 0;
261   int32_t *rank = &temp;
262
263   meta = NULL;
264   name = NULL;
265   if (GNUNET_OK == read_info (cfg, nsid, &meta, rank, &name))
266     {
267       if ((meta != NULL) && (name == NULL))
268         name = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
269                                                               EXTRACTOR_TITLE,
270                                                               EXTRACTOR_FILENAME,
271                                                               EXTRACTOR_DESCRIPTION,
272                                                               EXTRACTOR_SUBJECT,
273                                                               EXTRACTOR_PUBLISHER,
274                                                               EXTRACTOR_AUTHOR,
275                                                               EXTRACTOR_COMMENT,
276                                                               EXTRACTOR_SUMMARY,
277                                                               EXTRACTOR_OWNER,
278                                                               -1);
279       if (meta != NULL)
280         {
281           GNUNET_CONTAINER_meta_data_destroy (meta);
282           meta = NULL;
283         }
284     }
285   if (name == NULL)
286     name = GNUNET_strdup (_("no-name"));
287   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
288   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
289   GNUNET_assert (fn != NULL);
290
291   len = 0;
292   if (0 == STAT (fn, &sbuf))
293     GNUNET_DISK_file_size (fn, &len, GNUNET_YES);
294   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_CREATE
295                               | GNUNET_DISK_OPEN_READWRITE,
296                               GNUNET_DISK_PERM_USER_READ |
297                               GNUNET_DISK_PERM_USER_WRITE);
298   i = 0;
299   idx = -1;
300   while ((len >= sizeof (GNUNET_HashCode)) &&
301          (sizeof (GNUNET_HashCode)
302           == GNUNET_DISK_file_read (fh, &nh, sizeof (GNUNET_HashCode))))
303     {
304       if (0 == memcmp (&nh, nsid, sizeof (GNUNET_HashCode)))
305         {
306           idx = i;
307           break;
308         }
309       i++;
310       len -= sizeof (GNUNET_HashCode);
311     }
312   if (idx == -1)
313     {
314       idx = i;
315       if (sizeof (GNUNET_HashCode) !=
316           GNUNET_DISK_file_write (fh, nsid, sizeof (GNUNET_HashCode)))
317         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
318     }
319   GNUNET_DISK_file_close (fh);
320   ret = GNUNET_malloc (strlen (name) + 32);
321   GNUNET_snprintf (ret, strlen (name) + 32, "%s-%u", name, idx);
322   GNUNET_free (name);
323   GNUNET_free (fn);
324   return ret;
325 }
326
327 /**
328  * Get the namespace ID belonging to the given namespace name.
329  *
330  * @param cfg configuration to use
331  * @param ns_uname human-readable name for the namespace
332  * @param nsid set to namespace ID based on 'ns_uname'
333  * @return GNUNET_OK on success
334  */
335 int
336 GNUNET_PSEUDONYM_name_to_id (const struct GNUNET_CONFIGURATION_Handle *cfg,
337                              const char *ns_uname, GNUNET_HashCode * nsid)
338 {
339   size_t slen;
340   uint64_t len;
341   unsigned int idx;
342   char *name;
343   GNUNET_HashCode nh;
344   char *fn;
345   struct GNUNET_DISK_FileHandle *fh;
346
347   idx = -1;
348   slen = strlen (ns_uname);
349   while ((slen > 0) && (1 != sscanf (&ns_uname[slen - 1], "-%u", &idx)))
350     slen--;
351   if (slen == 0)
352     return GNUNET_SYSERR;
353   name = GNUNET_strdup (ns_uname);
354   name[slen - 1] = '\0';
355   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
356   GNUNET_free (name);
357   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
358   GNUNET_assert (fn != NULL);
359
360   if ((GNUNET_OK != GNUNET_DISK_file_test (fn) ||
361        (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES))) ||
362       ((idx + 1) * sizeof (GNUNET_HashCode) > len))
363     {
364       GNUNET_free (fn);
365       return GNUNET_SYSERR;
366     }
367   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_CREATE
368                               | GNUNET_DISK_OPEN_READWRITE,
369                               GNUNET_DISK_PERM_USER_READ |
370                               GNUNET_DISK_PERM_USER_WRITE);
371   GNUNET_free (fn);
372   GNUNET_DISK_file_seek (fh, idx * sizeof (GNUNET_HashCode),
373                          GNUNET_DISK_SEEK_SET);
374   if (sizeof (GNUNET_HashCode) !=
375       GNUNET_DISK_file_read (fh, nsid, sizeof (GNUNET_HashCode)))
376     {
377       GNUNET_DISK_file_close (fh);
378       return GNUNET_SYSERR;
379     }
380   GNUNET_DISK_file_close (fh);
381   return GNUNET_OK;
382 }
383
384
385
386 /**
387  * FIXME
388  */
389 struct ListPseudonymClosure
390 {
391
392   /**
393    * FIXME
394    */
395   GNUNET_PSEUDONYM_Iterator iterator;
396
397   /**
398    * FIXME
399    */
400   void *closure;
401
402   /**
403    * FIXME
404    */
405   const struct GNUNET_CONFIGURATION_Handle *cfg;
406 };
407
408
409
410 /**
411  * FIXME
412  */
413 static int
414 list_pseudonym_helper (void *cls, const char *fullname)
415 {
416   struct ListPseudonymClosure *c = cls;
417   int ret;
418   GNUNET_HashCode id;
419   int rating;
420   struct GNUNET_CONTAINER_MetaData *meta;
421   const char *fn;
422   char *str;
423
424   if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
425     return GNUNET_OK;
426   fn =
427     &fullname[strlen (fullname) + 1 -
428               sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
429   if (fn[-1] != DIR_SEPARATOR)
430     return GNUNET_OK;
431   ret = GNUNET_OK;
432   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (fn, &id))
433     return GNUNET_OK;           /* invalid name */
434   str = NULL;
435   if (GNUNET_OK != read_info (c->cfg, &id, &meta, &rating, &str))
436     return GNUNET_OK;           /* ignore entry */
437   GNUNET_free_non_null (str);
438   if (c->iterator != NULL)
439     ret = c->iterator (c->closure, &id, meta, rating);
440   GNUNET_CONTAINER_meta_data_destroy (meta);
441   return ret;
442 }
443
444
445 /**
446  * List all available pseudonyms.
447  *
448  * @param cfg overall configuration 
449  * @param iterator function to call for each pseudonym
450  * @param closure closure for iterator
451  * @return number of pseudonyms found
452  */
453 int
454 GNUNET_PSEUDONYM_list_all (const struct GNUNET_CONFIGURATION_Handle *cfg,
455                            GNUNET_PSEUDONYM_Iterator iterator, void *closure)
456 {
457   struct ListPseudonymClosure cls;
458   char *fn;
459   int ret;
460
461   cls.iterator = iterator;
462   cls.closure = closure;
463   cls.cfg = cfg;
464   fn = get_data_filename (cfg, PS_METADATA_DIR, NULL);
465   GNUNET_assert (fn != NULL);
466   GNUNET_DISK_directory_create (fn);
467   ret = GNUNET_DISK_directory_scan (fn, &list_pseudonym_helper, &cls);
468   GNUNET_free (fn);
469   return ret;
470 }
471
472 /**
473  * Change the ranking of a pseudonym.
474  *
475  * @param cfg overall configuration
476  * @param nsid id of the pseudonym
477  * @param delta by how much should the rating be
478  *  changed?
479  * @return new rating of the pseudonym
480  */
481 int
482 GNUNET_PSEUDONYM_rank (const struct GNUNET_CONFIGURATION_Handle *cfg,
483                        const GNUNET_HashCode * nsid, int delta)
484 {
485   struct GNUNET_CONTAINER_MetaData *meta;
486   int ret;
487   int32_t ranking;
488   char *name;
489
490   name = NULL;
491   ret = read_info (cfg, nsid, &meta, &ranking, &name);
492   if (ret == GNUNET_SYSERR)
493     {
494       ranking = 0;
495       meta = GNUNET_CONTAINER_meta_data_create ();
496     }
497   ranking += delta;
498   write_pseudonym_info (cfg, nsid, meta, ranking, name);
499   GNUNET_CONTAINER_meta_data_destroy (meta);
500   GNUNET_free_non_null (name);
501   return ranking;
502 }
503
504 /**
505  * Insert metadata into existing MD record (passed as cls).
506  *
507  * @param cls metadata to add to
508  * @param type type of entry to insert
509  * @param data value of entry to insert
510  */
511 static int
512 merge_meta_helper (void *cls, EXTRACTOR_KeywordType type, const char *data)
513 {
514   struct GNUNET_CONTAINER_MetaData *meta = cls;
515   GNUNET_CONTAINER_meta_data_insert (meta, type, data);
516   return GNUNET_OK;
517 }
518
519
520
521 /**
522  * Add a pseudonym to the set of known pseudonyms.
523  * For all pseudonym advertisements that we discover
524  * FS should automatically call this function.
525  *
526  * @param cfg overall configuration
527  * @param id the pseudonym identifier
528  * @param meta metadata for the pseudonym
529  */
530 void
531 GNUNET_PSEUDONYM_add (const struct GNUNET_CONFIGURATION_Handle *cfg,
532                       const GNUNET_HashCode * id,
533                       const struct GNUNET_CONTAINER_MetaData *meta)
534 {
535   char *name;
536   int32_t ranking;
537   struct GNUNET_CONTAINER_MetaData *old;
538   char *fn;
539   struct stat sbuf;
540
541   ranking = 0;
542   fn = get_data_filename (cfg, PS_METADATA_DIR, id);
543   GNUNET_assert (fn != NULL);
544
545   if ((0 == STAT (fn, &sbuf)) &&
546       (GNUNET_OK == read_info (cfg, id, &old, &ranking, &name)))
547     {
548       GNUNET_CONTAINER_meta_data_get_contents (meta, &merge_meta_helper, old);
549       write_pseudonym_info (cfg, id, old, ranking, name);
550       GNUNET_CONTAINER_meta_data_destroy (old);
551       GNUNET_free_non_null (name);
552     }
553   else
554     {
555       write_pseudonym_info (cfg, id, meta, ranking, NULL);
556     }
557   GNUNET_free (fn);
558   internal_notify (id, meta, ranking);
559 }
560
561
562
563
564
565 /* end of pseudonym.c */