glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / topology / friends.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013 Christian Grothoff
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14 */
15
16 /**
17  * @file topology/friends.c
18  * @brief library to read and write the FRIENDS file
19  * @author Christian Grothoff
20  */
21 #include "platform.h"
22 #include "gnunet_friends_lib.h"
23
24
25 /**
26  * Parse the FRIENDS file.
27  *
28  * @param cfg our configuration
29  * @param cb function to call on each friend found
30  * @param cb_cls closure for @a cb
31  * @return #GNUNET_OK on success, #GNUNET_SYSERR on parsing errors
32  */
33 int
34 GNUNET_FRIENDS_parse (const struct GNUNET_CONFIGURATION_Handle *cfg,
35                       GNUNET_FRIENDS_Callback cb,
36                       void *cb_cls)
37 {
38   char *fn;
39   char *data;
40   size_t pos;
41   size_t start;
42   struct GNUNET_PeerIdentity pid;
43   uint64_t fsize;
44   ssize_t ssize;
45
46   if (GNUNET_OK !=
47       GNUNET_CONFIGURATION_get_value_filename (cfg,
48                                                "TOPOLOGY",
49                                                "FRIENDS",
50                                                &fn))
51   {
52     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
53                                "topology",
54                                "FRIENDS");
55     return GNUNET_SYSERR;
56   }
57   if (GNUNET_SYSERR ==
58       GNUNET_DISK_directory_create_for_file (fn))
59   {
60     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
61                               "mkdir",
62                               fn);
63     GNUNET_free (fn);
64     return GNUNET_SYSERR;
65   }
66   if ( (GNUNET_OK !=
67         GNUNET_DISK_file_test (fn)) &&
68        (GNUNET_OK !=
69         GNUNET_DISK_fn_write (fn,
70                               NULL,
71                               0,
72                               GNUNET_DISK_PERM_USER_READ |
73                               GNUNET_DISK_PERM_USER_WRITE |
74                               GNUNET_DISK_OPEN_CREATE)) )
75     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
76                               "write",
77                               fn);
78   if ( (GNUNET_OK !=
79         GNUNET_DISK_file_size (fn,
80                                &fsize,
81                                GNUNET_NO,
82                                GNUNET_YES)) ||
83        (0 == fsize) )
84   {
85     GNUNET_free (fn);
86     return GNUNET_OK;
87   }
88   data = GNUNET_malloc_large (fsize);
89   if (NULL == data)
90   {
91     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "malloc");
92     GNUNET_free (fn);
93     return GNUNET_SYSERR;
94   }
95   ssize = GNUNET_DISK_fn_read (fn,
96                                data,
97                                fsize);
98   if ( (ssize < 0) ||
99        (fsize != (uint64_t) ssize) )
100   {
101     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
102                               "read",
103                               "fn");
104     GNUNET_free (fn);
105     GNUNET_free (data);
106     return GNUNET_SYSERR;
107   }
108   start = 0;
109   pos = 0;
110   while (pos < fsize)
111   {
112     while ( (pos < fsize) &&
113             (! isspace ((unsigned char) data[pos])) )
114       pos++;
115     if (GNUNET_OK !=
116         GNUNET_CRYPTO_eddsa_public_key_from_string (&data[start],
117                                                     pos - start,
118                                                     &pid.public_key))
119     {
120       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
121                   _("Syntax error in FRIENDS file at offset %llu, skipping bytes `%.*s'.\n"),
122                   (unsigned long long) pos,
123                   (int) (pos - start),
124                   &data[start]);
125       pos++;
126       start = pos;
127       continue;
128     }
129     pos++;
130     start = pos;
131     cb (cb_cls, &pid);
132   }
133   GNUNET_free (data);
134   GNUNET_free (fn);
135   return GNUNET_OK;
136 }
137
138
139 /**
140  * Handle for writing a friends file.
141  */
142 struct GNUNET_FRIENDS_Writer
143 {
144   /**
145    * Handle to the file.
146    */
147   struct GNUNET_DISK_FileHandle *fh;
148 };
149
150
151 /**
152  * Start writing a fresh FRIENDS file.  Will make a backup of the
153  * old one.
154  *
155  * @param cfg configuration to use.
156  * @return NULL on error
157  */
158 struct GNUNET_FRIENDS_Writer *
159 GNUNET_FRIENDS_write_start (const struct GNUNET_CONFIGURATION_Handle *cfg)
160 {
161   struct GNUNET_FRIENDS_Writer *w;
162   char *fn;
163
164   if (GNUNET_OK !=
165       GNUNET_CONFIGURATION_get_value_filename (cfg, "TOPOLOGY", "FRIENDS", &fn))
166   {
167     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
168                                "topology", "FRIENDS");
169     return NULL;
170   }
171   if (GNUNET_OK !=
172       GNUNET_DISK_directory_create_for_file (fn))
173   {
174     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
175                 _("Directory for file `%s' does not seem to be writable.\n"),
176                 fn);
177     GNUNET_free (fn);
178     return NULL;
179   }
180   if (GNUNET_OK == GNUNET_DISK_file_test (fn))
181     GNUNET_DISK_file_backup (fn);
182   w = GNUNET_new (struct GNUNET_FRIENDS_Writer);
183   w->fh = GNUNET_DISK_file_open  (fn,
184                                   GNUNET_DISK_OPEN_CREATE |
185                                   GNUNET_DISK_OPEN_WRITE |
186                                   GNUNET_DISK_OPEN_FAILIFEXISTS,
187                                   GNUNET_DISK_PERM_USER_READ);
188   GNUNET_free (fn);
189   if (NULL == w->fh)
190   {
191     GNUNET_free (w);
192     return NULL;
193   }
194   return w;
195 }
196
197
198 /**
199  * Finish writing out the friends file.
200  *
201  * @param w write handle
202  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
203  */
204 int
205 GNUNET_FRIENDS_write_stop (struct GNUNET_FRIENDS_Writer *w)
206 {
207   int ret;
208
209   ret = GNUNET_DISK_file_close (w->fh);
210   GNUNET_free (w);
211   return ret;
212 }
213
214
215 /**
216  * Add a friend to the friends file.
217  *
218  * @param w write handle
219  * @param friend_id friend to add
220  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
221  */
222 int
223 GNUNET_FRIENDS_write (struct GNUNET_FRIENDS_Writer *w,
224                       const struct GNUNET_PeerIdentity *friend_id)
225 {
226   char *buf;
227   char *ret;
228   size_t slen;
229
230   ret = GNUNET_CRYPTO_eddsa_public_key_to_string (&friend_id->public_key);
231   GNUNET_asprintf (&buf,
232                    "%s\n",
233                    ret);
234   GNUNET_free (ret);
235   slen = strlen (buf);
236   if (slen !=
237       GNUNET_DISK_file_write (w->fh,
238                               buf,
239                               slen))
240   {
241     GNUNET_free (buf);
242     return GNUNET_SYSERR;
243   }
244   GNUNET_free (buf);
245   return GNUNET_OK;
246 }
247
248
249 /* end of friends.c */