From: gregor herrmann Date: Wed, 12 Jan 2011 23:11:47 +0000 (+0100) Subject: Merge branch 'master' into gregoa X-Git-Url: https://git.toastfreeware.priv.at/gregoa/bti.git/commitdiff_plain/265cb514ec1ec84872929a9182c9091b73009272?hp=a2a9b063ba94efbaa4a70e2d9a0e2ffd4d3c5531 Merge branch 'master' into gregoa --- diff --git a/ChangeLog b/ChangeLog index ef2183a..a71b1a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,39 @@ +Summary of changes from v028 to v029 +============================================ + +Amir Mohammad Saied (2): + Update my copyright date. + Fixing Identi.ca's OAuth request_token URI + +Dwi Sasongko S (1): + Support proper 'reply_to' and 'retweet' + +Greg Kroah-Hartman (9): + updated man pages generated from the docbook source + Merge https://github.com/leif81/bti into main tree + Merge testing branch into master + Merge branch 'oauth-readme' of https://github.com/agimenez/bti into agimenez-oauth-readme + reformat README to have a bit shorter lines + move verbose flag into the session structure + create bti.h + add config.c + move output logic to one function + +Justin Forest (1): + Support shrinking with bit.ly and j.mp. + +L. Alberto Giménez (1): + Document how to configure OAuth. + +Leif Gruenwoldt (1): + user and public actions may not require auth + +gregor herrmann (3): + Add documentation for new shrink_host feature in bti-shrink-urls. + Document the new --retweet feature in bti.xml and add it to bash completion. + Active --dry-run for OAuth. + + Summary of changes from v027 to v028 ============================================ diff --git a/Makefile.am b/Makefile.am index 7b5953e..904a058 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,6 +5,8 @@ bin_SCRIPTS = \ bti-shrink-urls bti_SOURCES = \ + bti.h \ + config.c \ bti.c bti_CFLAGS = \ diff --git a/README b/README index ff709f7..1c5022f 100644 --- a/README +++ b/README @@ -17,3 +17,27 @@ bti is developed using git and the tree can be found at: git://github.com/gregkh/bti.git and browsed at: http://github.com/gregkh/bti/ + + +About OAuth configuration + +If you want to use an OAuth-enabled service (like twitter), you should +configure bti to use the consumer key and secret shipped with the source +code (check the oath.keys file). + +For example, you should add the following two lines to your ~/.bti +configuration file: + +---8<------------------- +# Consumer key +consumer_key=cZy8DdioswAfu3LJYg6E2w + +# Consumer secret +consumer_secret=fnIGGU0T12mMWKjmThUdSeKN32NLWfmnwapwubVQ +---8<------------------- + +The next time that you run bti, you will be told to visit an URL that +will provide you a PIN number. You should then input that number in the +bti prompt, and you will be given two new configuration values +(access_token_secret and access_token_key) that you need to add to your +bti configuration file to authenticate requests from bti. diff --git a/RELEASE-NOTES b/RELEASE-NOTES index ef98163..4bde935 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,3 +1,8 @@ +bti 029 +============= +Bugfixes for a variety of little things +lots better oauth documentation. + bti 028 ============= Build fixes thanks to Flameeyes and other minor tweaks. diff --git a/bti-shrink-urls.1 b/bti-shrink-urls.1 index 288c3ec..0c5ca7f 100644 --- a/bti-shrink-urls.1 +++ b/bti-shrink-urls.1 @@ -1,6 +1,7 @@ +'\" t .\" Title: bti-shrink-urls .\" Author: [see the "AUTHOR" section] -.\" Generator: DocBook XSL Stylesheets v1.74.0 +.\" Generator: DocBook XSL Stylesheets v1.75.2 .\" Date: March 2009 .\" Manual: bti-shrink-urls .\" Source: bti-shrink-urls @@ -8,155 +9,6 @@ .\" .TH "BTI\-SHRINK\-URLS" "1" "March 2009" "bti-shrink-urls" "bti-shrink-urls" .\" ----------------------------------------------------------------- -.\" * (re)Define some macros -.\" ----------------------------------------------------------------- -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" toupper - uppercase a string (locale-aware) -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de toupper -.tr aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ -\\$* -.tr aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz -.. -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" SH-xref - format a cross-reference to an SH section -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de SH-xref -.ie n \{\ -.\} -.toupper \\$* -.el \{\ -\\$* -.\} -.. -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" SH - level-one heading that works better for non-TTY output -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de1 SH -.\" put an extra blank line of space above the head in non-TTY output -.if t \{\ -.sp 1 -.\} -.sp \\n[PD]u -.nr an-level 1 -.set-an-margin -.nr an-prevailing-indent \\n[IN] -.fi -.in \\n[an-margin]u -.ti 0 -.HTML-TAG ".NH \\n[an-level]" -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -\." make the size of the head bigger -.ps +3 -.ft B -.ne (2v + 1u) -.ie n \{\ -.\" if n (TTY output), use uppercase -.toupper \\$* -.\} -.el \{\ -.nr an-break-flag 0 -.\" if not n (not TTY), use normal case (not uppercase) -\\$1 -.in \\n[an-margin]u -.ti 0 -.\" if not n (not TTY), put a border/line under subheading -.sp -.6 -\l'\n(.lu' -.\} -.. -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" SS - level-two heading that works better for non-TTY output -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de1 SS -.sp \\n[PD]u -.nr an-level 1 -.set-an-margin -.nr an-prevailing-indent \\n[IN] -.fi -.in \\n[IN]u -.ti \\n[SN]u -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -.ps \\n[PS-SS]u -\." make the size of the head bigger -.ps +2 -.ft B -.ne (2v + 1u) -.if \\n[.$] \&\\$* -.. -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" BB/BE - put background/screen (filled box) around block of text -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de BB -.if t \{\ -.sp -.5 -.br -.in +2n -.ll -2n -.gcolor red -.di BX -.\} -.. -.de EB -.if t \{\ -.if "\\$2"adjust-for-leading-newline" \{\ -.sp -1 -.\} -.br -.di -.in -.ll -.gcolor -.nr BW \\n(.lu-\\n(.i -.nr BH \\n(dn+.5v -.ne \\n(BHu+.5v -.ie "\\$2"adjust-for-leading-newline" \{\ -\M[\\$1]\h'1n'\v'+.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[] -.\} -.el \{\ -\M[\\$1]\h'1n'\v'-.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[] -.\} -.in 0 -.sp -.5v -.nf -.BX -.in -.sp .5v -.fi -.\} -.. -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" BM/EM - put colored marker in margin next to block of text -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.de BM -.if t \{\ -.br -.ll -2n -.gcolor red -.di BX -.\} -.. -.de EM -.if t \{\ -.br -.di -.ll -.gcolor -.nr BH \\n(dn -.ne \\n(BHu -\M[\\$1]\D'P -.75n 0 0 \\n(BHu -(\\n[.i]u - \\n(INu - .75n) 0 0 -\\n(BHu'\M[] -.in 0 -.nf -.BX -.in -.fi -.\} -.. -.\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation @@ -166,18 +18,16 @@ .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- -.SH "Name" +.SH "NAME" bti-shrink-urls \- convert URLs to a shorter form using a web service -.SH "Synopsis" -.fam C +.SH "SYNOPSIS" .HP \w'\fBbti\fR\ 'u \fBbti\fR [\fB\-\-escaped\fR] [\fB\-\-help\fR] [\fBURL\fR] -.fam .SH "DESCRIPTION" .PP bti\-shrink\-urls converts URLs to a shorter form using a web service\&. .PP -Currently only http://2tu\&.us/ is supported\&. +Currently http://2tu\&.us/ (default) and http://bit\&.ly / http://j\&.mp are supported\&. .SH "OPTIONS" .PP \fB\-\-escaped\fR @@ -194,13 +44,31 @@ Print help text\&. .RS 4 Specify the URL to be converted\&. If no URL is given bti\-shrink\-urls waits for input on stdin\&. .RE +.SH "CONFIGURATION" +.PP +bti\-shrink\-urls is configured by setting some values in ~/\&.bti: +.PP +\fBshrink_host\fR +.RS 4 +Possible values: 2tu\&.us (default), bit\&.ly, j\&.mp +.RE +.PP +\fBshrink_bitly_login\fR +.RS 4 +API login for bit\&.ly, j\&.mp, required if shrink_host is set to bit\&.ly or j\&.mp\&. See https://code\&.google\&.com/p/bitly\-api/wiki/ApiDocumentation +.RE +.PP +\fBshrink_bitly_key\fR +.RS 4 +API key for bit\&.ly, j\&.mp, required if shrink_host is set to bit\&.ly or j\&.mp\&. See https://code\&.google\&.com/p/bitly\-api/wiki/ApiDocumentation +.RE .SH "AUTHOR" .PP Written by Bart Trojanowski -\FCbart@jukie\&.net\F[]\&. +bart@jukie\&.net\&. .SH "COPYRIGHT AND LICENSE" .PP Copyright (C) 2009 Bart Trojanowski -\FCbart@jukie\&.net\F[]\&. +bart@jukie\&.net\&. .PP This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License\&. diff --git a/bti.1 b/bti.1 index 778d58d..228cdf8 100644 --- a/bti.1 +++ b/bti.1 @@ -22,7 +22,7 @@ bti \- send a tweet to twitter\&.com or identi\&.ca from the command line .SH "SYNOPSIS" .HP \w'\fBbti\fR\ 'u -\fBbti\fR [\fB\-\-account\ account\fR] [\fB\-\-password\ password\fR] [\fB\-\-action\ action\fR] [\fB\-\-user\ screenname\fR] [\fB\-\-host\ HOST_NAME\fR] [\fB\-\-proxy\ PROXY:PORT\fR] [\fB\-\-logfile\ LOGFILE\fR] [\fB\-\-config\ CONFIGFILE\fR] [\fB\-\-replyto\ ID\fR] [\fB\-\-page\ PAGENUMBER\fR] [\fB\-\-bash\fR] [\fB\-\-shrink\-urls\fR] [\fB\-\-debug\fR] [\fB\-\-dry\-run\fR] [\fB\-\-verbose\fR] [\fB\-\-version\fR] [\fB\-\-help\fR] +\fBbti\fR [\fB\-\-account\ account\fR] [\fB\-\-password\ password\fR] [\fB\-\-action\ action\fR] [\fB\-\-user\ screenname\fR] [\fB\-\-host\ HOST_NAME\fR] [\fB\-\-proxy\ PROXY:PORT\fR] [\fB\-\-logfile\ LOGFILE\fR] [\fB\-\-config\ CONFIGFILE\fR] [\fB\-\-replyto\ ID\fR] [\fB\-\-retweet\ ID\fR] [\fB\-\-page\ PAGENUMBER\fR] [\fB\-\-bash\fR] [\fB\-\-shrink\-urls\fR] [\fB\-\-debug\fR] [\fB\-\-dry\-run\fR] [\fB\-\-verbose\fR] [\fB\-\-version\fR] [\fB\-\-help\fR] .SH "DESCRIPTION" .PP bti sends a tweet message to twitter\&.com or identi\&.ca\&. @@ -81,11 +81,18 @@ For twitter, this is ignored unless the message starts with the @name of the own For status\&.net, this can link any two messages into context with each other\&. Status\&.net will also link a message that contains an @name without this without regard to context\&. .RE .PP +\fB\-\-retweet ID\fR +.RS 4 +Status ID of a single post which you want to retweet\&. +.RE +.PP \fB\-\-shrink\-urls\fR .RS 4 Scans the tweet text for valid URL patterns and passes each through the supplied bti\-shrink\-urls script\&. The script will pass the URL to a web service that shrinks the URLs, making it more suitable for micro\-blogging\&. .sp -Currently, only http://2tu\&.us/ is used as a URL shrinking service\&. +The following URL shrinking services are available: http://2tu\&.us/ (default) and http://bit\&.ly / http://j\&.mp +.sp +See the documentation for bti\-shrink\-urls for the configuration options\&. .RE .PP \fB\-\-debug\fR diff --git a/bti.c b/bti.c index 26d7928..3938e0a 100644 --- a/bti.c +++ b/bti.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010 Greg Kroah-Hartman + * Copyright (C) 2008-2011 Greg Kroah-Hartman * Copyright (C) 2009 Bart Trojanowski * Copyright (C) 2009-2010 Amir Mohammad Saied * @@ -40,7 +40,7 @@ #include #include #include - +#include "bti.h" #define zalloc(size) calloc(size, 1) @@ -53,62 +53,6 @@ static int debug; -static int verbose; - -enum host { - HOST_TWITTER = 0, - HOST_IDENTICA = 1, - HOST_CUSTOM = 2 -}; - -enum action { - ACTION_UPDATE = 0, - ACTION_FRIENDS = 1, - ACTION_USER = 2, - ACTION_REPLIES = 4, - ACTION_PUBLIC = 8, - ACTION_GROUP = 16, - ACTION_RETWEET = 32, - ACTION_UNKNOWN = 64 -}; - -struct session { - char *password; - char *account; - char *consumer_key; - char *consumer_secret; - char *access_token_key; - char *access_token_secret; - char *tweet; - char *proxy; - char *time; - char *homedir; - char *logfile; - char *user; - char *group; - char *hosturl; - char *hostname; - char *configfile; - char *replyto; - char *retweet; - int bash; - int background; - int interactive; - int shrink_urls; - int dry_run; - int page; - int no_oauth; - enum host host; - enum action action; - void *readline_handle; - char *(*readline)(const char *); -}; - -struct bti_curl_buffer { - char *data; - enum action action; - int length; -}; static void display_help(void) { @@ -303,10 +247,10 @@ static void bti_curl_buffer_free(struct bti_curl_buffer *buffer) free(buffer); } -static const char twitter_host[] = "http://api.twitter.com/1/statuses"; -static const char identica_host[] = "https://identi.ca/api/statuses"; -static const char twitter_name[] = "twitter"; -static const char identica_name[] = "identi.ca"; +const char twitter_host[] = "http://api.twitter.com/1/statuses"; +const char identica_host[] = "https://identi.ca/api/statuses"; +const char twitter_name[] = "twitter"; +const char identica_name[] = "identi.ca"; static const char twitter_request_token_uri[] = "http://twitter.com/oauth/request_token"; static const char twitter_access_token_uri[] = "http://twitter.com/oauth/access_token"; @@ -339,7 +283,18 @@ static CURL *curl_init(void) return curl; } -static void parse_statuses(xmlDocPtr doc, xmlNodePtr current) +/* The final place data is sent to the screen/pty/tty */ +bti_output_line(struct session *session, xmlChar *user, xmlChar *id, + xmlChar *created, xmlChar *text) +{ + if (session->verbose) + printf("[%s] {%s} (%.16s) %s\n", user, id, created, text); + else + printf("[%s] %s\n", user, text); +} + +static void parse_statuses(struct session *session, + xmlDocPtr doc, xmlNodePtr current) { xmlChar *text = NULL; xmlChar *user = NULL; @@ -369,12 +324,7 @@ static void parse_statuses(xmlDocPtr doc, xmlNodePtr current) } if (user && text && created && id) { - if (verbose) - printf("[%s] {%s} (%.16s) %s\n", - user, id, created, text); - else - printf("[%s] %s\n", - user, text); + bti_output_line(session, user, id, created, text); xmlFree(user); xmlFree(text); xmlFree(created); @@ -391,7 +341,7 @@ static void parse_statuses(xmlDocPtr doc, xmlNodePtr current) return; } -static void parse_timeline(char *document) +static void parse_timeline(char *document, struct session *session) { xmlDocPtr doc; xmlNodePtr current; @@ -417,7 +367,7 @@ static void parse_timeline(char *document) current = current->xmlChildrenNode; while (current != NULL) { if ((!xmlStrcmp(current->name, (const xmlChar *)"status"))) - parse_statuses(doc, current); + parse_statuses(session, doc, current); current = current->next; } xmlFreeDoc(doc); @@ -446,7 +396,7 @@ static size_t curl_callback(void *buffer, size_t size, size_t nmemb, memcpy(&curl_buf->data[curl_buf->length], (char *)buffer, buffer_size); curl_buf->length += buffer_size; if (curl_buf->action) - parse_timeline(curl_buf->data); + parse_timeline(curl_buf->data, curl_buf->session); dbg("%s\n", curl_buf->data); @@ -592,10 +542,11 @@ static int send_request(struct session *session) if (!session->hosturl) session->hosturl = strdup(twitter_host); - if (session->no_oauth) { + if (session->no_oauth || session->guest) { curl_buf = bti_curl_buffer_alloc(session->action); if (!curl_buf) return -ENOMEM; + curl_buf->session = session; curl = curl_init(); if (!curl) @@ -775,200 +726,11 @@ static int send_request(struct session *session) if ((session->action != ACTION_UPDATE) && (session->action != ACTION_RETWEET)) - parse_timeline(reply); + parse_timeline(reply, session); } return 0; } -static void parse_configfile(struct session *session) -{ - FILE *config_file; - char *line = NULL; - size_t len = 0; - char *account = NULL; - char *password = NULL; - char *consumer_key = NULL; - char *consumer_secret = NULL; - char *access_token_key = NULL; - char *access_token_secret = NULL; - char *host = NULL; - char *proxy = NULL; - char *logfile = NULL; - char *action = NULL; - char *user = NULL; - char *replyto = NULL; - char *retweet = NULL; - int shrink_urls = 0; - - config_file = fopen(session->configfile, "r"); - - /* No error if file does not exist or is unreadable. */ - if (config_file == NULL) - return; - - do { - ssize_t n = getline(&line, &len, config_file); - if (n < 0) - break; - if (line[n - 1] == '\n') - line[n - 1] = '\0'; - /* Parse file. Format is the usual value pairs: - account=name - passwort=value - # is a comment character - */ - *strchrnul(line, '#') = '\0'; - char *c = line; - while (isspace(*c)) - c++; - /* Ignore blank lines. */ - if (c[0] == '\0') - continue; - - if (!strncasecmp(c, "account", 7) && (c[7] == '=')) { - c += 8; - if (c[0] != '\0') - account = strdup(c); - } else if (!strncasecmp(c, "password", 8) && - (c[8] == '=')) { - c += 9; - if (c[0] != '\0') - password = strdup(c); - } else if (!strncasecmp(c, "consumer_key", 12) && - (c[12] == '=')) { - c += 13; - if (c[0] != '\0') - consumer_key = strdup(c); - } else if (!strncasecmp(c, "consumer_secret", 15) && - (c[15] == '=')) { - c += 16; - if (c[0] != '\0') - consumer_secret = strdup(c); - } else if (!strncasecmp(c, "access_token_key", 16) && - (c[16] == '=')) { - c += 17; - if (c[0] != '\0') - access_token_key = strdup(c); - } else if (!strncasecmp(c, "access_token_secret", 19) && - (c[19] == '=')) { - c += 20; - if (c[0] != '\0') - access_token_secret = strdup(c); - } else if (!strncasecmp(c, "host", 4) && - (c[4] == '=')) { - c += 5; - if (c[0] != '\0') - host = strdup(c); - } else if (!strncasecmp(c, "proxy", 5) && - (c[5] == '=')) { - c += 6; - if (c[0] != '\0') - proxy = strdup(c); - } else if (!strncasecmp(c, "logfile", 7) && - (c[7] == '=')) { - c += 8; - if (c[0] != '\0') - logfile = strdup(c); - } else if (!strncasecmp(c, "replyto", 7) && - (c[7] == '=')) { - c += 8; - if (c[0] != '\0') - replyto = strdup(c); - } else if (!strncasecmp(c, "action", 6) && - (c[6] == '=')) { - c += 7; - if (c[0] != '\0') - action = strdup(c); - } else if (!strncasecmp(c, "user", 4) && - (c[4] == '=')) { - c += 5; - if (c[0] != '\0') - user = strdup(c); - } else if (!strncasecmp(c, "shrink-urls", 11) && - (c[11] == '=')) { - c += 12; - if (!strncasecmp(c, "true", 4) || - !strncasecmp(c, "yes", 3)) - shrink_urls = 1; - } else if (!strncasecmp(c, "verbose", 7) && - (c[7] == '=')) { - c += 8; - if (!strncasecmp(c, "true", 4) || - !strncasecmp(c, "yes", 3)) - verbose = 1; - } else if (!strncasecmp(c,"retweet", 7) && - (c[7] == '=')) { - c += 8; - if (c[0] != '\0') - retweet = strdup(c); - } - } while (!feof(config_file)); - - if (password) - session->password = password; - if (account) - session->account = account; - if (consumer_key) - session->consumer_key = consumer_key; - if (consumer_secret) - session->consumer_secret = consumer_secret; - if (access_token_key) - session->access_token_key = access_token_key; - if (access_token_secret) - session->access_token_secret = access_token_secret; - if (host) { - if (strcasecmp(host, "twitter") == 0) { - session->host = HOST_TWITTER; - session->hosturl = strdup(twitter_host); - session->hostname = strdup(twitter_name); - } else if (strcasecmp(host, "identica") == 0) { - session->host = HOST_IDENTICA; - session->hosturl = strdup(identica_host); - session->hostname = strdup(identica_name); - } else { - session->host = HOST_CUSTOM; - session->hosturl = strdup(host); - session->hostname = strdup(host); - } - free(host); - } - if (proxy) { - if (session->proxy) - free(session->proxy); - session->proxy = proxy; - } - if (logfile) - session->logfile = logfile; - if (replyto) - session->replyto = replyto; - if (retweet) - session->retweet = retweet; - if (action) { - if (strcasecmp(action, "update") == 0) - session->action = ACTION_UPDATE; - else if (strcasecmp(action, "friends") == 0) - session->action = ACTION_FRIENDS; - else if (strcasecmp(action, "user") == 0) - session->action = ACTION_USER; - else if (strcasecmp(action, "replies") == 0) - session->action = ACTION_REPLIES; - else if (strcasecmp(action, "public") == 0) - session->action = ACTION_PUBLIC; - else if (strcasecmp(action, "group") == 0) - session->action = ACTION_GROUP; - else - session->action = ACTION_UNKNOWN; - free(action); - } - if (user) - session->user = user; - session->shrink_urls = shrink_urls; - - /* Free buffer and close file. */ - free(line); - fclose(config_file); -} - static void log_session(struct session *session, int retval) { FILE *log_file; @@ -1366,7 +1128,6 @@ int main(int argc, char *argv[], char *envp[]) int page_nr; debug = 0; - verbose = 0; session = session_alloc(); if (!session) { @@ -1396,7 +1157,7 @@ int main(int argc, char *argv[], char *envp[]) dbg("http_proxy = %s\n", session->proxy); } - parse_configfile(session); + bti_parse_configfile(session); while (1) { option = getopt_long_only(argc, argv, @@ -1409,7 +1170,7 @@ int main(int argc, char *argv[], char *envp[]) debug = 1; break; case 'V': - verbose = 1; + session->verbose = 1; break; case 'a': if (session->account) @@ -1520,7 +1281,7 @@ int main(int argc, char *argv[], char *envp[]) * previously set options from the command line, but * the user asked for it... */ - parse_configfile(session); + bti_parse_configfile(session); break; case 'h': display_help(); @@ -1547,11 +1308,17 @@ int main(int argc, char *argv[], char *envp[]) if (session->host == HOST_TWITTER) { if (!session->consumer_key || !session->consumer_secret) { - fprintf(stderr, - "Twitter no longer supports HTTP basic authentication.\n" - "Both consumer key, and consumer secret are required" - " for bti in order to behave as an OAuth consumer.\n"); - goto exit; + if (session->action == ACTION_USER || + session->action == ACTION_PUBLIC) { + /* Some actions may still work without authentication */ + session->guest = 1; + } else { + fprintf(stderr, + "Twitter no longer supports HTTP basic authentication.\n" + "Both consumer key, and consumer secret are required" + " for bti in order to behave as an OAuth consumer.\n"); + goto exit; + } } if (session->action == ACTION_GROUP) { fprintf(stderr, "Groups only work in Identi.ca.\n"); @@ -1573,7 +1340,7 @@ int main(int argc, char *argv[], char *envp[]) session->hostname); session->password = strdup(password); } - } else { + } else if (!session->guest) { if (!session->access_token_key || !session->access_token_secret) { request_access_token(session); diff --git a/bti.h b/bti.h new file mode 100644 index 0000000..cc93963 --- /dev/null +++ b/bti.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2008-2011 Greg Kroah-Hartman + * Copyright (C) 2009 Bart Trojanowski + * Copyright (C) 2009-2010 Amir Mohammad Saied + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BTI_H +#define __BTI_H + +enum host { + HOST_TWITTER = 0, + HOST_IDENTICA = 1, + HOST_CUSTOM = 2 +}; + +enum action { + ACTION_UPDATE = 0, + ACTION_FRIENDS = 1, + ACTION_USER = 2, + ACTION_REPLIES = 4, + ACTION_PUBLIC = 8, + ACTION_GROUP = 16, + ACTION_RETWEET = 32, + ACTION_UNKNOWN = 64 +}; + +struct session { + char *password; + char *account; + char *consumer_key; + char *consumer_secret; + char *access_token_key; + char *access_token_secret; + char *tweet; + char *proxy; + char *time; + char *homedir; + char *logfile; + char *user; + char *group; + char *hosturl; + char *hostname; + char *configfile; + char *replyto; + char *retweet; + int bash; + int background; + int interactive; + int shrink_urls; + int dry_run; + int page; + int no_oauth; + int guest; + int verbose; + enum host host; + enum action action; + void *readline_handle; + char *(*readline)(const char *); +}; + +struct bti_curl_buffer { + char *data; + struct session *session; + enum action action; + int length; +}; + +extern const char twitter_host[]; +extern const char identica_host[]; +extern const char twitter_name[]; +extern const char identica_name[]; + +extern void bti_parse_configfile(struct session *session); + +#endif diff --git a/config.c b/config.c new file mode 100644 index 0000000..829711a --- /dev/null +++ b/config.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2008-2011 Greg Kroah-Hartman + * Copyright (C) 2009 Bart Trojanowski + * Copyright (C) 2009-2010 Amir Mohammad Saied + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bti.h" + +void bti_parse_configfile(struct session *session) +{ + FILE *config_file; + char *line = NULL; + size_t len = 0; + char *account = NULL; + char *password = NULL; + char *consumer_key = NULL; + char *consumer_secret = NULL; + char *access_token_key = NULL; + char *access_token_secret = NULL; + char *host = NULL; + char *proxy = NULL; + char *logfile = NULL; + char *action = NULL; + char *user = NULL; + char *replyto = NULL; + char *retweet = NULL; + int shrink_urls = 0; + + config_file = fopen(session->configfile, "r"); + + /* No error if file does not exist or is unreadable. */ + if (config_file == NULL) + return; + + do { + ssize_t n = getline(&line, &len, config_file); + if (n < 0) + break; + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + /* Parse file. Format is the usual value pairs: + account=name + passwort=value + # is a comment character + */ + *strchrnul(line, '#') = '\0'; + char *c = line; + while (isspace(*c)) + c++; + /* Ignore blank lines. */ + if (c[0] == '\0') + continue; + + if (!strncasecmp(c, "account", 7) && (c[7] == '=')) { + c += 8; + if (c[0] != '\0') + account = strdup(c); + } else if (!strncasecmp(c, "password", 8) && + (c[8] == '=')) { + c += 9; + if (c[0] != '\0') + password = strdup(c); + } else if (!strncasecmp(c, "consumer_key", 12) && + (c[12] == '=')) { + c += 13; + if (c[0] != '\0') + consumer_key = strdup(c); + } else if (!strncasecmp(c, "consumer_secret", 15) && + (c[15] == '=')) { + c += 16; + if (c[0] != '\0') + consumer_secret = strdup(c); + } else if (!strncasecmp(c, "access_token_key", 16) && + (c[16] == '=')) { + c += 17; + if (c[0] != '\0') + access_token_key = strdup(c); + } else if (!strncasecmp(c, "access_token_secret", 19) && + (c[19] == '=')) { + c += 20; + if (c[0] != '\0') + access_token_secret = strdup(c); + } else if (!strncasecmp(c, "host", 4) && + (c[4] == '=')) { + c += 5; + if (c[0] != '\0') + host = strdup(c); + } else if (!strncasecmp(c, "proxy", 5) && + (c[5] == '=')) { + c += 6; + if (c[0] != '\0') + proxy = strdup(c); + } else if (!strncasecmp(c, "logfile", 7) && + (c[7] == '=')) { + c += 8; + if (c[0] != '\0') + logfile = strdup(c); + } else if (!strncasecmp(c, "replyto", 7) && + (c[7] == '=')) { + c += 8; + if (c[0] != '\0') + replyto = strdup(c); + } else if (!strncasecmp(c, "action", 6) && + (c[6] == '=')) { + c += 7; + if (c[0] != '\0') + action = strdup(c); + } else if (!strncasecmp(c, "user", 4) && + (c[4] == '=')) { + c += 5; + if (c[0] != '\0') + user = strdup(c); + } else if (!strncasecmp(c, "shrink-urls", 11) && + (c[11] == '=')) { + c += 12; + if (!strncasecmp(c, "true", 4) || + !strncasecmp(c, "yes", 3)) + shrink_urls = 1; + } else if (!strncasecmp(c, "verbose", 7) && + (c[7] == '=')) { + c += 8; + if (!strncasecmp(c, "true", 4) || + !strncasecmp(c, "yes", 3)) + session->verbose = 1; + } else if (!strncasecmp(c,"retweet", 7) && + (c[7] == '=')) { + c += 8; + if (c[0] != '\0') + retweet = strdup(c); + } + } while (!feof(config_file)); + + if (password) + session->password = password; + if (account) + session->account = account; + if (consumer_key) + session->consumer_key = consumer_key; + if (consumer_secret) + session->consumer_secret = consumer_secret; + if (access_token_key) + session->access_token_key = access_token_key; + if (access_token_secret) + session->access_token_secret = access_token_secret; + if (host) { + if (strcasecmp(host, "twitter") == 0) { + session->host = HOST_TWITTER; + session->hosturl = strdup(twitter_host); + session->hostname = strdup(twitter_name); + } else if (strcasecmp(host, "identica") == 0) { + session->host = HOST_IDENTICA; + session->hosturl = strdup(identica_host); + session->hostname = strdup(identica_name); + } else { + session->host = HOST_CUSTOM; + session->hosturl = strdup(host); + session->hostname = strdup(host); + } + free(host); + } + if (proxy) { + if (session->proxy) + free(session->proxy); + session->proxy = proxy; + } + if (logfile) + session->logfile = logfile; + if (replyto) + session->replyto = replyto; + if (retweet) + session->retweet = retweet; + if (action) { + if (strcasecmp(action, "update") == 0) + session->action = ACTION_UPDATE; + else if (strcasecmp(action, "friends") == 0) + session->action = ACTION_FRIENDS; + else if (strcasecmp(action, "user") == 0) + session->action = ACTION_USER; + else if (strcasecmp(action, "replies") == 0) + session->action = ACTION_REPLIES; + else if (strcasecmp(action, "public") == 0) + session->action = ACTION_PUBLIC; + else if (strcasecmp(action, "group") == 0) + session->action = ACTION_GROUP; + else + session->action = ACTION_UNKNOWN; + free(action); + } + if (user) + session->user = user; + session->shrink_urls = shrink_urls; + + /* Free buffer and close file. */ + free(line); + fclose(config_file); +} + + diff --git a/configure.ac b/configure.ac index 4b5fd3a..581c63a 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ -AC_INIT([bti], [028], [greg@kroah.com]) +AC_INIT([bti], [029], [greg@kroah.com]) AC_PREREQ(2.60) -AM_INIT_AUTOMAKE(bti, 028) +AM_INIT_AUTOMAKE(bti, 029) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])