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 = strchr(line, '#'); |
355 |
if (line == hashmarker) |
356 |
line[0] = '\0'; |
357 |
else { |
358 |
while (hashmarker != NULL) { |
359 |
--hashmarker; |
360 |
if (isblank(hashmarker[0])) { |
361 |
hashmarker[0] = '\0'; |
362 |
break; |
363 |
} else { |
364 |
/* |
365 |
* false positive; '#' occured |
366 |
* within a string |
367 |
*/ |
368 |
hashmarker = strchr(hashmarker+2, '#'); |
369 |
} |
370 |
} |
371 |
} |
372 |
c = line; |
373 |
while (isspace(*c)) |
374 |
c++; |
375 |
/* Ignore blank lines. */ |
376 |
if (c[0] == '\0') |
377 |
continue; |
378 |
|
379 |
/* parse the line into a key and value pair */ |
380 |
get_key(session, c, &key, &value); |
381 |
|
382 |
process_line(session, key, value); |
383 |
} while (!feof(config_file)); |
384 |
|
385 |
/* Free buffer and close file. */ |
386 |
free(line); |
387 |
fclose(config_file); |
388 |
} |
389 |
|