/[debian]/bti/trunk/bti.c
ViewVC logotype

Contents of /bti/trunk/bti.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26