/[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 1501 - (hide annotations)
Sat Jan 10 04:52:54 2009 UTC (12 years, 3 months ago) by gregoa
File MIME type: text/plain
File size: 10466 byte(s)
[svn-upgrade] Integrating new upstream version, bti (008)
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     int bash;
53     enum host host;
54     };
55    
56     struct bti_curl_buffer {
57     char *data;
58     int length;
59     };
60    
61     static void display_help(void)
62     {
63     fprintf(stdout, "bti - send tweet to twitter\n");
64     fprintf(stdout, "Version: " BTI_VERSION "\n");
65     fprintf(stdout, "Usage:\n");
66     fprintf(stdout, " bti [options]\n");
67     fprintf(stdout, "options are:\n");
68     fprintf(stdout, " --account accountname\n");
69     fprintf(stdout, " --password password\n");
70     fprintf(stdout, " --host HOST\n");
71     fprintf(stdout, " --bash\n");
72     fprintf(stdout, " --debug\n");
73     fprintf(stdout, " --version\n");
74     fprintf(stdout, " --help\n");
75     }
76    
77     static void display_version(void)
78     {
79     fprintf(stdout, "bti - version %s\n", BTI_VERSION);
80     }
81    
82     static char *get_string_from_stdin(void)
83     {
84     char *temp;
85     char *string;
86    
87     string = zalloc(1000);
88     if (!string)
89     return NULL;
90    
91     if (!fgets(string, 999, stdin))
92     return NULL;
93     temp = strchr(string, '\n');
94     *temp = '\0';
95     return string;
96     }
97    
98     static struct session *session_alloc(void)
99     {
100     struct session *session;
101    
102     session = zalloc(sizeof(*session));
103     if (!session)
104     return NULL;
105     return session;
106     }
107    
108     static void session_free(struct session *session)
109     {
110     if (!session)
111     return;
112     free(session->password);
113     free(session->account);
114     free(session->tweet);
115     free(session);
116     }
117    
118     static struct bti_curl_buffer *bti_curl_buffer_alloc(void)
119     {
120     struct bti_curl_buffer *buffer;
121    
122     buffer = zalloc(sizeof(*buffer));
123     if (!buffer)
124     return NULL;
125    
126     /* start out with a data buffer of 1 byte to
127     * make the buffer fill logic simpler */
128     buffer->data = zalloc(1);
129     if (!buffer->data) {
130     free(buffer);
131     return NULL;
132     }
133     buffer->length = 0;
134     return buffer;
135     }
136    
137     static void bti_curl_buffer_free(struct bti_curl_buffer *buffer)
138     {
139     if (!buffer)
140     return;
141     free(buffer->data);
142     free(buffer);
143     }
144    
145     static const char *twitter_url = "https://twitter.com/statuses/update.xml";
146     static const char *identica_url = "http://identi.ca/api/statuses/update.xml";
147    
148     static CURL *curl_init(void)
149     {
150     CURL *curl;
151    
152     curl = curl_easy_init();
153     if (!curl) {
154     fprintf(stderr, "Can not init CURL!\n");
155     return NULL;
156     }
157     /* some ssl sanity checks on the connection we are making */
158     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
159     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
160     return curl;
161     }
162    
163     size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *userp)
164     {
165     struct bti_curl_buffer *curl_buf = userp;
166     size_t buffer_size = size * nmemb;
167     char *temp;
168    
169     if ((!buffer) || (!buffer_size) || (!curl_buf))
170     return -EINVAL;
171    
172     /* add to the data we already have */
173     temp = zalloc(curl_buf->length + buffer_size + 1);
174     if (!temp)
175     return -ENOMEM;
176    
177     memcpy(temp, curl_buf->data, curl_buf->length);
178     free(curl_buf->data);
179     curl_buf->data = temp;
180     memcpy(&curl_buf->data[curl_buf->length], (char *)buffer, buffer_size);
181     curl_buf->length += buffer_size;
182    
183     dbg("%s\n", curl_buf->data);
184    
185     return buffer_size;
186     }
187    
188     static int send_tweet(struct session *session)
189     {
190     char user_password[500];
191     char data[500];
192     struct bti_curl_buffer *curl_buf;
193     CURL *curl = NULL;
194     CURLcode res;
195     struct curl_httppost *formpost = NULL;
196     struct curl_httppost *lastptr = NULL;
197 gregoa 1501 struct curl_slist *slist = NULL;
198 gregoa 1473
199     if (!session)
200     return -EINVAL;
201    
202     curl_buf = bti_curl_buffer_alloc();
203     if (!curl_buf)
204     return -ENOMEM;
205    
206     snprintf(user_password, sizeof(user_password), "%s:%s",
207     session->account, session->password);
208     snprintf(data, sizeof(data), "status=\"%s\"", session->tweet);
209    
210     curl = curl_init();
211     if (!curl)
212     return -EINVAL;
213    
214     curl_formadd(&formpost, &lastptr,
215     CURLFORM_COPYNAME, "status",
216     CURLFORM_COPYCONTENTS, session->tweet,
217     CURLFORM_END);
218    
219     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 1501 /*
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     if (debug)
237     curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
238     curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
239    
240     dbg("user_password = %s\n", user_password);
241     dbg("data = %s\n", data);
242    
243     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
244     curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_buf);
245     res = curl_easy_perform(curl);
246     if (res && !session->bash) {
247     fprintf(stderr, "error(%d) trying to send tweet\n", res);
248     return -EINVAL;
249     }
250    
251     curl_easy_cleanup(curl);
252     curl_formfree(formpost);
253     bti_curl_buffer_free(curl_buf);
254     return 0;
255     }
256    
257     static void parse_configfile(struct session *session)
258     {
259     FILE *config_file;
260     char *line = NULL;
261     size_t len = 0;
262     char *account = NULL;
263     char *password = NULL;
264     char *host = NULL;
265     char *file;
266     char *home = getenv("HOME");
267    
268     /* config file is ~/.bti */
269     file = alloca(strlen(home) + 7);
270    
271     sprintf(file, "%s/.bti", home);
272    
273     config_file = fopen(file, "r");
274    
275     /* No error if file does not exist or is unreadable. */
276     if (config_file == NULL)
277     return;
278    
279     do {
280     ssize_t n = getline(&line, &len, config_file);
281     if (n < 0)
282     break;
283     if (line[n - 1] == '\n')
284     line[n - 1] = '\0';
285     /* Parse file. Format is the usual value pairs:
286     account=name
287     passwort=value
288     # is a comment character
289     */
290     *strchrnul(line, '#') = '\0';
291     char *c = line;
292     while (isspace(*c))
293     c++;
294     /* Ignore blank lines. */
295     if (c[0] == '\0')
296     continue;
297    
298     if (!strncasecmp(c, "account", 7) && (c[7] == '=')) {
299     c += 8;
300     if (c[0] != '\0')
301     account = strdup(c);
302     } else if (!strncasecmp(c, "password", 8) &&
303     (c[8] == '=')) {
304     c += 9;
305     if (c[0] != '\0')
306     password = strdup(c);
307     } else if (!strncasecmp(c, "host", 4) &&
308     (c[4] == '=')) {
309     c += 5;
310     if (c[0] != '\0')
311     host = strdup(c);
312     }
313     } while (!feof(config_file));
314    
315     if (password)
316     session->password = password;
317     if (account)
318     session->account = account;
319     if (host) {
320     if (strcasecmp(host, "twitter") == 0)
321     session->host = HOST_TWITTER;
322     if (strcasecmp(host, "identica") == 0)
323     session->host = HOST_IDENTICA;
324     free(host);
325     }
326    
327     /* Free buffer and close file. */
328     free(line);
329     fclose(config_file);
330     }
331    
332     int main(int argc, char *argv[], char *envp[])
333     {
334     static const struct option options[] = {
335     { "debug", 0, NULL, 'd' },
336     { "account", 1, NULL, 'a' },
337     { "password", 1, NULL, 'p' },
338     { "host", 1, NULL, 'H' },
339     { "help", 0, NULL, 'h' },
340     { "bash", 0, NULL, 'b' },
341     { "version", 0, NULL, 'v' },
342     { }
343     };
344     struct session *session;
345     pid_t child;
346     char *tweet;
347     int retval;
348     int option;
349     #if 0
350     char *home = getenv("HOME");
351     char *pwd = getenv("PWD");
352     char *dir;
353     #endif
354     session = session_alloc();
355     if (!session) {
356     fprintf(stderr, "no more memory...\n");
357     return -1;
358     }
359    
360     curl_global_init(CURL_GLOBAL_ALL);
361     parse_configfile(session);
362    
363     while (1) {
364     option = getopt_long_only(argc, argv, "dqe:p:H:a:h",
365     options, NULL);
366     if (option == -1)
367     break;
368     switch (option) {
369     case 'd':
370     debug = 1;
371     break;
372     case 'a':
373     if (session->account)
374     free(session->account);
375     session->account = strdup(optarg);
376     dbg("account = %s\n", session->account);
377     break;
378     case 'p':
379     if (session->password)
380     free(session->password);
381     session->password = strdup(optarg);
382     dbg("password = %s\n", session->password);
383     break;
384     case 'H':
385     if (strcasecmp(optarg, "twitter") == 0)
386     session->host = HOST_TWITTER;
387     if (strcasecmp(optarg, "identica") == 0)
388     session->host = HOST_IDENTICA;
389     dbg("host = %d\n", session->host);
390     break;
391     case 'b':
392     session->bash= 1;
393     break;
394     case 'h':
395     display_help();
396     goto exit;
397     case 'v':
398     display_version();
399     goto exit;
400     default:
401     display_help();
402     goto exit;
403     }
404     }
405    
406     if (!session->account) {
407     fprintf(stdout, "Enter twitter account: ");
408     session->account = get_string_from_stdin();
409     }
410    
411     if (!session->password) {
412     fprintf(stdout, "Enter twitter password: ");
413     session->password = get_string_from_stdin();
414     }
415     #if 0
416     /* get the current working directory basename */
417     if (strcmp(pwd, home) == 0)
418     dir = "~";
419     else {
420     dir = strrchr(pwd, '/');
421     if (dir)
422     dir++;
423     else
424     dir = "?";
425     }
426     #endif
427     tweet = get_string_from_stdin();
428 gregoa 1482 if (!tweet || strlen(tweet) == 0) {
429 gregoa 1473 dbg("no tweet?\n");
430     return -1;
431     }
432    
433     // session->tweet = zalloc(strlen(tweet) + strlen(dir) + 10);
434     session->tweet = zalloc(strlen(tweet) + 10);
435    
436     /* if --bash is specified, add the "PWD $ " to
437     * the start of the tweet. */
438     if (session->bash)
439     // sprintf(session->tweet, "%s $ %s", dir, tweet);
440     sprintf(session->tweet, "$ %s", tweet);
441     else
442     sprintf(session->tweet, "%s", tweet);
443     free(tweet);
444    
445     dbg("account = %s\n", session->account);
446     dbg("password = %s\n", session->password);
447     dbg("tweet = %s\n", session->tweet);
448     dbg("host = %d\n", session->host);
449    
450     /* fork ourself so that the main shell can get on
451     * with it's life as we try to connect and handle everything
452     */
453     if (session->bash) {
454     child = fork();
455     if (child) {
456     dbg("child is %d\n", child);
457     exit(0);
458     }
459     }
460    
461     retval = send_tweet(session);
462     if (retval && !session->bash) {
463     fprintf(stderr, "tweet failed\n");
464     return -1;
465     }
466    
467     session_free(session);
468     exit:
469     return 0;
470     }

  ViewVC Help
Powered by ViewVC 1.1.26