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

Annotation of /bti/trunk/bti.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1526 - (hide annotations)
Tue Jan 20 15:49:55 2009 UTC (12 years, 6 months ago) by gregoa
File MIME type: text/plain
File size: 12377 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 1473 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 gregoa 1509 fprintf(stdout, " --proxy PROXY:PORT\n");
76 gregoa 1473 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 gregoa 1526 static char *string = (char *)NULL;
91     if (string) {
92     free(string);
93     string = (char *)NULL;
94     }
95 gregoa 1473
96 gregoa 1526 string = readline("tweet: ");
97 gregoa 1473
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 gregoa 1509 free(session->proxy);
119 gregoa 1526 free(session->time);
120     free(session->homedir);
121 gregoa 1473 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 gregoa 1503 struct curl_slist *slist = NULL;
204 gregoa 1473
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 gregoa 1503 /*
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 gregoa 1473 break;
237     case HOST_IDENTICA:
238     curl_easy_setopt(curl, CURLOPT_URL, identica_url);
239     break;
240     }
241    
242 gregoa 1509 if (session->proxy)
243     curl_easy_setopt(curl, CURLOPT_PROXY, session->proxy);
244    
245 gregoa 1473 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 gregoa 1509 dbg("proxy = %s\n", session->proxy);
252 gregoa 1473
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 gregoa 1509 char *proxy = NULL;
276 gregoa 1473 char *file;
277    
278     /* config file is ~/.bti */
279 gregoa 1526 file = alloca(strlen(session->homedir) + 7);
280 gregoa 1473
281 gregoa 1526 sprintf(file, "%s/.bti", session->homedir);
282 gregoa 1473
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 gregoa 1509 } else if (!strncasecmp(c, "proxy", 5) &&
323     (c[5] == '=')) {
324     c += 6;
325     if (c[0] != '\0')
326     proxy = 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 1473
347     /* Free buffer and close file. */
348     free(line);
349     fclose(config_file);
350     }
351    
352 gregoa 1526 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 gregoa 1473 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 gregoa 1513 { "proxy", 1, NULL, 'P' },
394 gregoa 1473 { "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 gregoa 1526 int retval = 0;
403 gregoa 1473 int option;
404 gregoa 1513 char *http_proxy;
405 gregoa 1526 time_t t;
406 gregoa 1473 #if 0
407     char *pwd = getenv("PWD");
408     char *dir;
409     #endif
410 gregoa 1526
411     rl_bind_key('\t', rl_insert);
412 gregoa 1473 session = session_alloc();
413     if (!session) {
414     fprintf(stderr, "no more memory...\n");
415     return -1;
416     }
417    
418 gregoa 1526 /* 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 gregoa 1473 curl_global_init(CURL_GLOBAL_ALL);
426 gregoa 1513
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 gregoa 1473 parse_configfile(session);
438    
439     while (1) {
440 gregoa 1513 option = getopt_long_only(argc, argv, "dqe:p:P:H:a:h",
441 gregoa 1473 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 gregoa 1513 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 gregoa 1473 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 gregoa 1484 if (!tweet || strlen(tweet) == 0) {
511 gregoa 1473 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 gregoa 1526 if (retval && !session->bash)
545 gregoa 1473 fprintf(stderr, "tweet failed\n");
546    
547 gregoa 1526 // log_session(session, retval);
548     exit:
549 gregoa 1473 session_free(session);
550 gregoa 1526 return retval;;
551 gregoa 1473 }

  ViewVC Help
Powered by ViewVC 1.1.26