/[debian]/bti/branches/upstream/current/bti.c
ViewVC logotype

Contents of /bti/branches/upstream/current/bti.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1511 - (show annotations)
Mon Jan 12 21:38:36 2009 UTC (11 years, 9 months ago) by gregoa
File MIME type: text/plain
File size: 11387 byte(s)
[svn-upgrade] Integrating new upstream version, bti (010)
1 /*
2 * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stddef.h>
21 #include <string.h>
22 #include <getopt.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <curl/curl.h>
29 #include "bti_version.h"
30
31
32 #define zalloc(size) calloc(size, 1)
33
34 #define dbg(format, arg...) \
35 do { \
36 if (debug) \
37 printf("%s: " format , __func__ , ## arg ); \
38 } while (0)
39
40
41 static int debug = 0;
42
43 enum host {
44 HOST_TWITTER = 0,
45 HOST_IDENTICA = 1,
46 };
47
48 struct session {
49 char *password;
50 char *account;
51 char *tweet;
52 char *proxy;
53 int bash;
54 enum host host;
55 };
56
57 struct bti_curl_buffer {
58 char *data;
59 int length;
60 };
61
62 static void display_help(void)
63 {
64 fprintf(stdout, "bti - send tweet to twitter\n");
65 fprintf(stdout, "Version: " BTI_VERSION "\n");
66 fprintf(stdout, "Usage:\n");
67 fprintf(stdout, " bti [options]\n");
68 fprintf(stdout, "options are:\n");
69 fprintf(stdout, " --account accountname\n");
70 fprintf(stdout, " --password password\n");
71 fprintf(stdout, " --proxy PROXY:PORT\n");
72 fprintf(stdout, " --host HOST\n");
73 fprintf(stdout, " --bash\n");
74 fprintf(stdout, " --debug\n");
75 fprintf(stdout, " --version\n");
76 fprintf(stdout, " --help\n");
77 }
78
79 static void display_version(void)
80 {
81 fprintf(stdout, "bti - version %s\n", BTI_VERSION);
82 }
83
84 static char *get_string_from_stdin(void)
85 {
86 char *temp;
87 char *string;
88
89 string = zalloc(1000);
90 if (!string)
91 return NULL;
92
93 if (!fgets(string, 999, stdin))
94 return NULL;
95 temp = strchr(string, '\n');
96 *temp = '\0';
97 return string;
98 }
99
100 static struct session *session_alloc(void)
101 {
102 struct session *session;
103
104 session = zalloc(sizeof(*session));
105 if (!session)
106 return NULL;
107 return session;
108 }
109
110 static void session_free(struct session *session)
111 {
112 if (!session)
113 return;
114 free(session->password);
115 free(session->account);
116 free(session->tweet);
117 free(session->proxy);
118 free(session);
119 }
120
121 static struct bti_curl_buffer *bti_curl_buffer_alloc(void)
122 {
123 struct bti_curl_buffer *buffer;
124
125 buffer = zalloc(sizeof(*buffer));
126 if (!buffer)
127 return NULL;
128
129 /* start out with a data buffer of 1 byte to
130 * make the buffer fill logic simpler */
131 buffer->data = zalloc(1);
132 if (!buffer->data) {
133 free(buffer);
134 return NULL;
135 }
136 buffer->length = 0;
137 return buffer;
138 }
139
140 static void bti_curl_buffer_free(struct bti_curl_buffer *buffer)
141 {
142 if (!buffer)
143 return;
144 free(buffer->data);
145 free(buffer);
146 }
147
148 static const char *twitter_url = "https://twitter.com/statuses/update.xml";
149 static const char *identica_url = "http://identi.ca/api/statuses/update.xml";
150
151 static CURL *curl_init(void)
152 {
153 CURL *curl;
154
155 curl = curl_easy_init();
156 if (!curl) {
157 fprintf(stderr, "Can not init CURL!\n");
158 return NULL;
159 }
160 /* some ssl sanity checks on the connection we are making */
161 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
162 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
163 return curl;
164 }
165
166 size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *userp)
167 {
168 struct bti_curl_buffer *curl_buf = userp;
169 size_t buffer_size = size * nmemb;
170 char *temp;
171
172 if ((!buffer) || (!buffer_size) || (!curl_buf))
173 return -EINVAL;
174
175 /* add to the data we already have */
176 temp = zalloc(curl_buf->length + buffer_size + 1);
177 if (!temp)
178 return -ENOMEM;
179
180 memcpy(temp, curl_buf->data, curl_buf->length);
181 free(curl_buf->data);
182 curl_buf->data = temp;
183 memcpy(&curl_buf->data[curl_buf->length], (char *)buffer, buffer_size);
184 curl_buf->length += buffer_size;
185
186 dbg("%s\n", curl_buf->data);
187
188 return buffer_size;
189 }
190
191 static int send_tweet(struct session *session)
192 {
193 char user_password[500];
194 char data[500];
195 struct bti_curl_buffer *curl_buf;
196 CURL *curl = NULL;
197 CURLcode res;
198 struct curl_httppost *formpost = NULL;
199 struct curl_httppost *lastptr = NULL;
200 struct curl_slist *slist = NULL;
201
202 if (!session)
203 return -EINVAL;
204
205 curl_buf = bti_curl_buffer_alloc();
206 if (!curl_buf)
207 return -ENOMEM;
208
209 snprintf(user_password, sizeof(user_password), "%s:%s",
210 session->account, session->password);
211 snprintf(data, sizeof(data), "status=\"%s\"", session->tweet);
212
213 curl = curl_init();
214 if (!curl)
215 return -EINVAL;
216
217 curl_formadd(&formpost, &lastptr,
218 CURLFORM_COPYNAME, "status",
219 CURLFORM_COPYCONTENTS, session->tweet,
220 CURLFORM_END);
221
222 curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
223
224 switch (session->host) {
225 case HOST_TWITTER:
226 curl_easy_setopt(curl, CURLOPT_URL, twitter_url);
227 /*
228 * twitter doesn't like the "Expect: 100-continue" header
229 * anymore, so turn it off.
230 */
231 slist = curl_slist_append(slist, "Expect:");
232 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
233 break;
234 case HOST_IDENTICA:
235 curl_easy_setopt(curl, CURLOPT_URL, identica_url);
236 break;
237 }
238
239 if (session->proxy)
240 curl_easy_setopt(curl, CURLOPT_PROXY, session->proxy);
241
242 if (debug)
243 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
244 curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
245
246 dbg("user_password = %s\n", user_password);
247 dbg("data = %s\n", data);
248 dbg("proxy = %s\n", session->proxy);
249
250 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
251 curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_buf);
252 res = curl_easy_perform(curl);
253 if (res && !session->bash) {
254 fprintf(stderr, "error(%d) trying to send tweet\n", res);
255 return -EINVAL;
256 }
257
258 curl_easy_cleanup(curl);
259 curl_formfree(formpost);
260 bti_curl_buffer_free(curl_buf);
261 return 0;
262 }
263
264 static void parse_configfile(struct session *session)
265 {
266 FILE *config_file;
267 char *line = NULL;
268 size_t len = 0;
269 char *account = NULL;
270 char *password = NULL;
271 char *host = NULL;
272 char *proxy = NULL;
273 char *file;
274 char *home = getenv("HOME");
275
276 /* config file is ~/.bti */
277 file = alloca(strlen(home) + 7);
278
279 sprintf(file, "%s/.bti", home);
280
281 config_file = fopen(file, "r");
282
283 /* No error if file does not exist or is unreadable. */
284 if (config_file == NULL)
285 return;
286
287 do {
288 ssize_t n = getline(&line, &len, config_file);
289 if (n < 0)
290 break;
291 if (line[n - 1] == '\n')
292 line[n - 1] = '\0';
293 /* Parse file. Format is the usual value pairs:
294 account=name
295 passwort=value
296 # is a comment character
297 */
298 *strchrnul(line, '#') = '\0';
299 char *c = line;
300 while (isspace(*c))
301 c++;
302 /* Ignore blank lines. */
303 if (c[0] == '\0')
304 continue;
305
306 if (!strncasecmp(c, "account", 7) && (c[7] == '=')) {
307 c += 8;
308 if (c[0] != '\0')
309 account = strdup(c);
310 } else if (!strncasecmp(c, "password", 8) &&
311 (c[8] == '=')) {
312 c += 9;
313 if (c[0] != '\0')
314 password = strdup(c);
315 } else if (!strncasecmp(c, "host", 4) &&
316 (c[4] == '=')) {
317 c += 5;
318 if (c[0] != '\0')
319 host = strdup(c);
320 } else if (!strncasecmp(c, "proxy", 5) &&
321 (c[5] == '=')) {
322 c += 6;
323 if (c[0] != '\0')
324 proxy = strdup(c);
325 }
326 } while (!feof(config_file));
327
328 if (password)
329 session->password = password;
330 if (account)
331 session->account = account;
332 if (host) {
333 if (strcasecmp(host, "twitter") == 0)
334 session->host = HOST_TWITTER;
335 if (strcasecmp(host, "identica") == 0)
336 session->host = HOST_IDENTICA;
337 free(host);
338 }
339 if (proxy) {
340 if (session->proxy)
341 free(session->proxy);
342 session->proxy = proxy;
343 }
344
345 /* Free buffer and close file. */
346 free(line);
347 fclose(config_file);
348 }
349
350 int main(int argc, char *argv[], char *envp[])
351 {
352 static const struct option options[] = {
353 { "debug", 0, NULL, 'd' },
354 { "account", 1, NULL, 'a' },
355 { "password", 1, NULL, 'p' },
356 { "host", 1, NULL, 'H' },
357 { "proxy", 1, NULL, 'P' },
358 { "help", 0, NULL, 'h' },
359 { "bash", 0, NULL, 'b' },
360 { "version", 0, NULL, 'v' },
361 { }
362 };
363 struct session *session;
364 pid_t child;
365 char *tweet;
366 int retval;
367 int option;
368 char *http_proxy;
369 #if 0
370 char *home = getenv("HOME");
371 char *pwd = getenv("PWD");
372 char *dir;
373 #endif
374 session = session_alloc();
375 if (!session) {
376 fprintf(stderr, "no more memory...\n");
377 return -1;
378 }
379
380 curl_global_init(CURL_GLOBAL_ALL);
381
382 /* Set environment variables first, before reading command line options
383 * or config file values. */
384 http_proxy = getenv("http_proxy");
385 if (http_proxy) {
386 if (session->proxy)
387 free(session->proxy);
388 session->proxy = strdup(http_proxy);
389 dbg("http_proxy = %s\n", session->proxy);
390 }
391
392 parse_configfile(session);
393
394 while (1) {
395 option = getopt_long_only(argc, argv, "dqe:p:P:H:a:h",
396 options, NULL);
397 if (option == -1)
398 break;
399 switch (option) {
400 case 'd':
401 debug = 1;
402 break;
403 case 'a':
404 if (session->account)
405 free(session->account);
406 session->account = strdup(optarg);
407 dbg("account = %s\n", session->account);
408 break;
409 case 'p':
410 if (session->password)
411 free(session->password);
412 session->password = strdup(optarg);
413 dbg("password = %s\n", session->password);
414 break;
415 case 'P':
416 if (session->proxy)
417 free(session->proxy);
418 session->proxy = strdup(optarg);
419 dbg("proxy = %s\n", session->proxy);
420 break;
421 case 'H':
422 if (strcasecmp(optarg, "twitter") == 0)
423 session->host = HOST_TWITTER;
424 if (strcasecmp(optarg, "identica") == 0)
425 session->host = HOST_IDENTICA;
426 dbg("host = %d\n", session->host);
427 break;
428 case 'b':
429 session->bash= 1;
430 break;
431 case 'h':
432 display_help();
433 goto exit;
434 case 'v':
435 display_version();
436 goto exit;
437 default:
438 display_help();
439 goto exit;
440 }
441 }
442
443 if (!session->account) {
444 fprintf(stdout, "Enter twitter account: ");
445 session->account = get_string_from_stdin();
446 }
447
448 if (!session->password) {
449 fprintf(stdout, "Enter twitter password: ");
450 session->password = get_string_from_stdin();
451 }
452 #if 0
453 /* get the current working directory basename */
454 if (strcmp(pwd, home) == 0)
455 dir = "~";
456 else {
457 dir = strrchr(pwd, '/');
458 if (dir)
459 dir++;
460 else
461 dir = "?";
462 }
463 #endif
464 tweet = get_string_from_stdin();
465 if (!tweet || strlen(tweet) == 0) {
466 dbg("no tweet?\n");
467 return -1;
468 }
469
470 // session->tweet = zalloc(strlen(tweet) + strlen(dir) + 10);
471 session->tweet = zalloc(strlen(tweet) + 10);
472
473 /* if --bash is specified, add the "PWD $ " to
474 * the start of the tweet. */
475 if (session->bash)
476 // sprintf(session->tweet, "%s $ %s", dir, tweet);
477 sprintf(session->tweet, "$ %s", tweet);
478 else
479 sprintf(session->tweet, "%s", tweet);
480 free(tweet);
481
482 dbg("account = %s\n", session->account);
483 dbg("password = %s\n", session->password);
484 dbg("tweet = %s\n", session->tweet);
485 dbg("host = %d\n", session->host);
486
487 /* fork ourself so that the main shell can get on
488 * with it's life as we try to connect and handle everything
489 */
490 if (session->bash) {
491 child = fork();
492 if (child) {
493 dbg("child is %d\n", child);
494 exit(0);
495 }
496 }
497
498 retval = send_tweet(session);
499 if (retval && !session->bash) {
500 fprintf(stderr, "tweet failed\n");
501 return -1;
502 }
503
504 session_free(session);
505 exit:
506 return 0;
507 }

  ViewVC Help
Powered by ViewVC 1.1.26