fix
[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 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
35
36 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
37
38 /**
39  * Name of the directory which stores meta data for pseudonym
40  */
41 #define PS_METADATA_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms" DIR_SEPARATOR_STR "metadata" DIR_SEPARATOR_STR
42
43 /**
44  * Name of the directory which stores names for pseudonyms
45  */
46 #define PS_NAMES_DIR    DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms" DIR_SEPARATOR_STR "names"    DIR_SEPARATOR_STR
47
48
49 /**
50  * Registered callbacks for discovery of pseudonyms.
51  */
52 struct DiscoveryCallback
53 {
54   /**
55    * This is a linked list.
56    */
57   struct DiscoveryCallback *next;
58
59   /**
60    * Function to call each time a pseudonym is discovered.
61    */
62   GNUNET_PSEUDONYM_Iterator callback;
63
64   /**
65    * Closure for callback.
66    */
67   void *closure;
68 };
69
70
71 /**
72  * Head of the linked list of functions to call when
73  * new pseudonyms are added.
74  */
75 static struct DiscoveryCallback *head;
76
77 /**
78  * Internal notification about new tracked URI.
79  * @param id a point to the hash code of pseudonym
80  * @param md meta data to be written
81  * @param rating rating of pseudonym
82  */
83 static void
84 internal_notify (const GNUNET_HashCode * id,
85                  const struct GNUNET_CONTAINER_MetaData *md, int rating)
86 {
87   struct DiscoveryCallback *pos;
88
89   pos = head;
90   while (pos != NULL)
91     {
92       pos->callback (pos->closure, id, md, rating);
93       pos = pos->next;
94     }
95 }
96
97 /**
98  * Register callback to be invoked whenever we discover
99  * a new pseudonym.
100  * @param cfg configuration to use
101  * @param iterator iterator over pseudonym
102  * @param closure point to a closure
103  */
104 int
105 GNUNET_PSEUDONYM_discovery_callback_register (const struct
106                                               GNUNET_CONFIGURATION_Handle
107                                               *cfg,
108                                               GNUNET_PSEUDONYM_Iterator
109                                               iterator, void *closure)
110 {
111   struct DiscoveryCallback *list;
112
113   list = GNUNET_malloc (sizeof (struct DiscoveryCallback));
114   list->callback = iterator;
115   list->closure = closure;
116   list->next = head;
117   head = list;
118   GNUNET_PSEUDONYM_list_all (cfg, iterator, closure);
119   return GNUNET_OK;
120 }
121
122 /**
123  * Unregister pseudonym discovery callback.
124  * @param iterator iterator over pseudonym
125  * @param closure point to a closure
126  */
127 int
128 GNUNET_PSEUDONYM_discovery_callback_unregister (GNUNET_PSEUDONYM_Iterator
129                                                 iterator, void *closure)
130 {
131   struct DiscoveryCallback *prev;
132   struct DiscoveryCallback *pos;
133
134   prev = NULL;
135   pos = head;
136   while ((pos != NULL) &&
137          ((pos->callback != iterator) || (pos->closure != closure)))
138     {
139       prev = pos;
140       pos = pos->next;
141     }
142   if (pos == NULL)
143     return GNUNET_SYSERR;
144   if (prev == NULL)
145     head = pos->next;
146   else
147     prev->next = pos->next;
148   GNUNET_free (pos);
149   return GNUNET_OK;
150 }
151
152
153 /**
154  * Get the filename (or directory name) for the given
155  * pseudonym identifier and directory prefix.
156  * @param cfg configuration to use
157  * @param prefix path components to append to the private directory name
158  * @param psid hash code of pseudonym, can be NULL
159  * @return filename of the pseudonym (if psid != NULL) or directory with the data (if psid == NULL)
160  */
161 static char *
162 get_data_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
163                    const char *prefix, const GNUNET_HashCode * psid)
164 {
165   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
166
167   if (psid != NULL)
168     GNUNET_CRYPTO_hash_to_enc (psid, &enc);
169   return GNUNET_DISK_get_home_filename (cfg, GNUNET_CLIENT_SERVICE_NAME,
170                                         prefix,
171                                         (psid ==
172                                          NULL) ? NULL : (const char *) &enc,
173                                         NULL);
174 }
175
176
177 /**
178  * Write the pseudonym infomation into a file
179  * @param cfg configuration to use
180  * @param nsid hash code of a pseudonym
181  * @param meta meta data to be written into a file
182  * @param ranking ranking of a pseudonym
183  * @param ns_name name of a pseudonym
184  */
185 static void
186 write_pseudonym_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
187                       const GNUNET_HashCode * nsid,
188                       const struct GNUNET_CONTAINER_MetaData *meta,
189                       int32_t ranking, const char *ns_name)
190 {
191   char *fn;
192   struct GNUNET_BIO_WriteHandle *fileW;
193
194   fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
195   GNUNET_assert (fn != NULL);
196   fileW = GNUNET_BIO_write_open (fn);
197   if (NULL != fileW)
198     {
199       if ((GNUNET_OK != GNUNET_BIO_write_int32 (fileW, ranking)) ||
200           (GNUNET_OK != GNUNET_BIO_write_string (fileW, ns_name)) ||
201           (GNUNET_OK != GNUNET_BIO_write_meta_data (fileW, meta)))
202         {
203           (void) GNUNET_BIO_write_close (fileW);
204           GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
205           GNUNET_free (fn);
206           return;
207         }
208       if (GNUNET_OK != GNUNET_BIO_write_close (fileW))
209         {
210           GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
211           GNUNET_free (fn);
212           return;
213         }
214     }
215   GNUNET_free (fn);
216   /* create entry for pseudonym name in names */
217   /* FIXME: 90% of what this call does is not needed
218    * here => refactor code to only create the entry! */
219   GNUNET_free_non_null (GNUNET_PSEUDONYM_id_to_name (cfg, nsid));
220 }
221
222
223 /**
224  * read the pseudonym infomation from a file
225  * @param cfg configuration to use
226  * @param nsid hash code of a pseudonym
227  * @param meta meta data to be read from a file
228  * @param ranking ranking of a pseudonym
229  * @param ns_name name of a pseudonym
230  */
231 static int
232 read_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
233            const GNUNET_HashCode * nsid,
234            struct GNUNET_CONTAINER_MetaData **meta, int32_t * ranking,
235            char **ns_name)
236 {
237   char *fn;
238   char *emsg;
239   struct GNUNET_BIO_ReadHandle *fileR;
240
241   fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
242   GNUNET_assert (fn != NULL);
243   fileR = GNUNET_BIO_read_open (fn);
244   if (fileR == NULL)
245     {
246       GNUNET_free (fn);
247       return GNUNET_SYSERR;
248     }
249   emsg = NULL;
250   *ns_name = NULL;
251   if ((GNUNET_OK != GNUNET_BIO_read_int32 (fileR, ranking)) ||
252       (GNUNET_OK !=
253        GNUNET_BIO_read_string (fileR, "Read string error!", ns_name, 200)) ||
254       (GNUNET_OK !=
255        GNUNET_BIO_read_meta_data (fileR, "Read meta data error!", meta)))
256     {
257       (void) GNUNET_BIO_read_close (fileR, &emsg);
258       GNUNET_free_non_null (emsg);
259       GNUNET_free_non_null (*ns_name);
260       *ns_name = NULL;
261       GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
262       GNUNET_free (fn);
263       return GNUNET_SYSERR;
264     }
265   if (GNUNET_OK != GNUNET_BIO_read_close (fileR, &emsg))
266     {
267       LOG (GNUNET_ERROR_TYPE_WARNING,
268            _
269            ("Failed to parse metadata about pseudonym from file `%s': %s\n"),
270            fn, emsg);
271       GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
272       GNUNET_CONTAINER_meta_data_destroy (*meta);
273       *meta = NULL;
274       GNUNET_free_non_null (*ns_name);
275       *ns_name = NULL;
276       GNUNET_free_non_null (emsg);
277       GNUNET_free (fn);
278       return GNUNET_SYSERR;
279     }
280   GNUNET_free (fn);
281   return GNUNET_OK;
282 }
283
284
285
286 /**
287  * Return the unique, human readable name for the given namespace.
288  *
289  * @param cfg configuration
290  * @param nsid cryptographic ID of the namespace
291  * @return NULL on failure (should never happen)
292  */
293 char *
294 GNUNET_PSEUDONYM_id_to_name (const struct GNUNET_CONFIGURATION_Handle *cfg,
295                              const GNUNET_HashCode * nsid)
296 {
297   struct GNUNET_CONTAINER_MetaData *meta;
298   char *name;
299   GNUNET_HashCode nh;
300   char *fn;
301   uint64_t len;
302   struct GNUNET_DISK_FileHandle *fh;
303   unsigned int i;
304   unsigned int idx;
305   char *ret;
306   struct stat sbuf;
307   int32_t temp = 0;
308   int32_t *rank = &temp;
309
310   meta = NULL;
311   name = NULL;
312   if (GNUNET_OK == read_info (cfg, nsid, &meta, rank, &name))
313     {
314       if ((meta != NULL) && (name == NULL))
315         name =
316           GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
317                                                          EXTRACTOR_METATYPE_TITLE,
318                                                          EXTRACTOR_METATYPE_FILENAME,
319                                                          EXTRACTOR_METATYPE_DESCRIPTION,
320                                                          EXTRACTOR_METATYPE_SUBJECT,
321                                                          EXTRACTOR_METATYPE_PUBLISHER,
322                                                          EXTRACTOR_METATYPE_AUTHOR_NAME,
323                                                          EXTRACTOR_METATYPE_COMMENT,
324                                                          EXTRACTOR_METATYPE_SUMMARY,
325                                                          -1);
326       if (meta != NULL)
327         {
328           GNUNET_CONTAINER_meta_data_destroy (meta);
329           meta = NULL;
330         }
331     }
332   if (name == NULL)
333     name = GNUNET_strdup (_("no-name"));
334   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
335   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
336   GNUNET_assert (fn != NULL);
337
338   len = 0;
339   if (0 == STAT (fn, &sbuf))
340     GNUNET_DISK_file_size (fn, &len, GNUNET_YES);
341   fh = GNUNET_DISK_file_open (fn,
342                               GNUNET_DISK_OPEN_CREATE |
343                               GNUNET_DISK_OPEN_READWRITE,
344                               GNUNET_DISK_PERM_USER_READ |
345                               GNUNET_DISK_PERM_USER_WRITE);
346   i = 0;
347   idx = -1;
348   while ((len >= sizeof (GNUNET_HashCode)) &&
349          (sizeof (GNUNET_HashCode) ==
350           GNUNET_DISK_file_read (fh, &nh, sizeof (GNUNET_HashCode))))
351     {
352       if (0 == memcmp (&nh, nsid, sizeof (GNUNET_HashCode)))
353         {
354           idx = i;
355           break;
356         }
357       i++;
358       len -= sizeof (GNUNET_HashCode);
359     }
360   if (idx == -1)
361     {
362       idx = i;
363       if (sizeof (GNUNET_HashCode) !=
364           GNUNET_DISK_file_write (fh, nsid, sizeof (GNUNET_HashCode)))
365         LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "write", fn);
366     }
367   GNUNET_DISK_file_close (fh);
368   ret = GNUNET_malloc (strlen (name) + 32);
369   GNUNET_snprintf (ret, strlen (name) + 32, "%s-%u", name, idx);
370   GNUNET_free (name);
371   GNUNET_free (fn);
372   return ret;
373 }
374
375 /**
376  * Get the namespace ID belonging to the given namespace name.
377  *
378  * @param cfg configuration to use
379  * @param ns_uname human-readable name for the namespace
380  * @param nsid set to namespace ID based on 'ns_uname'
381  * @return GNUNET_OK on success
382  */
383 int
384 GNUNET_PSEUDONYM_name_to_id (const struct GNUNET_CONFIGURATION_Handle *cfg,
385                              const char *ns_uname, GNUNET_HashCode * nsid)
386 {
387   size_t slen;
388   uint64_t len;
389   unsigned int idx;
390   char *name;
391   GNUNET_HashCode nh;
392   char *fn;
393   struct GNUNET_DISK_FileHandle *fh;
394
395   idx = -1;
396   slen = strlen (ns_uname);
397   while ((slen > 0) && (1 != sscanf (&ns_uname[slen - 1], "-%u", &idx)))
398     slen--;
399   if (slen == 0)
400     return GNUNET_SYSERR;
401   name = GNUNET_strdup (ns_uname);
402   name[slen - 1] = '\0';
403   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
404   GNUNET_free (name);
405   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
406   GNUNET_assert (fn != NULL);
407
408   if ((GNUNET_OK != GNUNET_DISK_file_test (fn) ||
409        (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES))) ||
410       ((idx + 1) * sizeof (GNUNET_HashCode) > len))
411     {
412       GNUNET_free (fn);
413       return GNUNET_SYSERR;
414     }
415   fh = GNUNET_DISK_file_open (fn,
416                               GNUNET_DISK_OPEN_CREATE |
417                               GNUNET_DISK_OPEN_READWRITE,
418                               GNUNET_DISK_PERM_USER_READ |
419                               GNUNET_DISK_PERM_USER_WRITE);
420   GNUNET_free (fn);
421   GNUNET_DISK_file_seek (fh, idx * sizeof (GNUNET_HashCode),
422                          GNUNET_DISK_SEEK_SET);
423   if (sizeof (GNUNET_HashCode) !=
424       GNUNET_DISK_file_read (fh, nsid, sizeof (GNUNET_HashCode)))
425     {
426       GNUNET_DISK_file_close (fh);
427       return GNUNET_SYSERR;
428     }
429   GNUNET_DISK_file_close (fh);
430   return GNUNET_OK;
431 }
432
433
434
435 /**
436  * struct used to list the pseudonym
437  */
438 struct ListPseudonymClosure
439 {
440
441   /**
442    * iterator over pseudonym
443    */
444   GNUNET_PSEUDONYM_Iterator iterator;
445
446   /**
447    * Closure for iterator.
448    */
449   void *closure;
450
451   /**
452    * Configuration to use.
453    */
454   const struct GNUNET_CONFIGURATION_Handle *cfg;
455 };
456
457
458
459 /**
460  * the help function to list all available pseudonyms
461  * @param cls point to a struct ListPseudonymClosure
462  * @param fullname name of pseudonym
463  */
464 static int
465 list_pseudonym_helper (void *cls, const char *fullname)
466 {
467   struct ListPseudonymClosure *c = cls;
468   int ret;
469   GNUNET_HashCode id;
470   int rating;
471   struct GNUNET_CONTAINER_MetaData *meta;
472   const char *fn;
473   char *str;
474
475   if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
476     return GNUNET_OK;
477   fn = &fullname[strlen (fullname) + 1 -
478                  sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
479   if (fn[-1] != DIR_SEPARATOR)
480     return GNUNET_OK;
481   ret = GNUNET_OK;
482   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (fn, &id))
483     return GNUNET_OK;           /* invalid name */
484   str = NULL;
485   if (GNUNET_OK != read_info (c->cfg, &id, &meta, &rating, &str))
486     return GNUNET_OK;           /* ignore entry */
487   GNUNET_free_non_null (str);
488   if (c->iterator != NULL)
489     ret = c->iterator (c->closure, &id, meta, rating);
490   GNUNET_CONTAINER_meta_data_destroy (meta);
491   return ret;
492 }
493
494
495 /**
496  * List all available pseudonyms.
497  *
498  * @param cfg overall configuration
499  * @param iterator function to call for each pseudonym
500  * @param closure closure for iterator
501  * @return number of pseudonyms found
502  */
503 int
504 GNUNET_PSEUDONYM_list_all (const struct GNUNET_CONFIGURATION_Handle *cfg,
505                            GNUNET_PSEUDONYM_Iterator iterator, void *closure)
506 {
507   struct ListPseudonymClosure cls;
508   char *fn;
509   int ret;
510
511   cls.iterator = iterator;
512   cls.closure = closure;
513   cls.cfg = cfg;
514   fn = get_data_filename (cfg, PS_METADATA_DIR, NULL);
515   GNUNET_assert (fn != NULL);
516   GNUNET_DISK_directory_create (fn);
517   ret = GNUNET_DISK_directory_scan (fn, &list_pseudonym_helper, &cls);
518   GNUNET_free (fn);
519   return ret;
520 }
521
522
523 /**
524  * Change the ranking of a pseudonym.
525  *
526  * @param cfg overall configuration
527  * @param nsid id of the pseudonym
528  * @param delta by how much should the rating be
529  *  changed?
530  * @return new rating of the pseudonym
531  */
532 int
533 GNUNET_PSEUDONYM_rank (const struct GNUNET_CONFIGURATION_Handle *cfg,
534                        const GNUNET_HashCode * nsid, int delta)
535 {
536   struct GNUNET_CONTAINER_MetaData *meta;
537   int ret;
538   int32_t ranking;
539   char *name;
540
541   name = NULL;
542   ret = read_info (cfg, nsid, &meta, &ranking, &name);
543   if (ret == GNUNET_SYSERR)
544     {
545       ranking = 0;
546       meta = GNUNET_CONTAINER_meta_data_create ();
547     }
548   ranking += delta;
549   write_pseudonym_info (cfg, nsid, meta, ranking, name);
550   GNUNET_CONTAINER_meta_data_destroy (meta);
551   GNUNET_free_non_null (name);
552   return ranking;
553 }
554
555
556 /**
557  * Add a pseudonym to the set of known pseudonyms.
558  * For all pseudonym advertisements that we discover
559  * FS should automatically call this function.
560  *
561  * @param cfg overall configuration
562  * @param id the pseudonym identifier
563  * @param meta metadata for the pseudonym
564  */
565 void
566 GNUNET_PSEUDONYM_add (const struct GNUNET_CONFIGURATION_Handle *cfg,
567                       const GNUNET_HashCode * id,
568                       const struct GNUNET_CONTAINER_MetaData *meta)
569 {
570   char *name;
571   int32_t ranking;
572   struct GNUNET_CONTAINER_MetaData *old;
573   char *fn;
574   struct stat sbuf;
575
576   ranking = 0;
577   fn = get_data_filename (cfg, PS_METADATA_DIR, id);
578   GNUNET_assert (fn != NULL);
579
580   if ((0 == STAT (fn, &sbuf)) &&
581       (GNUNET_OK == read_info (cfg, id, &old, &ranking, &name)))
582     {
583       GNUNET_CONTAINER_meta_data_merge (old, meta);
584       write_pseudonym_info (cfg, id, old, ranking, name);
585       GNUNET_CONTAINER_meta_data_destroy (old);
586       GNUNET_free_non_null (name);
587     }
588   else
589     {
590       write_pseudonym_info (cfg, id, meta, ranking, NULL);
591     }
592   GNUNET_free (fn);
593   internal_notify (id, meta, ranking);
594 }
595
596
597 /* end of pseudonym.c */