Reported by Trent Creekmore
[dahdi/tools.git] / xpp / astribank_license.c
1 /*
2  * Written by Oron Peled <oron@actcom.co.il>
3  * Copyright (C) 2012, Xorcom
4  *
5  * All rights reserved.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22
23 #include <errno.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <xtalk/debug.h>
28 #include "astribank_license.h"
29
30 #define ARRAY_SIZE(a)   (sizeof(a)/sizeof((a)[0]))
31
32 static const struct boundary {
33         const char *name;
34         const char *markers[2];
35 } boundaries[] = {
36         [LICENSE_MARKER_NONE] = {       /* Skip 0 */
37         },
38         [LICENSE_MARKER_XORCOM] = {
39                 "Xorcom",
40                 {
41                         "-----BEGIN XORCOM LICENSE BLOCK-----",
42                         "-----END XORCOM LICENSE BLOCK-----",
43                 },
44         },
45         [LICENSE_MARKER_GENERIC] = {
46                 "Generic",
47                 {
48                         "-----BEGIN TELEPHONY DEVICE LICENSE BLOCK-----",
49                         "-----END TELEPHONY DEVICE LICENSE BLOCK-----",
50                 }
51         },
52 };
53
54 void license_markers_help(const char *prefix, FILE *fp)
55 {
56         int i;
57
58         fprintf(fp, "%sValid license markers:\n", prefix);
59         for (i = LICENSE_MARKER_NONE + 1; i < ARRAY_SIZE(boundaries); i++) {
60                 const struct boundary *b = &boundaries[i];
61                 if (b->markers[0] != 0)
62                         fprintf(fp, "%s\t%d - %s\n", prefix, i, b->name);
63         }
64 }
65
66 int license_marker_valid(unsigned int which)
67 {
68         if (which >= ARRAY_SIZE(boundaries))
69                 return 0;
70         if (boundaries[which].markers[0] == NULL)
71                 return 0;
72         return 1;
73 }
74
75 static const char *marker_string(unsigned int which, int end_marker)
76 {
77         int selector = (end_marker) ? 1 : 0;
78
79         if (license_marker_valid(which)) {
80                 return boundaries[which].markers[selector];
81         }
82         ERR("gen_marker: invalid marker %d\n", which);
83         return NULL;
84 }
85
86 static int marker_find(const char *str, int end_marker)
87 {
88         int selector = (end_marker) ? 1 : 0;
89         int i;
90
91         for (i = LICENSE_MARKER_NONE + 1; i < ARRAY_SIZE(boundaries); i++) {
92                 const struct boundary *b = &boundaries[i];
93                 const char *marker_str = b->markers[selector];
94
95 #if 0
96                 DBG("marker_find(%s,%d)[%d]: %s\n",
97                         str, end_marker, i, marker_str);
98 #endif
99                 if (!marker_str)
100                         continue;
101                 if (strcmp(str, marker_str) == 0)
102                         return i;
103         }
104         return 0;
105 }
106
107 static int bin_to_file(void *buf, int len, FILE *f)
108 {
109         static int bytes_on_line;
110         unsigned char *p = buf;
111         if (buf == NULL) {
112                 if (bytes_on_line != 0) {
113                         if (fprintf(f, "\n") != 1)
114                                 return -1;
115                         bytes_on_line = 0;
116                 }
117                 return 0;
118         }
119         int i;
120         for (i = 0; i < len; i++) {
121                 if (fprintf(f, "%02x", *p++) != 2)
122                         return -1;
123                 bytes_on_line++;
124                 if (bytes_on_line >= 16) {
125                         if (fprintf(f, "\n") != 1)
126                                 return -1;
127                         bytes_on_line = 0;
128                 }
129         }
130         return 0;
131 }
132
133 int write_to_file(
134         struct eeprom_table *eeprom_table,
135         struct capabilities *caps,
136         struct capkey *key,
137         unsigned int marker,
138         FILE *f)
139 {
140         fprintf(f, "%s\n", marker_string(marker, 0));
141         fprintf(f, "Version: 1.0\n");
142         fprintf(f, "Timestamp: %u\n", caps->timestamp);
143         fprintf(f, "Serial: %.*s\n", LABEL_SIZE, eeprom_table->label);
144         fprintf(f, "Capabilities.Port.FXS: %d\n", caps->ports_fxs);
145         fprintf(f, "Capabilities.Port.FXO: %d\n", caps->ports_fxo);
146         fprintf(f, "Capabilities.Port.BRI: %d\n", caps->ports_bri);
147         fprintf(f, "Capabilities.Port.PRI: %d\n", caps->ports_pri);
148         fprintf(f, "Capabilities.Port.ECHO: %d\n", caps->ports_echo);
149         fprintf(f, "Capabilities.Twinstar: %d\n", CAP_EXTRA_TWINSTAR(caps));
150         fprintf(f, "Data:\n");
151         bin_to_file(eeprom_table, sizeof(*eeprom_table), f);
152         bin_to_file(caps, sizeof(*caps), f);
153         bin_to_file(key, sizeof(*key), f);
154         bin_to_file(NULL, 0, f);
155         fprintf(f, "%s\n", marker_string(marker, 1));
156         return 0;
157 }
158
159 /*
160  * Removes whitespace on both sizes of the string.
161  * Returns a pointer to the first non-space char. The string
162  * is modified in place to trim trailing whitespace.
163  * If the whole string is whitespace, returns NULL.
164  */
165 static char *trim(char *s)
166 {
167         int len = strlen(s);
168         while (len > 0 && isspace(s[len-1])) {
169                 len--;
170         }
171         if (len == 0)
172                 return NULL;
173         s[len] = '\0';
174         while (isspace(*s))
175                 s++;
176         /* *s is not a space, since in this case we'd return NULL above */
177         return s;
178 }
179
180 static int get_key_value(char *line, char **key, char **value)
181 {
182         char *p = strchr(line, ':');
183         if (p == NULL)
184                 return -1;
185         *p = '\0';
186         *key = trim(line);
187         *value = trim(p + 1);
188         return 0;
189 }
190
191 static int hex_digit_to_int(char c)
192 {
193         if (c >= '0' && c <= '9')
194                 return c - '0';
195         else if (c >= 'a' && c <= 'f')
196                 return c - 'a' + 10;
197         else
198                 return -1;
199 }
200
201 static int str_to_bin(char *line, void *buf, int maxlen)
202 {
203         static int offset;
204         unsigned char *p = buf;
205         if (strlen(line) % 2 != 0)
206                 return -1;
207         while (offset < maxlen && *line) {
208                 uint8_t value;
209                 char c = hex_digit_to_int(*line++);
210                 if (c < 0 || *line == '\0')
211                         return -1;
212                 value = c << 4;
213                 c = hex_digit_to_int(*line++);
214                 if (c < 0)
215                         return -1;
216                 value |= c;
217                 p[offset++] = value;
218         }
219         if (offset == maxlen && *line)
220                 return -1;
221         return offset;
222 }
223
224 int read_from_file(
225         struct eeprom_table *eeprom_table,
226         struct capabilities *caps,
227         struct capkey *capkey,
228         unsigned int *used_marker,
229         FILE *f)
230 {
231         char buf[256];
232         char *line, *key, *value;
233         int lineno = 0;
234         unsigned int license_marker_begin = 0;
235         unsigned int license_marker_end;
236         struct table {
237                 struct eeprom_table eeprom_table;
238                 struct capabilities capabilities;
239                 struct capkey capkey;
240         } PACKED table;
241         enum PARSE_STATES {
242                 STATE_BEFORE_LICENSE = 0,
243                 STATE_EXPECT_VERSION = 1,
244                 STATE_EXPECT_DATA = 2,
245                 STATE_READ_DATA = 3,
246                 STATE_AFTER_LICENSE = 4,
247         } state = STATE_BEFORE_LICENSE;
248
249         memset(&table, 0, sizeof(struct table));
250         /*
251          * states:
252          * 0: start - before BEGIN_LICENSE_BLOCK line. on BEGIN_LICENSE_BLOCK line goto 1.
253          * 1: read Version, goto 2. if not version line then error.
254          * 2: after BEGIN line. split line into key:value. if line is Data:, goto 3.
255          * 3: read binary data. if line is END_LICENSE_BLOCK goto 4.
256          * 4: END_LICENSE_BLOCK - ignore lines.
257          */
258         while (fgets(buf, 256, f) != NULL) {
259                 lineno++;
260                 int len = strlen(buf);
261                 if (len > 0 && buf[len-1] != '\n') {
262                         ERR("Line %d: Line too long\n", lineno);
263                         return -1;
264                 }
265                 line = trim(buf);
266                 if (line == NULL) {
267                         if (state > STATE_BEFORE_LICENSE && state < STATE_AFTER_LICENSE) {
268                                 ERR("Line %d: Empty line\n", lineno);
269                                 return -1;
270                         }
271                         else
272                                 continue;
273                 }
274                 switch (state) {
275                         case STATE_BEFORE_LICENSE:
276                                 license_marker_begin = marker_find(line, 0);
277                                 if (license_marker_begin)
278                                         state = STATE_EXPECT_VERSION;
279                                 else {
280                                         ERR("Line %d: Invalid license begin block\n", lineno);
281                                         return -1;
282                                 }
283                                 break;
284                         case STATE_EXPECT_VERSION:
285                                 if (get_key_value(line, &key, &value) < 0) {
286                                         ERR("Line %d: Can't parse line\n", lineno);
287                                         return -1;
288                                 }
289                                 if (strcmp(key, "Version") == 0) {
290                                         if (strcmp(value, "1.0") == 0) {
291                                                 state = STATE_EXPECT_DATA;
292                                         } else {
293                                                 ERR("Line %d: Unknown license file version '%s', need version '1.0'\n", lineno, value);
294                                                 return -1;
295                                         }
296                                 } else {
297                                         ERR("Line %d: No license file version\n", lineno);
298                                         return -1;
299                                 }
300                                 break;
301                         case STATE_EXPECT_DATA:
302                                 if (get_key_value(line, &key, &value) < 0) {
303                                         ERR("Line %d: Can't parse line\n", lineno);
304                                         return -1;
305                                 }
306                                 if (strcmp(key, "Data") == 0) {
307                                         state = STATE_READ_DATA;
308                                         break;
309                                 }
310                                 break;
311                         case STATE_READ_DATA:
312                                 license_marker_end = marker_find(line, 1);
313                                 if (license_marker_end) {
314                                         if (license_marker_end != license_marker_begin) {
315                                                 ERR("Line %d: End marker != Begin marker\n", lineno);
316                                                 return -1;
317                                         }
318                                         state = STATE_AFTER_LICENSE;
319                                         break;
320                                 }
321                                 if (str_to_bin(line, &table, sizeof(table)) < 0) {
322                                         ERR("Line %d: Error in data block\n", lineno);
323                                         return -1;
324                                 }
325                                 break;
326                         case STATE_AFTER_LICENSE:
327                                 break;
328
329                 }
330         }
331         if (state != STATE_AFTER_LICENSE) {
332                 ERR("Invalid license file\n");
333                 return -1;
334         }
335         memcpy(eeprom_table, &table.eeprom_table, sizeof(*eeprom_table));
336         memcpy(caps, &table.capabilities, sizeof(*caps));
337         memcpy(capkey, &table.capkey, sizeof(*capkey));
338         return 0;
339 }
340