bti.xml: docsheet url cleanup
[gregoa/bti.git] / config.c
1 /*
2  * Copyright (C) 2008-2011 Greg Kroah-Hartman <greg@kroah.com>
3  * Copyright (C) 2009 Bart Trojanowski <bart@jukie.net>
4  * Copyright (C) 2009-2010 Amir Mohammad Saied <amirsaied@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation version 2 of the License.
9  *
10  * This program 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 along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19
20 #define _GNU_SOURCE
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <time.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/wait.h>
35 #include <curl/curl.h>
36 #include <libxml/xmlmemory.h>
37 #include <libxml/parser.h>
38 #include <libxml/tree.h>
39 #include <pcre.h>
40 #include <termios.h>
41 #include <dlfcn.h>
42 #include <oauth.h>
43 #include "bti.h"
44
45 typedef int (*config_function_callback)(struct session *session, char *value);
46
47 struct config_function {
48         const char *key;
49         config_function_callback callback;
50 };
51
52 /*
53  * get_key function
54  *
55  * Read a line from the config file and assign it a key and a value.
56  *
57  * This logic taken almost identically from taken from udev's rule file parsing
58  * logic in the file udev-rules.c, written by Kay Sievers and licensed under
59  * the GPLv2+.  I hate writing parsers, so it makes sense to borrow working
60  * logic from those smarter than I...
61  */
62 static int get_key(struct session *session, char *line, char **key, char **value)
63 {
64         char *linepos;
65         char *temp;
66         char terminator;
67
68         linepos = line;
69         if (linepos == NULL || linepos[0] == '\0')
70                 return -1;
71
72         /* skip whitespace */
73         while (isspace(linepos[0]) || linepos[0] == ',')
74                 linepos++;
75         if (linepos[0] == '\0')
76                 return -1;
77
78         *key = linepos;
79
80         for (;;) {
81                 linepos++;
82                 if (linepos[0] == '\0')
83                         return -1;
84                 if (isspace(linepos[0]))
85                         break;
86                 if (linepos[0] == '=')
87                         break;
88         }
89
90         /* remember the end of the key */
91         temp = linepos;
92
93         /* skip whitespace after key */
94         while (isspace(linepos[0]))
95                 linepos++;
96         if (linepos[0] == '\0')
97                 return -1;
98
99         /* make sure this is a = operation */
100         /*
101          * udev likes to check for += and == and lots of other complex
102          * assignments that we don't care about.
103          */
104         if (linepos[0] == '=')
105                 linepos++;
106         else
107                 return -1;
108
109         /* terminate key */
110         temp[0] = '\0';
111
112         /* skip whitespace after opearator */
113         while (isspace(linepos[0]))
114                 linepos++;
115         if (linepos[0] == '\0')
116                 return -1;
117
118         /*
119          * if the value is quoted, then terminate on a ", otherwise space is
120          * the terminator.
121          * */
122         if (linepos[0] == '"') {
123                 terminator = '"';
124                 linepos++;
125         } else
126                 terminator = ' ';
127
128         /* get the value */
129         *value = linepos;
130
131         /* terminate */
132         temp = strchr(linepos, terminator);
133         if (temp) {
134                 temp[0] = '\0';
135                 temp++;
136         } else {
137                 /*
138                  * perhaps we just hit the end of the line, so there would not
139                  * be a terminator, so just use the whole rest of the string as
140                  * the value.
141                  */
142         }
143         /* printf("%s = %s\n", *key, *value); */
144         return 0;
145 }
146
147 static int session_string(char **field, char *value)
148 {
149         char *string;
150
151         string = strdup(value);
152         if (string) {
153                 if (*field)
154                         free(*field);
155                 *field = string;
156                 return 0;
157         }
158         return -1;
159 }
160
161 static int session_bool(int *field, char *value)
162 {
163         if ((strncasecmp(value, "true", 4) == 0) ||
164             strncasecmp(value, "yes", 3) == 0)
165                 *field = 1;
166         return 0;
167 }
168
169 static int account_callback(struct session *session, char *value)
170 {
171         return session_string(&session->account, value);
172 }
173
174 static int password_callback(struct session *session, char *value)
175 {
176         return session_string(&session->password, value);
177 }
178
179 static int proxy_callback(struct session *session, char *value)
180 {
181         return session_string(&session->proxy, value);
182 }
183
184 static int user_callback(struct session *session, char *value)
185 {
186         return session_string(&session->user, value);
187 }
188
189 static int consumer_key_callback(struct session *session, char *value)
190 {
191         return session_string(&session->consumer_key, value);
192 }
193
194 static int consumer_secret_callback(struct session *session, char *value)
195 {
196         return session_string(&session->consumer_secret, value);
197 }
198
199 static int access_token_key_callback(struct session *session, char *value)
200 {
201         return session_string(&session->access_token_key, value);
202 }
203
204 static int access_token_secret_callback(struct session *session, char *value)
205 {
206         return session_string(&session->access_token_secret, value);
207 }
208
209 static int logfile_callback(struct session *session, char *value)
210 {
211         return session_string(&session->logfile, value);
212 }
213
214 static int replyto_callback(struct session *session, char *value)
215 {
216         return session_string(&session->replyto, value);
217 }
218
219 static int retweet_callback(struct session *session, char *value)
220 {
221         return session_string(&session->retweet, value);
222 }
223
224 static int host_callback(struct session *session, char *value)
225 {
226         if (strcasecmp(value, "twitter") == 0) {
227                 session->host = HOST_TWITTER;
228                 session->hosturl = strdup(twitter_host);
229                 session->hostname = strdup(twitter_name);
230         } else if (strcasecmp(value, "identica") == 0) {
231                 session->host = HOST_IDENTICA;
232                 session->hosturl = strdup(identica_host);
233                 session->hostname = strdup(identica_name);
234         } else {
235                 session->host = HOST_CUSTOM;
236                 session->hosturl = strdup(value);
237                 session->hostname = strdup(value);
238         }
239         return 0;
240 }
241
242 static int action_callback(struct session *session, char *value)
243 {
244         if (strcasecmp(value, "update") == 0)
245                 session->action = ACTION_UPDATE;
246         else if (strcasecmp(value, "friends") == 0)
247                 session->action = ACTION_FRIENDS;
248         else if (strcasecmp(value, "user") == 0)
249                 session->action = ACTION_USER;
250         else if (strcasecmp(value, "replies") == 0)
251                 session->action = ACTION_REPLIES;
252         else if (strcasecmp(value, "public") == 0)
253                 session->action = ACTION_PUBLIC;
254         else if (strcasecmp(value, "group") == 0)
255                 session->action = ACTION_GROUP;
256         else
257                 session->action= ACTION_UNKNOWN;
258         return 0;
259 }
260
261 static int verbose_callback(struct session *session, char *value)
262 {
263         return session_bool(&session->verbose, value);
264 }
265
266 static int shrink_urls_callback(struct session *session, char *value)
267 {
268         return session_bool(&session->shrink_urls, value);
269 }
270
271 /*
272  * List of all of the config file options.
273  *
274  * To add a new option, just add a string for the key name, and the callback
275  * function that will be called with the value read from the config file.
276  *
277  * Make sure the table is NULL terminated, otherwise bad things will happen.
278  */
279 static struct config_function config_table[] = {
280         { "account", account_callback },
281         { "password", password_callback },
282         { "proxy", proxy_callback },
283         { "user", user_callback },
284         { "consumer_key", consumer_key_callback },
285         { "consumer_secret", consumer_secret_callback },
286         { "access_token_key", access_token_key_callback },
287         { "access_token_secret", access_token_secret_callback },
288         { "logfile", logfile_callback },
289         { "replyto", replyto_callback },
290         { "retweet", retweet_callback },
291         { "host", host_callback },
292         { "action", action_callback },
293         { "verbose", verbose_callback },
294         { "shrink-urls", shrink_urls_callback },
295         { NULL, NULL }
296 };
297
298 static void process_line(struct session *session, char *key, char *value)
299 {
300         struct config_function *item;
301         int result;
302
303         if (key == NULL || value == NULL)
304                 return;
305
306         item = &config_table[0];
307         for (;;) {
308                 if (item->key == NULL || item->callback == NULL)
309                         break;
310
311                 if (strncasecmp(item->key, key, strlen(item->key)) == 0) {
312                         /*
313                          * printf("calling %p, for key = '%s' and value = * '%s'\n",
314                          *        item->callback, key, value);
315                          */
316                         result = item->callback(session, value);
317                         if (!result)
318                                 return;
319                 }
320                 item++;
321         }
322 }
323
324 void bti_parse_configfile(struct session *session)
325 {
326         FILE *config_file;
327         char *line = NULL;
328         char *key = NULL;
329         char *value = NULL;
330         char *hashmarker;
331         size_t len = 0;
332         ssize_t n;
333         char *c;
334
335         config_file = fopen(session->configfile, "r");
336
337         /* No error if file does not exist or is unreadable.  */
338         if (config_file == NULL)
339                 return;
340
341         do {
342                 n = getline(&line, &len, config_file);
343                 if (n < 0)
344                         break;
345                 if (line[n - 1] == '\n')
346                         line[n - 1] = '\0';
347
348                 /*
349                  * '#' is comment markers, like bash style but it is a valid
350                  * character in some fields, so only treat it as a comment
351                  * marker if it occurs at the beginning of the line, or after
352                  * whitespace
353                  */
354                 hashmarker = strchrnul(line, '#');
355                 if (line == hashmarker)
356                         line[0] = '\0';
357                 else {
358                         while (hashmarker[0] != '\0') {
359                                 --hashmarker;
360                                 if (isblank(hashmarker[0]))
361                                         hashmarker[0] = '\0';
362                                 else {
363                                         /*
364                                          * false positive; '#' occured
365                                          * within a string
366                                          */
367                                         hashmarker = strchrnul(hashmarker+2, '#');
368                                 }
369                         }
370                 }
371                 c = line;
372                 while (isspace(*c))
373                         c++;
374                 /* Ignore blank lines.  */
375                 if (c[0] == '\0')
376                         continue;
377
378                 /* parse the line into a key and value pair */
379                 get_key(session, c, &key, &value);
380
381                 process_line(session, key, value);
382         } while (!feof(config_file));
383
384         /* Free buffer and close file.  */
385         free(line);
386         fclose(config_file);
387 }
388