bugfix
[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   int ret;
181   fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
182   GNUNET_assert (fn != NULL);
183   struct GNUNET_BIO_WriteHandle *fileW;
184   fileW = GNUNET_BIO_write_open(fn);
185   if((NULL != fileW)&&
186          (GNUNET_OK == GNUNET_BIO_write_int32(fileW, ranking))&&
187      (GNUNET_OK == GNUNET_BIO_write_string(fileW, ns_name))&&
188      (GNUNET_OK == GNUNET_BIO_write_meta_data(fileW, meta))&&
189      (GNUNET_OK == GNUNET_BIO_write_close(fileW)))
190   ret = GNUNET_OK;
191   else
192   ret = GNUNET_SYSERR;
193   GNUNET_free (fn);
194   /* create entry for pseudonym name in names */
195   GNUNET_free_non_null (GNUNET_PSEUDONYM_id_to_name (cfg, nsid));
196 }
197
198
199 /**
200  * FIXME
201  */
202 static int
203 read_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
204            const GNUNET_HashCode * nsid,
205            struct GNUNET_CONTAINER_MetaData **meta,
206            int32_t * ranking, char **ns_name)
207 {
208   char *fn;
209   char *emsg;
210   int ret;
211   fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
212   GNUNET_assert (fn != NULL);
213   struct GNUNET_BIO_ReadHandle *fileR;
214   fileR = GNUNET_BIO_read_open(fn);
215   if((NULL != fileR)&&
216      (GNUNET_OK == GNUNET_BIO_read_int32__(fileR, "Read int32 error!", ranking))&&
217      (GNUNET_OK == GNUNET_BIO_read_string(fileR, "Read string error!", ns_name, 200))&&
218      (GNUNET_OK == GNUNET_BIO_read_meta_data(fileR, "Read meta data error!", meta))&&
219      (GNUNET_OK == GNUNET_BIO_read_close(fileR, &emsg)))
220   ret = GNUNET_OK;
221   else
222   ret = GNUNET_SYSERR;
223   GNUNET_free (fn);
224   return ret;
225 }
226
227
228
229 /**
230  * Return the unique, human readable name for the given namespace.
231  *
232  * @param cfg configuration 
233  * @param nsid cryptographic ID of the namespace
234  * @return NULL on failure (should never happen)
235  */
236 char *
237 GNUNET_PSEUDONYM_id_to_name (const struct GNUNET_CONFIGURATION_Handle *cfg,
238                              const GNUNET_HashCode * nsid)
239 {
240   struct GNUNET_CONTAINER_MetaData *meta;
241   char *name;
242   GNUNET_HashCode nh;
243   char *fn;
244   uint64_t len;
245   struct GNUNET_DISK_FileHandle *fh;
246   unsigned int i;
247   unsigned int idx;
248   char *ret;
249   struct stat sbuf;
250   int32_t temp = 0;
251   int32_t *rank = &temp;
252
253   meta = NULL;
254   name = NULL;
255   if (GNUNET_OK == read_info (cfg, nsid, &meta, rank, &name))
256     {
257       if ((meta != NULL) && (name == NULL))
258         name = GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
259                                                               EXTRACTOR_TITLE,
260                                                               EXTRACTOR_FILENAME,
261                                                               EXTRACTOR_DESCRIPTION,
262                                                               EXTRACTOR_SUBJECT,
263                                                               EXTRACTOR_PUBLISHER,
264                                                               EXTRACTOR_AUTHOR,
265                                                               EXTRACTOR_COMMENT,
266                                                               EXTRACTOR_SUMMARY,
267                                                               EXTRACTOR_OWNER,
268                                                               -1);
269       if (meta != NULL)
270         {
271           GNUNET_CONTAINER_meta_data_destroy (meta);
272           meta = NULL;
273         }
274     }
275   if (name == NULL)
276     name = GNUNET_strdup (_("no-name"));
277   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
278   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
279   GNUNET_assert (fn != NULL);
280
281   len = 0;
282   if (0 == STAT (fn, &sbuf))
283     GNUNET_DISK_file_size (fn, &len, GNUNET_YES);
284   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_CREATE
285                               | GNUNET_DISK_OPEN_READWRITE,
286                               GNUNET_DISK_PERM_USER_READ |
287                               GNUNET_DISK_PERM_USER_WRITE);
288   i = 0;
289   idx = -1;
290   while ((len >= sizeof (GNUNET_HashCode)) &&
291          (sizeof (GNUNET_HashCode)
292           == GNUNET_DISK_file_read (fh, &nh, sizeof (GNUNET_HashCode))))
293     {
294       if (0 == memcmp (&nh, nsid, sizeof (GNUNET_HashCode)))
295         {
296           idx = i;
297           break;
298         }
299       i++;
300       len -= sizeof (GNUNET_HashCode);
301     }
302   if (idx == -1)
303     {
304       idx = i;
305       if (sizeof (GNUNET_HashCode) !=
306           GNUNET_DISK_file_write (fh, nsid, sizeof (GNUNET_HashCode)))
307         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn);
308     }
309   GNUNET_DISK_file_close (fh);
310   ret = GNUNET_malloc (strlen (name) + 32);
311   GNUNET_snprintf (ret, strlen (name) + 32, "%s-%u", name, idx);
312   GNUNET_free (name);
313   GNUNET_free (fn);
314   return ret;
315 }
316
317 /**
318  * Get the namespace ID belonging to the given namespace name.
319  *
320  * @param cfg configuration to use
321  * @param ns_uname human-readable name for the namespace
322  * @param nsid set to namespace ID based on 'ns_uname'
323  * @return GNUNET_OK on success
324  */
325 int
326 GNUNET_PSEUDONYM_name_to_id (const struct GNUNET_CONFIGURATION_Handle *cfg,
327                              const char *ns_uname, GNUNET_HashCode * nsid)
328 {
329   size_t slen;
330   uint64_t len;
331   unsigned int idx;
332   char *name;
333   GNUNET_HashCode nh;
334   char *fn;
335   struct GNUNET_DISK_FileHandle *fh;
336
337   idx = -1;
338   slen = strlen (ns_uname);
339   while ((slen > 0) && (1 != sscanf (&ns_uname[slen - 1], "-%u", &idx)))
340     slen--;
341   if (slen == 0)
342     return GNUNET_SYSERR;
343   name = GNUNET_strdup (ns_uname);
344   name[slen - 1] = '\0';
345   GNUNET_CRYPTO_hash (name, strlen (name), &nh);
346   GNUNET_free (name);
347   fn = get_data_filename (cfg, PS_NAMES_DIR, &nh);
348   GNUNET_assert (fn != NULL);
349
350   if ((GNUNET_OK != GNUNET_DISK_file_test (fn) ||
351        (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES))) ||
352       ((idx + 1) * sizeof (GNUNET_HashCode) > len))
353     {
354       GNUNET_free (fn);
355       return GNUNET_SYSERR;
356     }
357   fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_CREATE
358                               | GNUNET_DISK_OPEN_READWRITE,
359                               GNUNET_DISK_PERM_USER_READ |
360                               GNUNET_DISK_PERM_USER_WRITE);
361   GNUNET_free (fn);
362   GNUNET_DISK_file_seek (fh, idx * sizeof (GNUNET_HashCode),
363                          GNUNET_DISK_SEEK_SET);
364   if (sizeof (GNUNET_HashCode) !=
365       GNUNET_DISK_file_read (fh, nsid, sizeof (GNUNET_HashCode)))
366     {
367       GNUNET_DISK_file_close (fh);
368       return GNUNET_SYSERR;
369     }
370   GNUNET_DISK_file_close (fh);
371   return GNUNET_OK;
372 }
373
374
375
376 /**
377  * FIXME
378  */
379 struct ListPseudonymClosure
380 {
381
382   /**
383    * FIXME
384    */
385   GNUNET_PSEUDONYM_Iterator iterator;
386
387   /**
388    * FIXME
389    */
390   void *closure;
391
392   /**
393    * FIXME
394    */
395   const struct GNUNET_CONFIGURATION_Handle *cfg;
396 };
397
398
399
400 /**
401  * FIXME
402  */
403 static int
404 list_pseudonym_helper (void *cls, const char *fullname)
405 {
406   struct ListPseudonymClosure *c = cls;
407   int ret;
408   GNUNET_HashCode id;
409   int rating;
410   struct GNUNET_CONTAINER_MetaData *meta;
411   const char *fn;
412   char *str;
413
414   if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))
415     return GNUNET_OK;
416   fn =
417     &fullname[strlen (fullname) + 1 -
418               sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)];
419   if (fn[-1] != DIR_SEPARATOR)
420     return GNUNET_OK;
421   ret = GNUNET_OK;
422   if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (fn, &id))
423     return GNUNET_OK;           /* invalid name */
424   str = NULL;
425   if (GNUNET_OK != read_info (c->cfg, &id, &meta, &rating, &str))
426     return GNUNET_OK;           /* ignore entry */
427   GNUNET_free_non_null (str);
428   if (c->iterator != NULL)
429     ret = c->iterator (c->closure, &id, meta, rating);
430   GNUNET_CONTAINER_meta_data_destroy (meta);
431   return ret;
432 }
433
434
435 /**
436  * List all available pseudonyms.
437  *
438  * @param cfg overall configuration 
439  * @param iterator function to call for each pseudonym
440  * @param closure closure for iterator
441  * @return number of pseudonyms found
442  */
443 int
444 GNUNET_PSEUDONYM_list_all (const struct GNUNET_CONFIGURATION_Handle *cfg,
445                            GNUNET_PSEUDONYM_Iterator iterator, void *closure)
446 {
447   struct ListPseudonymClosure cls;
448   char *fn;
449   int ret;
450
451   cls.iterator = iterator;
452   cls.closure = closure;
453   cls.cfg = cfg;
454   fn = get_data_filename (cfg, PS_METADATA_DIR, NULL);
455   GNUNET_assert (fn != NULL);
456   GNUNET_DISK_directory_create (fn);
457   ret = GNUNET_DISK_directory_scan (fn, &list_pseudonym_helper, &cls);
458   GNUNET_free (fn);
459   return ret;
460 }
461
462 /**
463  * Change the ranking of a pseudonym.
464  *
465  * @param cfg overall configuration
466  * @param nsid id of the pseudonym
467  * @param delta by how much should the rating be
468  *  changed?
469  * @return new rating of the pseudonym
470  */
471 int
472 GNUNET_PSEUDONYM_rank (const struct GNUNET_CONFIGURATION_Handle *cfg,
473                        const GNUNET_HashCode * nsid, int delta)
474 {
475   struct GNUNET_CONTAINER_MetaData *meta;
476   int ret;
477   int32_t ranking;
478   char *name;
479
480   name = NULL;
481   ret = read_info (cfg, nsid, &meta, &ranking, &name);
482   if (ret == GNUNET_SYSERR)
483     {
484       ranking = 0;
485       meta = GNUNET_CONTAINER_meta_data_create ();
486     }
487   ranking += delta;
488   write_pseudonym_info (cfg, nsid, meta, ranking, name);
489   GNUNET_CONTAINER_meta_data_destroy (meta);
490   GNUNET_free_non_null (name);
491   return ranking;
492 }
493
494 /**
495  * Insert metadata into existing MD record (passed as cls).
496  *
497  * @param cls metadata to add to
498  * @param type type of entry to insert
499  * @param data value of entry to insert
500  */
501 static int
502 merge_meta_helper (void *cls, EXTRACTOR_KeywordType type, const char *data)
503 {
504   struct GNUNET_CONTAINER_MetaData *meta = cls;
505   GNUNET_CONTAINER_meta_data_insert (meta, type, data);
506   return GNUNET_OK;
507 }
508
509
510
511 /**
512  * Add a pseudonym to the set of known pseudonyms.
513  * For all pseudonym advertisements that we discover
514  * FS should automatically call this function.
515  *
516  * @param cfg overall configuration
517  * @param id the pseudonym identifier
518  * @param meta metadata for the pseudonym
519  */
520 void
521 GNUNET_PSEUDONYM_add (const struct GNUNET_CONFIGURATION_Handle *cfg,
522                       const GNUNET_HashCode * id,
523                       const struct GNUNET_CONTAINER_MetaData *meta)
524 {
525   char *name;
526   int32_t ranking;
527   struct GNUNET_CONTAINER_MetaData *old;
528   char *fn;
529   struct stat sbuf;
530
531   ranking = 0;
532   fn = get_data_filename (cfg, PS_METADATA_DIR, id);
533   GNUNET_assert (fn != NULL);
534
535   if ((0 == STAT (fn, &sbuf)) &&
536       (GNUNET_OK == read_info (cfg, id, &old, &ranking, &name)))
537     {
538       GNUNET_CONTAINER_meta_data_get_contents (meta, &merge_meta_helper, old);
539       write_pseudonym_info (cfg, id, old, ranking, name);
540       GNUNET_CONTAINER_meta_data_destroy (old);
541       GNUNET_free_non_null (name);
542     }
543   else
544     {
545       write_pseudonym_info (cfg, id, meta, ranking, NULL);
546     }
547   GNUNET_free (fn);
548   internal_notify (id, meta, ranking);
549 }
550
551
552
553
554
555 /* end of pseudonym.c */