91bdd6c9d18944620df954625f44a14ce128f00e
[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   const char *str = "not null";
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   if (GNUNET_OK != read_info (c->cfg, &id, &meta, &rating, &str))
425     return GNUNET_OK;           /* ignore entry */
426   if (c->iterator != NULL)
427     ret = c->iterator (c->closure, &id, meta, rating);
428   GNUNET_CONTAINER_meta_data_destroy (meta);
429   return ret;
430 }
431
432
433 /**
434  * List all available pseudonyms.
435  *
436  * @param cfg overall configuration 
437  * @param iterator function to call for each pseudonym
438  * @param closure closure for iterator
439  * @return number of pseudonyms found
440  */
441 int
442 GNUNET_PSEUDONYM_list_all (const struct GNUNET_CONFIGURATION_Handle *cfg,
443                            GNUNET_PSEUDONYM_Iterator iterator, void *closure)
444 {
445   struct ListPseudonymClosure cls;
446   char *fn;
447   int ret;
448
449   cls.iterator = iterator;
450   cls.closure = closure;
451   cls.cfg = cfg;
452   fn = get_data_filename (cfg, PS_METADATA_DIR, NULL);
453   GNUNET_assert (fn != NULL);
454   GNUNET_DISK_directory_create (fn);
455   ret = GNUNET_DISK_directory_scan (fn, &list_pseudonym_helper, &cls);
456   GNUNET_free (fn);
457   return ret;
458 }
459
460 /**
461  * Change the ranking of a pseudonym.
462  *
463  * @param cfg overall configuration
464  * @param nsid id of the pseudonym
465  * @param delta by how much should the rating be
466  *  changed?
467  * @return new rating of the pseudonym
468  */
469 int
470 GNUNET_PSEUDONYM_rank (const struct GNUNET_CONFIGURATION_Handle *cfg,
471                        const GNUNET_HashCode * nsid, int delta)
472 {
473   struct GNUNET_CONTAINER_MetaData *meta;
474   int ret;
475   int32_t ranking;
476   char *name;
477
478   name = NULL;
479   ret = read_info (cfg, nsid, &meta, &ranking, &name);
480   if (ret == GNUNET_SYSERR)
481     {
482       ranking = 0;
483       meta = GNUNET_CONTAINER_meta_data_create ();
484     }
485   ranking += delta;
486   write_pseudonym_info (cfg, nsid, meta, ranking, name);
487   GNUNET_CONTAINER_meta_data_destroy (meta);
488   GNUNET_free_non_null (name);
489   return ranking;
490 }
491
492 /**
493  * Insert metadata into existing MD record (passed as cls).
494  *
495  * @param cls metadata to add to
496  * @param type type of entry to insert
497  * @param data value of entry to insert
498  */
499 static int
500 merge_meta_helper (void *cls, EXTRACTOR_KeywordType type, const char *data)
501 {
502   struct GNUNET_CONTAINER_MetaData *meta = cls;
503   GNUNET_CONTAINER_meta_data_insert (meta, type, data);
504   return GNUNET_OK;
505 }
506
507
508
509 /**
510  * Add a pseudonym to the set of known pseudonyms.
511  * For all pseudonym advertisements that we discover
512  * FS should automatically call this function.
513  *
514  * @param cfg overall configuration
515  * @param id the pseudonym identifier
516  * @param meta metadata for the pseudonym
517  */
518 void
519 GNUNET_PSEUDONYM_add (const struct GNUNET_CONFIGURATION_Handle *cfg,
520                       const GNUNET_HashCode * id,
521                       const struct GNUNET_CONTAINER_MetaData *meta)
522 {
523   char *name;
524   int32_t ranking;
525   struct GNUNET_CONTAINER_MetaData *old;
526   char *fn;
527   struct stat sbuf;
528
529   ranking = 0;
530   fn = get_data_filename (cfg, PS_METADATA_DIR, id);
531   GNUNET_assert (fn != NULL);
532
533   if ((0 == STAT (fn, &sbuf)) &&
534       (GNUNET_OK == read_info (cfg, id, &old, &ranking, &name)))
535     {
536       GNUNET_CONTAINER_meta_data_get_contents (meta, &merge_meta_helper, old);
537       write_pseudonym_info (cfg, id, old, ranking, name);
538       GNUNET_CONTAINER_meta_data_destroy (old);
539       GNUNET_free_non_null (name);
540     }
541   else
542     {
543       write_pseudonym_info (cfg, id, meta, ranking, NULL);
544     }
545   GNUNET_free (fn);
546   internal_notify (id, meta, ranking);
547 }
548
549
550
551
552
553 /* end of pseudonym.c */