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

Annotation of /bti/trunk/bti.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2709 - (hide annotations)
Sat Dec 3 00:00:28 2011 UTC (9 years, 7 months ago) by gregoa
File MIME type: text/plain
File size: 38299 byte(s)
New upstream release
1 gregoa 1473 /*
2 gregoa 2338 * Copyright (C) 2008-2011 Greg Kroah-Hartman <greg@kroah.com>
3 gregoa 1653 * Copyright (C) 2009 Bart Trojanowski <bart@jukie.net>
4 gregoa 2338 * Copyright (C) 2009-2010 Amir Mohammad Saied <amirsaied@gmail.com>
5 gregoa 1473 *
6     * This program is free software; you can redistribute it and/or modify it
7     * under the terms of the GNU General Public License as published by the
8     * Free Software Foundation version 2 of the License.
9     *
10     * This program is distributed in the hope that it will be useful, but
11     * WITHOUT ANY WARRANTY; without even the implied warranty of
12     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13     * General Public License for more details.
14     *
15     * You should have received a copy of the GNU General Public License along
16     * with this program; if not, write to the Free Software Foundation, Inc.,
17     * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18     */
19    
20 gregoa 1682 #define _GNU_SOURCE
21    
22 gregoa 1473 #include <stdio.h>
23     #include <stdlib.h>
24     #include <stddef.h>
25     #include <string.h>
26     #include <getopt.h>
27     #include <errno.h>
28     #include <ctype.h>
29     #include <fcntl.h>
30     #include <unistd.h>
31 gregoa 1526 #include <time.h>
32 gregoa 1473 #include <sys/stat.h>
33 gregoa 1653 #include <sys/types.h>
34     #include <sys/wait.h>
35 gregoa 1473 #include <curl/curl.h>
36 gregoa 1623 #include <libxml/xmlmemory.h>
37     #include <libxml/parser.h>
38     #include <libxml/tree.h>
39 gregoa 1653 #include <pcre.h>
40 gregoa 1952 #include <termios.h>
41 gregoa 1988 #include <dlfcn.h>
42 gregoa 2235 #include <oauth.h>
43 gregoa 2338 #include "bti.h"
44 gregoa 1473
45     #define zalloc(size) calloc(size, 1)
46    
47     #define dbg(format, arg...) \
48     do { \
49     if (debug) \
50 gregoa 1682 fprintf(stdout, "bti: %s: " format , __func__ , \
51     ## arg); \
52 gregoa 1473 } while (0)
53    
54    
55 gregoa 2451 int debug;
56 gregoa 1473
57     static void display_help(void)
58     {
59 gregoa 2248 fprintf(stdout, "bti - send tweet to twitter or identi.ca\n"
60     "Version: %s\n"
61     "Usage:\n"
62     " bti [options]\n"
63     "options are:\n"
64     " --account accountname\n"
65     " --password password\n"
66     " --action action\n"
67     " ('update', 'friends', 'public', 'replies', or 'user')\n"
68     " --user screenname\n"
69     " --group groupname\n"
70     " --proxy PROXY:PORT\n"
71     " --host HOST\n"
72     " --logfile logfile\n"
73     " --config configfile\n"
74     " --replyto ID\n"
75 gregoa 2338 " --retweet ID\n"
76 gregoa 2248 " --shrink-urls\n"
77     " --page PAGENUMBER\n"
78 gregoa 2709 " --column COLUMNWIDTH\n"
79 gregoa 2248 " --bash\n"
80     " --background\n"
81     " --debug\n"
82     " --verbose\n"
83     " --dry-run\n"
84     " --version\n"
85     " --help\n", VERSION);
86 gregoa 1473 }
87    
88     static void display_version(void)
89     {
90 gregoa 1682 fprintf(stdout, "bti - version %s\n", VERSION);
91 gregoa 1473 }
92    
93 gregoa 1988 static char *get_string(const char *name)
94     {
95     char *temp;
96     char *string;
97    
98     string = zalloc(1000);
99     if (!string)
100     exit(1);
101     if (name != NULL)
102     fprintf(stdout, "%s", name);
103 gregoa 2709 if (!fgets(string, 999, stdin)) {
104     free(string);
105 gregoa 1988 return NULL;
106 gregoa 2709 }
107 gregoa 1988 temp = strchr(string, '\n');
108 gregoa 2159 if (temp)
109     *temp = '\0';
110 gregoa 1988 return string;
111     }
112    
113     /*
114     * Try to get a handle to a readline function from a variety of different
115     * libraries. If nothing is present on the system, then fall back to an
116     * internal one.
117     *
118     * Logic originally based off of code in the e2fsutils package in the
119     * lib/ss/get_readline.c file, which is licensed under the MIT license.
120     *
121     * This keeps us from having to relicense the bti codebase if readline
122 gregoa 2709 * ever changes its license, as there is no link-time dependency.
123 gregoa 1988 * It is a run-time thing only, and we handle any readline-like library
124     * in the same manner, making bti not be a derivative work of any
125     * other program.
126     */
127     static void session_readline_init(struct session *session)
128     {
129     /* Libraries we will try to use for readline/editline functionality */
130     const char *libpath = "libreadline.so.6:libreadline.so.5:"
131     "libreadline.so.4:libreadline.so:libedit.so.2:"
132     "libedit.so:libeditline.so.0:libeditline.so";
133     void *handle = NULL;
134     char *tmp, *cp, *next;
135     int (*bind_key)(int, void *);
136     void (*insert)(void);
137    
138 gregoa 2159 /* default to internal function if we can't or won't find anything */
139 gregoa 1988 session->readline = get_string;
140 gregoa 2159 if (!isatty(0))
141     return;
142     session->interactive = 1;
143 gregoa 1988
144     tmp = malloc(strlen(libpath)+1);
145     if (!tmp)
146     return;
147     strcpy(tmp, libpath);
148     for (cp = tmp; cp; cp = next) {
149     next = strchr(cp, ':');
150     if (next)
151     *next++ = 0;
152     if (*cp == 0)
153     continue;
154 gregoa 2159 handle = dlopen(cp, RTLD_NOW);
155     if (handle) {
156 gregoa 1988 dbg("Using %s for readline library\n", cp);
157     break;
158     }
159     }
160     free(tmp);
161     if (!handle) {
162     dbg("No readline library found.\n");
163     return;
164     }
165    
166     session->readline_handle = handle;
167     session->readline = (char *(*)(const char *))dlsym(handle, "readline");
168     if (session->readline == NULL) {
169     /* something odd happened, default back to internal stuff */
170     session->readline_handle = NULL;
171     session->readline = get_string;
172     return;
173     }
174    
175     /*
176     * If we found a library, turn off filename expansion
177     * as that makes no sense from within bti.
178     */
179     bind_key = (int (*)(int, void *))dlsym(handle, "rl_bind_key");
180     insert = (void (*)(void))dlsym(handle, "rl_insert");
181     if (bind_key && insert)
182     bind_key('\t', insert);
183     }
184    
185     static void session_readline_cleanup(struct session *session)
186     {
187     if (session->readline_handle)
188     dlclose(session->readline_handle);
189     }
190    
191 gregoa 1473 static struct session *session_alloc(void)
192     {
193     struct session *session;
194    
195     session = zalloc(sizeof(*session));
196     if (!session)
197     return NULL;
198     return session;
199     }
200    
201     static void session_free(struct session *session)
202     {
203     if (!session)
204     return;
205 gregoa 2338 free(session->retweet);
206 gregoa 2235 free(session->replyto);
207 gregoa 1473 free(session->password);
208     free(session->account);
209 gregoa 2235 free(session->consumer_key);
210     free(session->consumer_secret);
211     free(session->access_token_key);
212     free(session->access_token_secret);
213 gregoa 1473 free(session->tweet);
214 gregoa 1509 free(session->proxy);
215 gregoa 1526 free(session->time);
216     free(session->homedir);
217 gregoa 1623 free(session->user);
218 gregoa 1952 free(session->group);
219 gregoa 1714 free(session->hosturl);
220 gregoa 1988 free(session->hostname);
221 gregoa 2159 free(session->configfile);
222 gregoa 1473 free(session);
223     }
224    
225 gregoa 1623 static struct bti_curl_buffer *bti_curl_buffer_alloc(enum action action)
226 gregoa 1473 {
227     struct bti_curl_buffer *buffer;
228    
229     buffer = zalloc(sizeof(*buffer));
230     if (!buffer)
231     return NULL;
232    
233     /* start out with a data buffer of 1 byte to
234     * make the buffer fill logic simpler */
235     buffer->data = zalloc(1);
236     if (!buffer->data) {
237     free(buffer);
238     return NULL;
239     }
240     buffer->length = 0;
241 gregoa 1623 buffer->action = action;
242 gregoa 1473 return buffer;
243     }
244    
245     static void bti_curl_buffer_free(struct bti_curl_buffer *buffer)
246     {
247     if (!buffer)
248     return;
249     free(buffer->data);
250     free(buffer);
251     }
252    
253 gregoa 2338 const char twitter_host[] = "http://api.twitter.com/1/statuses";
254     const char identica_host[] = "https://identi.ca/api/statuses";
255     const char twitter_name[] = "twitter";
256     const char identica_name[] = "identi.ca";
257 gregoa 1473
258 gregoa 2248 static const char twitter_request_token_uri[] = "http://twitter.com/oauth/request_token";
259     static const char twitter_access_token_uri[] = "http://twitter.com/oauth/access_token";
260     static const char twitter_authorize_uri[] = "http://twitter.com/oauth/authorize?oauth_token=";
261 gregoa 2709 static const char identica_request_token_uri[] = "https://identi.ca/api/oauth/request_token?oauth_callback=oob";
262     static const char identica_access_token_uri[] = "https://identi.ca/api/oauth/access_token";
263     static const char identica_authorize_uri[] = "https://identi.ca/api/oauth/authorize?oauth_token=";
264     static const char custom_request_token_uri[] = "/../oauth/request_token?oauth_callback=oob";
265     static const char custom_access_token_uri[] = "/../oauth/access_token";
266     static const char custom_authorize_uri[] = "/../oauth/authorize?oauth_token=";
267 gregoa 1623
268 gregoa 2248 static const char user_uri[] = "/user_timeline/";
269     static const char update_uri[] = "/update.xml";
270     static const char public_uri[] = "/public_timeline.xml";
271     static const char friends_uri[] = "/friends_timeline.xml";
272     static const char mentions_uri[] = "/mentions.xml";
273     static const char replies_uri[] = "/replies.xml";
274 gregoa 2338 static const char retweet_uri[] = "/retweet/";
275 gregoa 2248 static const char group_uri[] = "/../statusnet/groups/timeline/";
276 gregoa 2235
277 gregoa 2709 static const char config_default[] = "/etc/bti";
278     static const char config_user_default[] = ".bti";
279    
280 gregoa 1473 static CURL *curl_init(void)
281     {
282     CURL *curl;
283    
284     curl = curl_easy_init();
285     if (!curl) {
286     fprintf(stderr, "Can not init CURL!\n");
287     return NULL;
288     }
289     /* some ssl sanity checks on the connection we are making */
290     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
291     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
292     return curl;
293     }
294    
295 gregoa 2338 /* The final place data is sent to the screen/pty/tty */
296 gregoa 2451 static void bti_output_line(struct session *session, xmlChar *user,
297     xmlChar *id, xmlChar *created, xmlChar *text)
298 gregoa 1623 {
299 gregoa 2338 if (session->verbose)
300 gregoa 2709 printf("[%*s] {%s} (%.16s) %s\n", -session->column_output, user,
301     id, created, text);
302 gregoa 2338 else
303 gregoa 2709 printf("[%*s] %s\n", -session->column_output, user, text);
304 gregoa 2338 }
305    
306     static void parse_statuses(struct session *session,
307     xmlDocPtr doc, xmlNodePtr current)
308     {
309 gregoa 1623 xmlChar *text = NULL;
310     xmlChar *user = NULL;
311 gregoa 1714 xmlChar *created = NULL;
312 gregoa 2235 xmlChar *id = NULL;
313 gregoa 1623 xmlNodePtr userinfo;
314    
315     current = current->xmlChildrenNode;
316     while (current != NULL) {
317     if (current->type == XML_ELEMENT_NODE) {
318 gregoa 1714 if (!xmlStrcmp(current->name, (const xmlChar *)"created_at"))
319     created = xmlNodeListGetString(doc, current->xmlChildrenNode, 1);
320 gregoa 1623 if (!xmlStrcmp(current->name, (const xmlChar *)"text"))
321     text = xmlNodeListGetString(doc, current->xmlChildrenNode, 1);
322 gregoa 2235 if (!xmlStrcmp(current->name, (const xmlChar *)"id"))
323     id = xmlNodeListGetString(doc, current->xmlChildrenNode, 1);
324 gregoa 1623 if (!xmlStrcmp(current->name, (const xmlChar *)"user")) {
325     userinfo = current->xmlChildrenNode;
326     while (userinfo != NULL) {
327     if ((!xmlStrcmp(userinfo->name, (const xmlChar *)"screen_name"))) {
328     if (user)
329     xmlFree(user);
330     user = xmlNodeListGetString(doc, userinfo->xmlChildrenNode, 1);
331     }
332     userinfo = userinfo->next;
333     }
334     }
335 gregoa 1714
336 gregoa 2235 if (user && text && created && id) {
337 gregoa 2457 bti_output_line(session, user, id,
338     created, text);
339 gregoa 1623 xmlFree(user);
340     xmlFree(text);
341 gregoa 1714 xmlFree(created);
342 gregoa 2235 xmlFree(id);
343 gregoa 1623 user = NULL;
344     text = NULL;
345 gregoa 1714 created = NULL;
346 gregoa 2235 id = NULL;
347 gregoa 1623 }
348     }
349     current = current->next;
350     }
351    
352     return;
353     }
354    
355 gregoa 2338 static void parse_timeline(char *document, struct session *session)
356 gregoa 1623 {
357     xmlDocPtr doc;
358     xmlNodePtr current;
359    
360 gregoa 1653 doc = xmlReadMemory(document, strlen(document), "timeline.xml",
361     NULL, XML_PARSE_NOERROR);
362 gregoa 1623 if (doc == NULL)
363     return;
364    
365     current = xmlDocGetRootElement(doc);
366     if (current == NULL) {
367     fprintf(stderr, "empty document\n");
368     xmlFreeDoc(doc);
369     return;
370     }
371    
372     if (xmlStrcmp(current->name, (const xmlChar *) "statuses")) {
373     fprintf(stderr, "unexpected document type\n");
374     xmlFreeDoc(doc);
375     return;
376     }
377    
378     current = current->xmlChildrenNode;
379     while (current != NULL) {
380     if ((!xmlStrcmp(current->name, (const xmlChar *)"status")))
381 gregoa 2338 parse_statuses(session, doc, current);
382 gregoa 1623 current = current->next;
383     }
384     xmlFreeDoc(doc);
385    
386     return;
387     }
388    
389 gregoa 1682 static size_t curl_callback(void *buffer, size_t size, size_t nmemb,
390     void *userp)
391 gregoa 1473 {
392     struct bti_curl_buffer *curl_buf = userp;
393     size_t buffer_size = size * nmemb;
394     char *temp;
395    
396     if ((!buffer) || (!buffer_size) || (!curl_buf))
397     return -EINVAL;
398    
399     /* add to the data we already have */
400     temp = zalloc(curl_buf->length + buffer_size + 1);
401     if (!temp)
402     return -ENOMEM;
403    
404     memcpy(temp, curl_buf->data, curl_buf->length);
405     free(curl_buf->data);
406     curl_buf->data = temp;
407     memcpy(&curl_buf->data[curl_buf->length], (char *)buffer, buffer_size);
408     curl_buf->length += buffer_size;
409 gregoa 1623 if (curl_buf->action)
410 gregoa 2338 parse_timeline(curl_buf->data, curl_buf->session);
411 gregoa 1473
412     dbg("%s\n", curl_buf->data);
413    
414     return buffer_size;
415     }
416    
417 gregoa 2235 static int parse_osp_reply(const char *reply, char **token, char **secret)
418     {
419     int rc;
420     int retval = 1;
421     char **rv = NULL;
422     rc = oauth_split_url_parameters(reply, &rv);
423     qsort(rv, rc, sizeof(char *), oauth_cmpstringp);
424     if (rc == 2 || rc == 4) {
425 gregoa 2248 if (!strncmp(rv[0], "oauth_token=", 11) &&
426     !strncmp(rv[1], "oauth_token_secret=", 18)) {
427 gregoa 2235 if (token)
428 gregoa 2248 *token = strdup(&(rv[0][12]));
429 gregoa 2235 if (secret)
430 gregoa 2248 *secret = strdup(&(rv[1][19]));
431 gregoa 2235
432     retval = 0;
433     }
434     } else if (rc == 3) {
435 gregoa 2248 if (!strncmp(rv[1], "oauth_token=", 11) &&
436     !strncmp(rv[2], "oauth_token_secret=", 18)) {
437 gregoa 2235 if (token)
438 gregoa 2248 *token = strdup(&(rv[1][12]));
439 gregoa 2235 if (secret)
440 gregoa 2248 *secret = strdup(&(rv[2][19]));
441 gregoa 2235
442     retval = 0;
443     }
444     }
445    
446     dbg("token: %s\n", *token);
447     dbg("secret: %s\n", *secret);
448    
449     if (rv)
450     free(rv);
451    
452     return retval;
453     }
454    
455     static int request_access_token(struct session *session)
456     {
457     char *post_params = NULL;
458     char *request_url = NULL;
459 gregoa 2457 char *reply = NULL;
460 gregoa 2235 char *at_key = NULL;
461     char *at_secret = NULL;
462     char *verifier = NULL;
463     char at_uri[90];
464 gregoa 2709 char token_uri[90];
465 gregoa 2235
466     if (!session)
467     return -EINVAL;
468    
469     if (session->host == HOST_TWITTER)
470     request_url = oauth_sign_url2(
471     twitter_request_token_uri, NULL,
472     OA_HMAC, NULL, session->consumer_key,
473     session->consumer_secret, NULL, NULL);
474     else if (session->host == HOST_IDENTICA)
475     request_url = oauth_sign_url2(
476     identica_request_token_uri, NULL,
477     OA_HMAC, NULL, session->consumer_key,
478     session->consumer_secret, NULL, NULL);
479 gregoa 2709 else {
480     sprintf(token_uri, "%s%s",
481     session->hosturl, custom_request_token_uri);
482     request_url = oauth_sign_url2(
483     token_uri, NULL,
484     OA_HMAC, NULL, session->consumer_key,
485     session->consumer_secret, NULL, NULL);
486     }
487 gregoa 2235 reply = oauth_http_get(request_url, post_params);
488    
489     if (request_url)
490     free(request_url);
491    
492     if (post_params)
493     free(post_params);
494    
495     if (!reply)
496     return 1;
497    
498     if (parse_osp_reply(reply, &at_key, &at_secret))
499     return 1;
500    
501     free(reply);
502    
503 gregoa 2248 fprintf(stdout,
504     "Please open the following link in your browser, and "
505     "allow 'bti' to access your account. Then paste "
506     "back the provided PIN in here.\n");
507 gregoa 2235 if (session->host == HOST_TWITTER) {
508     fprintf(stdout, "%s%s\nPIN: ", twitter_authorize_uri, at_key);
509     verifier = session->readline(NULL);
510 gregoa 2248 sprintf(at_uri, "%s?oauth_verifier=%s",
511     twitter_access_token_uri, verifier);
512 gregoa 2235 } else if (session->host == HOST_IDENTICA) {
513     fprintf(stdout, "%s%s\nPIN: ", identica_authorize_uri, at_key);
514     verifier = session->readline(NULL);
515 gregoa 2248 sprintf(at_uri, "%s?oauth_verifier=%s",
516     identica_access_token_uri, verifier);
517 gregoa 2709 } else {
518     fprintf(stdout, "%s%s%s\nPIN: ",
519     session->hosturl, custom_authorize_uri, at_key);
520     verifier = session->readline(NULL);
521     sprintf(at_uri, "%s%s?oauth_verifier=%s",
522     session->hosturl, custom_access_token_uri, verifier);
523 gregoa 2235 }
524     request_url = oauth_sign_url2(at_uri, NULL, OA_HMAC, NULL,
525 gregoa 2248 session->consumer_key,
526     session->consumer_secret,
527     at_key, at_secret);
528 gregoa 2235 reply = oauth_http_get(request_url, post_params);
529    
530     if (!reply)
531     return 1;
532    
533     if (parse_osp_reply(reply, &at_key, &at_secret))
534     return 1;
535    
536     free(reply);
537    
538 gregoa 2248 fprintf(stdout,
539     "Please put these two lines in your bti "
540 gregoa 2709 "configuration file (%s):\n"
541 gregoa 2248 "access_token_key=%s\n"
542     "access_token_secret=%s\n",
543 gregoa 2709 session->configfile, at_key, at_secret);
544 gregoa 2235
545     return 0;
546     }
547    
548 gregoa 1623 static int send_request(struct session *session)
549 gregoa 1473 {
550 gregoa 2235 char endpoint[500];
551 gregoa 1473 char user_password[500];
552     char data[500];
553     struct bti_curl_buffer *curl_buf;
554     CURL *curl = NULL;
555     CURLcode res;
556     struct curl_httppost *formpost = NULL;
557     struct curl_httppost *lastptr = NULL;
558 gregoa 1503 struct curl_slist *slist = NULL;
559 gregoa 2235 char *req_url = NULL;
560     char *reply = NULL;
561     char *postarg = NULL;
562     char *escaped_tweet = NULL;
563     int is_post = 0;
564 gregoa 1473
565     if (!session)
566     return -EINVAL;
567    
568 gregoa 1988 if (!session->hosturl)
569     session->hosturl = strdup(twitter_host);
570    
571 gregoa 2338 if (session->no_oauth || session->guest) {
572 gregoa 2235 curl_buf = bti_curl_buffer_alloc(session->action);
573     if (!curl_buf)
574     return -ENOMEM;
575 gregoa 2338 curl_buf->session = session;
576 gregoa 1473
577 gregoa 2235 curl = curl_init();
578 gregoa 2709 if (!curl) {
579     bti_curl_buffer_free(curl_buf);
580 gregoa 2235 return -EINVAL;
581 gregoa 2709 }
582 gregoa 1532
583 gregoa 2235 if (!session->hosturl)
584     session->hosturl = strdup(twitter_host);
585 gregoa 1952
586 gregoa 2235 switch (session->action) {
587 gregoa 2248 case ACTION_UPDATE:
588     snprintf(user_password, sizeof(user_password), "%s:%s",
589     session->account, session->password);
590     snprintf(data, sizeof(data), "status=\"%s\"",
591     session->tweet);
592     curl_formadd(&formpost, &lastptr,
593     CURLFORM_COPYNAME, "status",
594     CURLFORM_COPYCONTENTS, session->tweet,
595     CURLFORM_END);
596 gregoa 1623
597 gregoa 2248 curl_formadd(&formpost, &lastptr,
598     CURLFORM_COPYNAME, "source",
599     CURLFORM_COPYCONTENTS, "bti",
600     CURLFORM_END);
601    
602     if (session->replyto)
603 gregoa 2235 curl_formadd(&formpost, &lastptr,
604 gregoa 2457 CURLFORM_COPYNAME,
605     "in_reply_to_status_id",
606     CURLFORM_COPYCONTENTS,
607     session->replyto,
608 gregoa 2248 CURLFORM_END);
609 gregoa 1623
610 gregoa 2248 curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
611     slist = curl_slist_append(slist, "Expect:");
612     curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
613 gregoa 1623
614 gregoa 2248 sprintf(endpoint, "%s%s", session->hosturl, update_uri);
615     curl_easy_setopt(curl, CURLOPT_URL, endpoint);
616     curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
617     break;
618 gregoa 1623
619 gregoa 2248 case ACTION_FRIENDS:
620     snprintf(user_password, sizeof(user_password), "%s:%s",
621     session->account, session->password);
622     sprintf(endpoint, "%s%s?page=%d", session->hosturl,
623     friends_uri, session->page);
624     curl_easy_setopt(curl, CURLOPT_URL, endpoint);
625     curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
626     break;
627 gregoa 1623
628 gregoa 2248 case ACTION_USER:
629     sprintf(endpoint, "%s%s%s.xml?page=%d", session->hosturl,
630     user_uri, session->user, session->page);
631     curl_easy_setopt(curl, CURLOPT_URL, endpoint);
632     break;
633 gregoa 1952
634 gregoa 2248 case ACTION_REPLIES:
635     snprintf(user_password, sizeof(user_password), "%s:%s",
636     session->account, session->password);
637     sprintf(endpoint, "%s%s?page=%d", session->hosturl,
638     replies_uri, session->page);
639     curl_easy_setopt(curl, CURLOPT_URL, endpoint);
640     curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
641     break;
642 gregoa 1473
643 gregoa 2248 case ACTION_PUBLIC:
644     sprintf(endpoint, "%s%s?page=%d", session->hosturl,
645     public_uri, session->page);
646     curl_easy_setopt(curl, CURLOPT_URL, endpoint);
647     break;
648 gregoa 1509
649 gregoa 2248 case ACTION_GROUP:
650     sprintf(endpoint, "%s%s%s.xml?page=%d",
651     session->hosturl, group_uri, session->group,
652     session->page);
653     curl_easy_setopt(curl, CURLOPT_URL, endpoint);
654     break;
655 gregoa 1473
656 gregoa 2248 default:
657     break;
658 gregoa 2235 }
659 gregoa 1473
660 gregoa 2235 if (session->proxy)
661     curl_easy_setopt(curl, CURLOPT_PROXY, session->proxy);
662    
663     if (debug)
664     curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
665    
666     dbg("user_password = %s\n", user_password);
667     dbg("data = %s\n", data);
668     dbg("proxy = %s\n", session->proxy);
669    
670     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
671     curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_buf);
672     if (!session->dry_run) {
673     res = curl_easy_perform(curl);
674 gregoa 2457 if (!session->background) {
675     xmlDocPtr doc;
676     xmlNodePtr current;
677    
678     if (res) {
679     fprintf(stderr, "error(%d) trying to "
680     "perform operation\n", res);
681 gregoa 2709 curl_easy_cleanup(curl);
682     if (session->action == ACTION_UPDATE)
683     curl_formfree(formpost);
684     bti_curl_buffer_free(curl_buf);
685 gregoa 2457 return -EINVAL;
686     }
687    
688     doc = xmlReadMemory(curl_buf->data,
689     curl_buf->length,
690     "response.xml", NULL,
691     XML_PARSE_NOERROR);
692 gregoa 2709 if (doc == NULL) {
693     curl_easy_cleanup(curl);
694     if (session->action == ACTION_UPDATE)
695     curl_formfree(formpost);
696     bti_curl_buffer_free(curl_buf);
697 gregoa 2457 return -EINVAL;
698 gregoa 2709 }
699 gregoa 2457
700     current = xmlDocGetRootElement(doc);
701     if (current == NULL) {
702     fprintf(stderr, "empty document\n");
703     xmlFreeDoc(doc);
704 gregoa 2709 curl_easy_cleanup(curl);
705     if (session->action == ACTION_UPDATE)
706     curl_formfree(formpost);
707     bti_curl_buffer_free(curl_buf);
708 gregoa 2457 return -EINVAL;
709     }
710    
711     if (xmlStrcmp(current->name, (const xmlChar *)"status")) {
712     fprintf(stderr, "unexpected document type\n");
713     xmlFreeDoc(doc);
714 gregoa 2709 curl_easy_cleanup(curl);
715     if (session->action == ACTION_UPDATE)
716     curl_formfree(formpost);
717     bti_curl_buffer_free(curl_buf);
718 gregoa 2457 return -EINVAL;
719     }
720    
721     xmlFreeDoc(doc);
722 gregoa 2235 }
723 gregoa 1653 }
724 gregoa 2235
725     curl_easy_cleanup(curl);
726     if (session->action == ACTION_UPDATE)
727     curl_formfree(formpost);
728     bti_curl_buffer_free(curl_buf);
729     } else {
730     switch (session->action) {
731 gregoa 2248 case ACTION_UPDATE:
732     escaped_tweet = oauth_url_escape(session->tweet);
733 gregoa 2338 if (session->replyto) {
734     sprintf(endpoint,
735     "%s%s?status=%s&in_reply_to_status_id=%s",
736     session->hosturl, update_uri,
737     escaped_tweet, session->replyto);
738     } else {
739     sprintf(endpoint, "%s%s?status=%s",
740     session->hosturl, update_uri,
741     escaped_tweet);
742     }
743    
744 gregoa 2248 is_post = 1;
745     break;
746     case ACTION_USER:
747     sprintf(endpoint, "%s%s%s.xml?page=%d",
748     session->hosturl, user_uri, session->user,
749     session->page);
750     break;
751     case ACTION_REPLIES:
752     sprintf(endpoint, "%s%s?page=%d", session->hosturl,
753     mentions_uri, session->page);
754     break;
755     case ACTION_PUBLIC:
756     sprintf(endpoint, "%s%s?page=%d", session->hosturl,
757     public_uri, session->page);
758     break;
759     case ACTION_GROUP:
760     sprintf(endpoint, "%s%s%s.xml?page=%d",
761     session->hosturl, group_uri, session->group,
762     session->page);
763     break;
764     case ACTION_FRIENDS:
765     sprintf(endpoint, "%s%s?page=%d", session->hosturl,
766     friends_uri, session->page);
767     break;
768 gregoa 2338 case ACTION_RETWEET:
769     sprintf(endpoint, "%s%s%s.xml", session->hosturl,
770     retweet_uri, session->retweet);
771     is_post = 1;
772     break;
773 gregoa 2248 default:
774     break;
775 gregoa 2235 }
776    
777 gregoa 2338 dbg("%s\n", endpoint);
778     if (!session->dry_run) {
779     if (is_post) {
780     req_url = oauth_sign_url2(endpoint, &postarg, OA_HMAC,
781     NULL, session->consumer_key,
782     session->consumer_secret,
783     session->access_token_key,
784     session->access_token_secret);
785     reply = oauth_http_post(req_url, postarg);
786     } else {
787     req_url = oauth_sign_url2(endpoint, NULL, OA_HMAC, NULL,
788     session->consumer_key,
789     session->consumer_secret,
790     session->access_token_key,
791     session->access_token_secret);
792     reply = oauth_http_get(req_url, postarg);
793     }
794    
795     dbg("%s\n", req_url);
796     dbg("%s\n", reply);
797     if (req_url)
798     free(req_url);
799 gregoa 2235 }
800    
801 gregoa 2451 if (!reply) {
802     fprintf(stderr, "Error retrieving from URL (%s)\n", endpoint);
803     return -EIO;
804     }
805    
806 gregoa 2338 if ((session->action != ACTION_UPDATE) &&
807     (session->action != ACTION_RETWEET))
808     parse_timeline(reply, session);
809 gregoa 1473 }
810     return 0;
811     }
812    
813 gregoa 1526 static void log_session(struct session *session, int retval)
814     {
815     FILE *log_file;
816     char *filename;
817    
818 gregoa 1532 /* Only log something if we have a log file set */
819     if (!session->logfile)
820     return;
821 gregoa 1526
822 gregoa 1532 filename = alloca(strlen(session->homedir) +
823     strlen(session->logfile) + 3);
824 gregoa 1526
825 gregoa 1532 sprintf(filename, "%s/%s", session->homedir, session->logfile);
826    
827 gregoa 1526 log_file = fopen(filename, "a+");
828     if (log_file == NULL)
829     return;
830    
831 gregoa 1623 switch (session->action) {
832     case ACTION_UPDATE:
833     if (retval)
834     fprintf(log_file, "%s: host=%s tweet failed\n",
835 gregoa 1988 session->time, session->hostname);
836 gregoa 1623 else
837     fprintf(log_file, "%s: host=%s tweet=%s\n",
838 gregoa 2159 session->time, session->hostname,
839     session->tweet);
840 gregoa 1623 break;
841     case ACTION_FRIENDS:
842     fprintf(log_file, "%s: host=%s retrieving friends timeline\n",
843 gregoa 1988 session->time, session->hostname);
844 gregoa 1623 break;
845     case ACTION_USER:
846     fprintf(log_file, "%s: host=%s retrieving %s's timeline\n",
847 gregoa 1988 session->time, session->hostname, session->user);
848 gregoa 1623 break;
849     case ACTION_REPLIES:
850     fprintf(log_file, "%s: host=%s retrieving replies\n",
851 gregoa 1988 session->time, session->hostname);
852 gregoa 1623 break;
853     case ACTION_PUBLIC:
854     fprintf(log_file, "%s: host=%s retrieving public timeline\n",
855 gregoa 1988 session->time, session->hostname);
856 gregoa 1623 break;
857 gregoa 1952 case ACTION_GROUP:
858     fprintf(log_file, "%s: host=%s retrieving group timeline\n",
859 gregoa 1988 session->time, session->hostname);
860 gregoa 1952 break;
861 gregoa 1623 default:
862     break;
863     }
864 gregoa 1526
865     fclose(log_file);
866     }
867    
868 gregoa 1623 static char *get_string_from_stdin(void)
869     {
870     char *temp;
871     char *string;
872    
873     string = zalloc(1000);
874     if (!string)
875     return NULL;
876    
877 gregoa 2709 if (!fgets(string, 999, stdin)) {
878     free(string);
879 gregoa 1623 return NULL;
880 gregoa 2709 }
881 gregoa 1623 temp = strchr(string, '\n');
882 gregoa 2159 if (temp)
883     *temp = '\0';
884 gregoa 1623 return string;
885     }
886    
887 gregoa 1988 static void read_password(char *buf, size_t len, char *host)
888 gregoa 1952 {
889     char pwd[80];
890     struct termios old;
891     struct termios tp;
892    
893     tcgetattr(0, &tp);
894     old = tp;
895    
896     tp.c_lflag &= (~ECHO);
897     tcsetattr(0, TCSANOW, &tp);
898    
899 gregoa 1988 fprintf(stdout, "Enter password for %s: ", host);
900 gregoa 1952 fflush(stdout);
901     tcflow(0, TCOOFF);
902 gregoa 2709
903     /*
904     * I'd like to do something with the return value here, but really,
905     * what can be done?
906     */
907     (void)scanf("%79s", pwd);
908    
909 gregoa 1952 tcflow(0, TCOON);
910     fprintf(stdout, "\n");
911    
912     tcsetattr(0, TCSANOW, &old);
913    
914     strncpy(buf, pwd, len);
915     buf[len-1] = '\0';
916     }
917    
918 gregoa 1653 static int find_urls(const char *tweet, int **pranges)
919     {
920     /*
921     * magic obtained from
922     * http://www.geekpedia.com/KB65_How-to-validate-an-URL-using-RegEx-in-Csharp.html
923     */
924     static const char *re_magic =
925     "(([a-zA-Z][0-9a-zA-Z+\\-\\.]*:)/{1,3}"
926     "[0-9a-zA-Z;/~?:@&=+$\\.\\-_'()%]+)"
927     "(#[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)?";
928     pcre *re;
929     const char *errptr;
930     int erroffset;
931     int ovector[10] = {0,};
932     const size_t ovsize = sizeof(ovector)/sizeof(*ovector);
933     int startoffset, tweetlen;
934     int i, rc;
935     int rbound = 10;
936     int rcount = 0;
937     int *ranges = malloc(sizeof(int) * rbound);
938    
939     re = pcre_compile(re_magic,
940     PCRE_NO_AUTO_CAPTURE,
941     &errptr, &erroffset, NULL);
942     if (!re) {
943     fprintf(stderr, "pcre_compile @%u: %s\n", erroffset, errptr);
944     exit(1);
945     }
946    
947     tweetlen = strlen(tweet);
948     for (startoffset = 0; startoffset < tweetlen; ) {
949    
950     rc = pcre_exec(re, NULL, tweet, strlen(tweet), startoffset, 0,
951     ovector, ovsize);
952     if (rc == PCRE_ERROR_NOMATCH)
953     break;
954    
955     if (rc < 0) {
956     fprintf(stderr, "pcre_exec @%u: %s\n",
957     erroffset, errptr);
958     exit(1);
959     }
960    
961     for (i = 0; i < rc; i += 2) {
962     if ((rcount+2) == rbound) {
963     rbound *= 2;
964     ranges = realloc(ranges, sizeof(int) * rbound);
965     }
966    
967     ranges[rcount++] = ovector[i];
968     ranges[rcount++] = ovector[i+1];
969     }
970    
971     startoffset = ovector[1];
972     }
973    
974     pcre_free(re);
975    
976     *pranges = ranges;
977     return rcount;
978     }
979    
980     /**
981     * bidirectional popen() call
982     *
983     * @param rwepipe - int array of size three
984     * @param exe - program to run
985     * @param argv - argument list
986     * @return pid or -1 on error
987     *
988     * The caller passes in an array of three integers (rwepipe), on successful
989     * execution it can then write to element 0 (stdin of exe), and read from
990     * element 1 (stdout) and 2 (stderr).
991     */
992     static int popenRWE(int *rwepipe, const char *exe, const char *const argv[])
993     {
994     int in[2];
995     int out[2];
996     int err[2];
997     int pid;
998     int rc;
999    
1000     rc = pipe(in);
1001     if (rc < 0)
1002     goto error_in;
1003    
1004     rc = pipe(out);
1005     if (rc < 0)
1006     goto error_out;
1007    
1008     rc = pipe(err);
1009     if (rc < 0)
1010     goto error_err;
1011    
1012     pid = fork();
1013     if (pid > 0) {
1014     /* parent */
1015     close(in[0]);
1016     close(out[1]);
1017     close(err[1]);
1018     rwepipe[0] = in[1];
1019     rwepipe[1] = out[0];
1020     rwepipe[2] = err[0];
1021     return pid;
1022     } else if (pid == 0) {
1023     /* child */
1024     close(in[1]);
1025     close(out[0]);
1026     close(err[0]);
1027     close(0);
1028     rc = dup(in[0]);
1029     close(1);
1030     rc = dup(out[1]);
1031     close(2);
1032     rc = dup(err[1]);
1033    
1034     execvp(exe, (char **)argv);
1035     exit(1);
1036     } else
1037     goto error_fork;
1038    
1039     return pid;
1040    
1041     error_fork:
1042     close(err[0]);
1043     close(err[1]);
1044     error_err:
1045     close(out[0]);
1046     close(out[1]);
1047     error_out:
1048     close(in[0]);
1049     close(in[1]);
1050     error_in:
1051     return -1;
1052     }
1053    
1054     static int pcloseRWE(int pid, int *rwepipe)
1055     {
1056 gregoa 2709 int status;
1057 gregoa 1653 close(rwepipe[0]);
1058     close(rwepipe[1]);
1059     close(rwepipe[2]);
1060 gregoa 2709 (void)waitpid(pid, &status, 0);
1061 gregoa 1653 return status;
1062     }
1063    
1064     static char *shrink_one_url(int *rwepipe, char *big)
1065     {
1066     int biglen = strlen(big);
1067     char *small;
1068     int smalllen;
1069     int rc;
1070    
1071     rc = dprintf(rwepipe[0], "%s\n", big);
1072     if (rc < 0)
1073     return big;
1074    
1075     smalllen = biglen + 128;
1076     small = malloc(smalllen);
1077     if (!small)
1078     return big;
1079    
1080     rc = read(rwepipe[1], small, smalllen);
1081     if (rc < 0 || rc > biglen)
1082     goto error_free_small;
1083    
1084     if (strncmp(small, "http://", 7))
1085     goto error_free_small;
1086    
1087     smalllen = rc;
1088     while (smalllen && isspace(small[smalllen-1]))
1089     small[--smalllen] = 0;
1090    
1091     free(big);
1092     return small;
1093    
1094     error_free_small:
1095     free(small);
1096     return big;
1097     }
1098    
1099     static char *shrink_urls(char *text)
1100     {
1101     int *ranges;
1102     int rcount;
1103     int i;
1104     int inofs = 0;
1105     int outofs = 0;
1106     const char *const shrink_args[] = {
1107     "bti-shrink-urls",
1108     NULL
1109     };
1110     int shrink_pid;
1111     int shrink_pipe[3];
1112     int inlen = strlen(text);
1113    
1114     dbg("before len=%u\n", inlen);
1115    
1116     shrink_pid = popenRWE(shrink_pipe, shrink_args[0], shrink_args);
1117     if (shrink_pid < 0)
1118     return text;
1119    
1120     rcount = find_urls(text, &ranges);
1121     if (!rcount)
1122     return text;
1123    
1124     for (i = 0; i < rcount; i += 2) {
1125     int url_start = ranges[i];
1126     int url_end = ranges[i+1];
1127     int long_url_len = url_end - url_start;
1128     char *url = strndup(text + url_start, long_url_len);
1129     int short_url_len;
1130     int not_url_len = url_start - inofs;
1131    
1132     dbg("long url[%u]: %s\n", long_url_len, url);
1133     url = shrink_one_url(shrink_pipe, url);
1134     short_url_len = url ? strlen(url) : 0;
1135     dbg("short url[%u]: %s\n", short_url_len, url);
1136    
1137     if (!url || short_url_len >= long_url_len) {
1138     /* The short url ended up being too long
1139     * or unavailable */
1140     if (inofs) {
1141     strncpy(text + outofs, text + inofs,
1142     not_url_len + long_url_len);
1143     }
1144     inofs += not_url_len + long_url_len;
1145     outofs += not_url_len + long_url_len;
1146    
1147     } else {
1148     /* copy the unmodified block */
1149     strncpy(text + outofs, text + inofs, not_url_len);
1150     inofs += not_url_len;
1151     outofs += not_url_len;
1152    
1153     /* copy the new url */
1154     strncpy(text + outofs, url, short_url_len);
1155     inofs += long_url_len;
1156     outofs += short_url_len;
1157     }
1158    
1159     free(url);
1160     }
1161    
1162     /* copy the last block after the last match */
1163     if (inofs) {
1164     int tail = inlen - inofs;
1165     if (tail) {
1166     strncpy(text + outofs, text + inofs, tail);
1167     outofs += tail;
1168     }
1169     }
1170    
1171     free(ranges);
1172    
1173     (void)pcloseRWE(shrink_pid, shrink_pipe);
1174    
1175     text[outofs] = 0;
1176     dbg("after len=%u\n", outofs);
1177     return text;
1178     }
1179    
1180 gregoa 1473 int main(int argc, char *argv[], char *envp[])
1181     {
1182     static const struct option options[] = {
1183     { "debug", 0, NULL, 'd' },
1184 gregoa 1741 { "verbose", 0, NULL, 'V' },
1185 gregoa 1473 { "account", 1, NULL, 'a' },
1186     { "password", 1, NULL, 'p' },
1187     { "host", 1, NULL, 'H' },
1188 gregoa 1513 { "proxy", 1, NULL, 'P' },
1189 gregoa 1623 { "action", 1, NULL, 'A' },
1190     { "user", 1, NULL, 'u' },
1191 gregoa 1952 { "group", 1, NULL, 'G' },
1192 gregoa 1532 { "logfile", 1, NULL, 'L' },
1193 gregoa 1653 { "shrink-urls", 0, NULL, 's' },
1194 gregoa 1473 { "help", 0, NULL, 'h' },
1195     { "bash", 0, NULL, 'b' },
1196 gregoa 2248 { "background", 0, NULL, 'B' },
1197 gregoa 1653 { "dry-run", 0, NULL, 'n' },
1198     { "page", 1, NULL, 'g' },
1199 gregoa 2709 { "column", 1, NULL, 'o' },
1200 gregoa 1473 { "version", 0, NULL, 'v' },
1201 gregoa 2159 { "config", 1, NULL, 'c' },
1202 gregoa 2235 { "replyto", 1, NULL, 'r' },
1203 gregoa 2338 { "retweet", 1, NULL, 'w' },
1204 gregoa 1473 { }
1205     };
1206     struct session *session;
1207     pid_t child;
1208     char *tweet;
1209 gregoa 1952 static char password[80];
1210 gregoa 1526 int retval = 0;
1211 gregoa 1473 int option;
1212 gregoa 1513 char *http_proxy;
1213 gregoa 2709 char *home;
1214     const char *config_file;
1215 gregoa 1526 time_t t;
1216 gregoa 1653 int page_nr;
1217 gregoa 1526
1218 gregoa 1563 debug = 0;
1219    
1220 gregoa 1473 session = session_alloc();
1221     if (!session) {
1222     fprintf(stderr, "no more memory...\n");
1223     return -1;
1224     }
1225    
1226 gregoa 1526 /* get the current time so that we can log it later */
1227     time(&t);
1228     session->time = strdup(ctime(&t));
1229     session->time[strlen(session->time)-1] = 0x00;
1230    
1231 gregoa 2709 /*
1232     * Get the home directory so we can try to find a config file.
1233     * If we have no home dir set up, look in /etc/bti
1234     */
1235     home = getenv("HOME");
1236     if (home) {
1237     /* We have a home dir, so this might be a user */
1238     session->homedir = strdup(home);
1239     config_file = config_user_default;
1240     } else {
1241     session->homedir = strdup("");
1242     config_file = config_default;
1243     }
1244 gregoa 1526
1245 gregoa 2159 /* set up a default config file location (traditionally ~/.bti) */
1246 gregoa 2709 session->configfile = zalloc(strlen(session->homedir) + strlen(config_file) + 7);
1247     sprintf(session->configfile, "%s/%s", session->homedir, config_file);
1248 gregoa 2159
1249 gregoa 1513 /* Set environment variables first, before reading command line options
1250     * or config file values. */
1251     http_proxy = getenv("http_proxy");
1252     if (http_proxy) {
1253     if (session->proxy)
1254     free(session->proxy);
1255     session->proxy = strdup(http_proxy);
1256     dbg("http_proxy = %s\n", session->proxy);
1257     }
1258    
1259 gregoa 2338 bti_parse_configfile(session);
1260 gregoa 1473
1261     while (1) {
1262 gregoa 2248 option = getopt_long_only(argc, argv,
1263 gregoa 2709 "dp:P:H:a:A:u:c:hg:o:G:sr:nVvw:",
1264 gregoa 1473 options, NULL);
1265     if (option == -1)
1266     break;
1267     switch (option) {
1268     case 'd':
1269     debug = 1;
1270     break;
1271 gregoa 1741 case 'V':
1272 gregoa 2338 session->verbose = 1;
1273 gregoa 1741 break;
1274 gregoa 1473 case 'a':
1275     if (session->account)
1276     free(session->account);
1277     session->account = strdup(optarg);
1278     dbg("account = %s\n", session->account);
1279     break;
1280 gregoa 1653 case 'g':
1281     page_nr = atoi(optarg);
1282     dbg("page = %d\n", page_nr);
1283     session->page = page_nr;
1284     break;
1285 gregoa 2709 case 'o':
1286     session->column_output = atoi(optarg);
1287     dbg("column_output = %d\n", session->column_output);
1288     break;
1289 gregoa 2235 case 'r':
1290     session->replyto = strdup(optarg);
1291     dbg("in_reply_to_status_id = %s\n", session->replyto);
1292     break;
1293 gregoa 2338 case 'w':
1294     session->retweet = strdup(optarg);
1295     dbg("Retweet ID = %s\n", session->retweet);
1296     break;
1297 gregoa 1473 case 'p':
1298     if (session->password)
1299     free(session->password);
1300     session->password = strdup(optarg);
1301     dbg("password = %s\n", session->password);
1302     break;
1303 gregoa 1513 case 'P':
1304     if (session->proxy)
1305     free(session->proxy);
1306     session->proxy = strdup(optarg);
1307     dbg("proxy = %s\n", session->proxy);
1308     break;
1309 gregoa 1623 case 'A':
1310     if (strcasecmp(optarg, "update") == 0)
1311     session->action = ACTION_UPDATE;
1312     else if (strcasecmp(optarg, "friends") == 0)
1313     session->action = ACTION_FRIENDS;
1314     else if (strcasecmp(optarg, "user") == 0)
1315     session->action = ACTION_USER;
1316     else if (strcasecmp(optarg, "replies") == 0)
1317     session->action = ACTION_REPLIES;
1318     else if (strcasecmp(optarg, "public") == 0)
1319     session->action = ACTION_PUBLIC;
1320 gregoa 1952 else if (strcasecmp(optarg, "group") == 0)
1321     session->action = ACTION_GROUP;
1322 gregoa 2457 else if (strcasecmp(optarg, "retweet") == 0)
1323 gregoa 2338 session->action = ACTION_RETWEET;
1324 gregoa 1623 else
1325     session->action = ACTION_UNKNOWN;
1326     dbg("action = %d\n", session->action);
1327     break;
1328     case 'u':
1329     if (session->user)
1330     free(session->user);
1331     session->user = strdup(optarg);
1332     dbg("user = %s\n", session->user);
1333     break;
1334 gregoa 1952
1335     case 'G':
1336     if (session->group)
1337     free(session->group);
1338     session->group = strdup(optarg);
1339     dbg("group = %s\n", session->group);
1340     break;
1341 gregoa 1532 case 'L':
1342     if (session->logfile)
1343     free(session->logfile);
1344     session->logfile = strdup(optarg);
1345     dbg("logfile = %s\n", session->logfile);
1346     break;
1347 gregoa 1653 case 's':
1348     session->shrink_urls = 1;
1349     break;
1350 gregoa 1473 case 'H':
1351 gregoa 1714 if (session->hosturl)
1352     free(session->hosturl);
1353 gregoa 1988 if (session->hostname)
1354     free(session->hostname);
1355 gregoa 1714 if (strcasecmp(optarg, "twitter") == 0) {
1356 gregoa 1473 session->host = HOST_TWITTER;
1357 gregoa 1714 session->hosturl = strdup(twitter_host);
1358 gregoa 1988 session->hostname = strdup(twitter_name);
1359 gregoa 1714 } else if (strcasecmp(optarg, "identica") == 0) {
1360 gregoa 1473 session->host = HOST_IDENTICA;
1361 gregoa 1714 session->hosturl = strdup(identica_host);
1362 gregoa 1988 session->hostname = strdup(identica_name);
1363 gregoa 1714 } else {
1364     session->host = HOST_CUSTOM;
1365     session->hosturl = strdup(optarg);
1366 gregoa 1988 session->hostname = strdup(optarg);
1367 gregoa 1714 }
1368 gregoa 1473 dbg("host = %d\n", session->host);
1369     break;
1370     case 'b':
1371 gregoa 1563 session->bash = 1;
1372 gregoa 2248 /* fall-through intended */
1373     case 'B':
1374     session->background = 1;
1375 gregoa 1473 break;
1376 gregoa 2159 case 'c':
1377     if (session->configfile)
1378     free(session->configfile);
1379     session->configfile = strdup(optarg);
1380     dbg("configfile = %s\n", session->configfile);
1381    
1382     /*
1383 gregoa 2248 * read the config file now. Yes, this could override
1384     * previously set options from the command line, but
1385     * the user asked for it...
1386 gregoa 2159 */
1387 gregoa 2338 bti_parse_configfile(session);
1388 gregoa 2159 break;
1389 gregoa 1473 case 'h':
1390     display_help();
1391     goto exit;
1392 gregoa 1653 case 'n':
1393     session->dry_run = 1;
1394     break;
1395 gregoa 1473 case 'v':
1396     display_version();
1397     goto exit;
1398     default:
1399     display_help();
1400     goto exit;
1401     }
1402     }
1403    
1404 gregoa 2159 session_readline_init(session);
1405 gregoa 1682 /*
1406     * Show the version to make it easier to determine what
1407     * is going on here
1408     */
1409     if (debug)
1410     display_version();
1411    
1412 gregoa 2235 if (session->host == HOST_TWITTER) {
1413     if (!session->consumer_key || !session->consumer_secret) {
1414 gregoa 2338 if (session->action == ACTION_USER ||
1415     session->action == ACTION_PUBLIC) {
1416 gregoa 2457 /*
1417     * Some actions may still work without
1418     * authentication
1419     */
1420 gregoa 2338 session->guest = 1;
1421     } else {
1422     fprintf(stderr,
1423     "Twitter no longer supports HTTP basic authentication.\n"
1424     "Both consumer key, and consumer secret are required"
1425     " for bti in order to behave as an OAuth consumer.\n");
1426     goto exit;
1427     }
1428 gregoa 2235 }
1429     if (session->action == ACTION_GROUP) {
1430     fprintf(stderr, "Groups only work in Identi.ca.\n");
1431     goto exit;
1432     }
1433     } else {
1434 gregoa 2248 if (!session->consumer_key || !session->consumer_secret)
1435 gregoa 2235 session->no_oauth = 1;
1436     }
1437 gregoa 2248
1438 gregoa 2235 if (session->no_oauth) {
1439     if (!session->account) {
1440 gregoa 2248 fprintf(stdout, "Enter account for %s: ",
1441     session->hostname);
1442 gregoa 2235 session->account = session->readline(NULL);
1443     }
1444     if (!session->password) {
1445 gregoa 2248 read_password(password, sizeof(password),
1446     session->hostname);
1447 gregoa 2235 session->password = strdup(password);
1448     }
1449 gregoa 2338 } else if (!session->guest) {
1450 gregoa 2248 if (!session->access_token_key ||
1451     !session->access_token_secret) {
1452 gregoa 2235 request_access_token(session);
1453     goto exit;
1454 gregoa 2248 }
1455 gregoa 2235 }
1456    
1457 gregoa 1623 if (session->action == ACTION_UNKNOWN) {
1458 gregoa 2248 fprintf(stderr, "Unknown action, valid actions are:\n"
1459     "'update', 'friends', 'public', 'replies', 'group' or 'user'.\n");
1460 gregoa 1623 goto exit;
1461     }
1462    
1463 gregoa 1952 if (session->action == ACTION_GROUP && !session->group) {
1464     fprintf(stdout, "Enter group name: ");
1465 gregoa 1988 session->group = session->readline(NULL);
1466 gregoa 1952 }
1467    
1468 gregoa 2338 if (session->action == ACTION_RETWEET) {
1469 gregoa 2451 if (!session->retweet) {
1470     char *rtid;
1471 gregoa 2338
1472 gregoa 2451 fprintf(stdout, "Status ID to retweet: ");
1473     rtid = get_string_from_stdin();
1474     session->retweet = zalloc(strlen(rtid) + 10);
1475 gregoa 2457 sprintf(session->retweet, "%s", rtid);
1476 gregoa 2451 free(rtid);
1477     }
1478    
1479     if (!session->retweet || strlen(session->retweet) == 0) {
1480 gregoa 2338 dbg("no retweet?\n");
1481     return -1;
1482     }
1483    
1484     dbg("retweet ID = %s\n", session->retweet);
1485     }
1486    
1487 gregoa 1623 if (session->action == ACTION_UPDATE) {
1488 gregoa 2248 if (session->background || !session->interactive)
1489 gregoa 1623 tweet = get_string_from_stdin();
1490     else
1491 gregoa 1988 tweet = session->readline("tweet: ");
1492 gregoa 1623 if (!tweet || strlen(tweet) == 0) {
1493     dbg("no tweet?\n");
1494     return -1;
1495     }
1496    
1497 gregoa 1653 if (session->shrink_urls)
1498     tweet = shrink_urls(tweet);
1499    
1500 gregoa 1623 session->tweet = zalloc(strlen(tweet) + 10);
1501     if (session->bash)
1502 gregoa 1952 sprintf(session->tweet, "%c %s",
1503     getuid() ? '$' : '#', tweet);
1504 gregoa 1623 else
1505     sprintf(session->tweet, "%s", tweet);
1506    
1507     free(tweet);
1508     dbg("tweet = %s\n", session->tweet);
1509 gregoa 1473 }
1510    
1511 gregoa 1653 if (session->page == 0)
1512     session->page = 1;
1513 gregoa 2159 dbg("config file = %s\n", session->configfile);
1514 gregoa 1473 dbg("host = %d\n", session->host);
1515 gregoa 1623 dbg("action = %d\n", session->action);
1516 gregoa 1473
1517     /* fork ourself so that the main shell can get on
1518     * with it's life as we try to connect and handle everything
1519     */
1520 gregoa 2248 if (session->background) {
1521 gregoa 1473 child = fork();
1522 gregoa 1623 if (child) {
1523 gregoa 1473 dbg("child is %d\n", child);
1524     exit(0);
1525     }
1526     }
1527    
1528 gregoa 1623 retval = send_request(session);
1529 gregoa 2248 if (retval && !session->background)
1530 gregoa 1623 fprintf(stderr, "operation failed\n");
1531 gregoa 1473
1532 gregoa 1532 log_session(session, retval);
1533 gregoa 1526 exit:
1534 gregoa 2159 session_readline_cleanup(session);
1535 gregoa 1473 session_free(session);
1536 gregoa 1526 return retval;;
1537 gregoa 1473 }

  ViewVC Help
Powered by ViewVC 1.1.26