Initial commit.
[routeros-api.git] / src / main.c
1 /**
2  * libmikrotik - src/main.c
3  * Copyright (C) 2009  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <stdint.h>
26 #include <inttypes.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <assert.h>
30
31 #include "routeros_api.h"
32
33 /*
34  * Private structures
35  */
36 struct mt_connection_s
37 {
38         int fd;
39 };
40
41 struct mt_reply_s
42 {
43         unsigned int params_num;
44         char *status;
45         char **keys;
46         char **values;
47
48         mt_reply_t *next;
49 };
50
51 /*
52  * Private functions
53  */
54 static mt_reply_t *reply_alloc (void) /* {{{ */
55 {
56         mt_reply_t *r;
57
58         r = malloc (sizeof (*r));
59         if (r == NULL)
60                 return (NULL);
61
62         memset (r, 0, sizeof (*r));
63         r->keys = NULL;
64         r->values = NULL;
65         r->next = NULL;
66
67         return (r);
68 } /* }}} mt_reply_s *reply_alloc */
69
70 static void reply_free (mt_reply_t *r) /* {{{ */
71 {
72         mt_reply_t *next;
73
74         if (r == NULL)
75                 return;
76
77         next = r->next;
78
79         free (r->keys);
80         free (r->values);
81         free (r);
82
83         reply_free (next);
84 } /* }}} void reply_free */
85
86 static int buffer_init (char **ret_buffer, size_t *ret_buffer_size) /* {{{ */
87 {
88         if ((ret_buffer == NULL) || (ret_buffer_size == NULL))
89                 return (EINVAL);
90
91         if (*ret_buffer_size < 1)
92                 return (EINVAL);
93
94         return (0);
95 } /* }}} int buffer_init */
96
97 static int buffer_add (char **ret_buffer, size_t *ret_buffer_size, /* {{{ */
98                 const char *string)
99 {
100         char *buffer;
101         size_t buffer_size;
102         size_t string_size;
103         size_t req_size;
104
105         if ((ret_buffer == NULL) || (ret_buffer_size == NULL) || (string == NULL))
106                 return (EINVAL);
107
108         buffer = *ret_buffer;
109         buffer_size = *ret_buffer_size;
110
111         string_size = strlen (string);
112         if (string_size == 0)
113                 return (EINVAL);
114
115         if (string_size >= 0x10000000)
116                 req_size = 5 + string_size;
117         else if (string_size >= 0x200000)
118                 req_size = 4 + string_size;
119         else if (string_size >= 0x4000)
120                 req_size = 3 + string_size;
121         else if (string_size >= 0x80)
122                 req_size = 2 + string_size;
123         else
124                 req_size = 1 + string_size;
125
126         if (*ret_buffer_size < req_size)
127                 return (ENOMEM);
128
129         if (string_size >= 0x10000000)
130         {
131                 buffer[0] = 0xF0;
132                 buffer[1] = (string_size >> 24) & 0xff;
133                 buffer[2] = (string_size >> 16) & 0xff;
134                 buffer[3] = (string_size >>  8) & 0xff;
135                 buffer[4] = (string_size      ) & 0xff;
136                 buffer += 5;
137                 buffer_size -= 5;
138         }
139         else if (string_size >= 0x200000)
140         {
141                 buffer[0] = (string_size >> 24) & 0x1f;
142                 buffer[0] |= 0xE0;
143                 buffer[1] = (string_size >> 16) & 0xff;
144                 buffer[2] = (string_size >>  8) & 0xff;
145                 buffer[3] = (string_size      ) & 0xff;
146                 buffer += 4;
147                 buffer_size -= 4;
148         }
149         else if (string_size >= 0x4000)
150         {
151                 buffer[0] = (string_size >> 16) & 0x3f;
152                 buffer[0] |= 0xC0;
153                 buffer[1] = (string_size >>  8) & 0xff;
154                 buffer[2] = (string_size      ) & 0xff;
155                 buffer += 3;
156                 buffer_size -= 3;
157         }
158         else if (string_size >= 0x80)
159         {
160                 buffer[0] = (string_size >>  8) & 0x7f;
161                 buffer[0] |= 0x80;
162                 buffer[1] = (string_size      ) & 0xff;
163                 buffer += 2;
164                 buffer_size -= 2;
165         }
166         else /* if (string_size <= 0x7f) */
167         {
168                 buffer[0] = (char) string_size;
169                 buffer += 1;
170                 buffer_size -= 1;
171         }
172
173         assert (buffer_size >= string_size);
174         memcpy (buffer, string, string_size);
175         buffer += string_size;
176         buffer_size -= string_size;
177
178         *ret_buffer = buffer;
179         *ret_buffer_size = buffer_size;
180
181         return (0);
182 } /* }}} int buffer_add */
183
184 static int buffer_end (char **ret_buffer, size_t *ret_buffer_size) /* {{{ */
185 {
186         if ((ret_buffer == NULL) || (ret_buffer_size == NULL))
187                 return (EINVAL);
188
189         if (*ret_buffer_size < 1)
190                 return (EINVAL);
191
192         /* Add empty word. */
193         (*ret_buffer)[0] = 0;
194         (*ret_buffer)++;
195         (*ret_buffer_size)--;
196
197         return (0);
198 } /* }}} int buffer_end */
199
200 static int buffer_decode_next (char **ret_buffer, size_t *ret_buffer_size, /* {{{ */
201                 char *dst, size_t *dst_size)
202 {
203         uint8_t *buffer;
204         size_t buffer_size;
205         size_t req_size;
206
207         if ((ret_buffer == NULL) || (ret_buffer_size == NULL) || (dst_size == NULL))
208                 return (EINVAL);
209
210         buffer = (uint8_t *) (*ret_buffer);
211         buffer_size = *ret_buffer_size;
212
213         if (buffer_size < 1)
214                 return (EINVAL);
215
216         /* Calculate `req_size' and update `buffer' and `buffer_size'. */
217         if (buffer[0] == 0xF0) /* {{{ */
218         {
219                 if (buffer_size < (0x10000000 + 5))
220                         return (EPROTO);
221                 req_size = (buffer[1] << 24)
222                         | (buffer[2] << 16)
223                         | (buffer[3] << 8)
224                         | buffer[4];
225                 buffer += 5;
226                 buffer_size -= 5;
227         }
228         else if ((buffer[0] & 0xE0) == 0xE0)
229         {
230                 if (buffer_size < (0x200000 + 4))
231                         return (EPROTO);
232                 req_size = ((buffer[0] & 0x1F) << 24)
233                         | (buffer[1] << 16)
234                         | (buffer[2] << 8)
235                         | buffer[3];
236                 buffer += 4;
237                 buffer_size -= 4;
238         }
239         else if ((buffer[0] & 0xC0) == 0xC0)
240         {
241                 if (buffer_size < (0x4000 + 3))
242                         return (EPROTO);
243                 req_size = ((buffer[0] & 0x3F) << 16)
244                         | (buffer[1] << 8)
245                         | buffer[2];
246                 buffer += 3;
247                 buffer_size -= 3;
248         }
249         else if ((buffer[0] & 0x80) == 0x80)
250         {
251                 if (buffer_size < (0x80 + 2))
252                         return (EPROTO);
253                 req_size = ((buffer[0] & 0x7F) << 8) | buffer[1];
254                 buffer += 2;
255                 buffer_size -= 2;
256         }
257         else if ((buffer[0] & 0x80) == 0)
258         {
259                 req_size = buffer[0];
260                 buffer += 1;
261                 buffer_size -= 1;
262         }
263         else
264         {
265                 /* First nibble is `F' but second nibble is not `0'. */
266                 return (EPROTO);
267         } /* }}} */
268
269         if (buffer_size < req_size)
270                 return (EPROTO);
271
272         if (dst == NULL)
273         {
274                 *dst_size = (req_size + 1);
275                 return (0);
276         }
277
278         if (*dst_size <= req_size)
279                 return (ENOMEM);
280
281         memcpy (dst, buffer, req_size);
282         dst[req_size] = 0;
283         *dst_size = (req_size + 1);
284
285         assert (buffer_size >= req_size);
286         buffer += req_size;
287         buffer_size -= req_size;
288
289         *ret_buffer = buffer;
290         *ret_buffer_size = buffer_size;
291         
292         return (0);
293 } /* }}} int buffer_decode_next */
294
295 /*
296  * Public functions
297  */
298 mt_connection_t *mt_connect (const char *node, const char *service,
299                 const char *username, const char *password);
300 int mt_disconnect (mt_connection_t *con);
301
302 int mt_query (mt_connection_t *c,
303                 const char *command,
304                 size_t args_num, const char * const *args,
305                 mt_reply_handler_t *handler, void *user_data)
306 {
307         char buffer[4096];
308         char *buffer_ptr;
309         size_t buffer_size;
310
311         size_t i;
312         int status;
313
314         buffer_ptr = buffer;
315         buffer_size = sizeof (buffer);
316
317         status = buffer_init (&buffer_ptr, &buffer_size);
318         if (status != 0)
319                 return (status);
320
321         status = buffer_add (&buffer_ptr, &buffer_size, command);
322         if (status != 0)
323                 return (status);
324
325         for (i = 0; i < args_num; i++)
326         {
327                 if (args[i] == NULL)
328                         return (EINVAL);
329
330                 status = buffer_add (&buffer_ptr, &buffer_size, args[i]);
331                 if (status != 0)
332                         return (status);
333         }
334
335         status = buffer_end (&buffer_ptr, &buffer_size);
336         if (status != 0)
337                 return (status);
338
339         /* FIXME: Send out the buffer and read back results. */
340 }
341
342 const mt_reply_t *mt_reply_next (const mt_reply_t *r) /* {{{ */
343 {
344         if (r == NULL)
345                 return (NULL);
346
347         return (r->next);
348 } /* }}} mt_reply_t *mt_reply_next */
349
350 int mt_reply_num (const mt_reply_t *r) /* {{{ */
351 {
352         int ret;
353         const mt_reply_t *ptr;
354
355         ret = 0;
356         for (ptr = r; ptr != NULL; ptr = ptr->next)
357                 ret++;
358
359         return (ret);
360 } /* }}} int mt_reply_num */
361
362 const char *mt_reply_param_key_by_index (const mt_reply_t *r, /* {{{ */
363                 unsigned int index)
364 {
365         if (r == NULL)
366                 return (NULL);
367
368         if (index >= r->params_num)
369                 return (NULL);
370
371         return (r->keys[index]);
372 } /* }}} char *mt_reply_param_key_by_index */
373
374 const char *mt_reply_param_val_by_index (const mt_reply_t *r, /* {{{ */
375                 unsigned int index)
376 {
377         if (r == NULL)
378                 return (NULL);
379
380         if (index >= r->params_num)
381                 return (NULL);
382
383         return (r->values[index]);
384 } /* }}} char *mt_reply_param_key_by_index */
385
386 const char *mt_reply_param_val_by_key (const mt_reply_t *r, /* {{{ */
387                 const char *key)
388 {
389         unsigned int i;
390
391         if ((r == NULL) || (key == NULL))
392                 return (NULL);
393
394         for (i = 0; i < r->params_num; i++)
395                 if (strcmp (r->keys[i], key) == 0)
396                         return (r->values[i]);
397
398         return (NULL);
399 } /* }}} char *mt_reply_param_val_by_key */
400
401 /* vim: set ts=2 sw=2 noet fdm=marker : */