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

Annotation of /bti/trunk/bti.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26