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

Annotation of /bti/trunk/bti.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26