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