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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1511 - (hide annotations)
Mon Jan 12 21:38:36 2009 UTC (12 years, 3 months ago) by gregoa
File MIME type: text/plain
File size: 11387 byte(s)
[svn-upgrade] Integrating new upstream version, bti (010)
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     #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 gregoa 1507 char *proxy;
53 gregoa 1473 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 gregoa 1507 fprintf(stdout, " --proxy PROXY:PORT\n");
72 gregoa 1473 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 gregoa 1507 free(session->proxy);
118 gregoa 1473 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 gregoa 1501 struct curl_slist *slist = NULL;
201 gregoa 1473
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 gregoa 1501 /*
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 gregoa 1473 break;
234     case HOST_IDENTICA:
235     curl_easy_setopt(curl, CURLOPT_URL, identica_url);
236     break;
237     }
238    
239 gregoa 1507 if (session->proxy)
240     curl_easy_setopt(curl, CURLOPT_PROXY, session->proxy);
241    
242 gregoa 1473 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 gregoa 1507 dbg("proxy = %s\n", session->proxy);
249 gregoa 1473
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 gregoa 1507 char *proxy = NULL;
273 gregoa 1473 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 gregoa 1507 } else if (!strncasecmp(c, "proxy", 5) &&
321     (c[5] == '=')) {
322     c += 6;
323     if (c[0] != '\0')
324     proxy = strdup(c);
325 gregoa 1473 }
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 gregoa 1511 if (proxy) {
340     if (session->proxy)
341     free(session->proxy);
342 gregoa 1507 session->proxy = proxy;
343 gregoa 1511 }
344 gregoa 1473
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 gregoa 1511 { "proxy", 1, NULL, 'P' },
358 gregoa 1473 { "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 gregoa 1511 char *http_proxy;
369 gregoa 1473 #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 gregoa 1511
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 gregoa 1473 parse_configfile(session);
393    
394     while (1) {
395 gregoa 1511 option = getopt_long_only(argc, argv, "dqe:p:P:H:a:h",
396 gregoa 1473 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 gregoa 1511 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 gregoa 1473 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 gregoa 1482 if (!tweet || strlen(tweet) == 0) {
466 gregoa 1473 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