X-Git-Url: https://git.toastfreeware.priv.at/gregoa/bti.git/blobdiff_plain/ae9c3fe1363f742e4288f75cb96daa3909b8e609..35f9afd2df1cc9b633ec9eee01a3292dc7eb5ccd:/bti.c diff --git a/bti.c b/bti.c index 753870c..fe4c6e2 100644 --- a/bti.c +++ b/bti.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2008 Greg Kroah-Hartman * Copyright (C) 2009 Bart Trojanowski + * Copyright (C) 2009 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 @@ -16,6 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#define _GNU_SOURCE + #include #include #include @@ -35,7 +38,6 @@ #include #include #include -#include "bti_version.h" #define zalloc(size) calloc(size, 1) @@ -43,23 +45,26 @@ #define dbg(format, arg...) \ do { \ if (debug) \ - printf("%s: " format , __func__ , ## arg); \ + fprintf(stdout, "bti: %s: " format , __func__ , \ + ## arg); \ } while (0) static int debug; +static int verbose; enum host { - HOST_TWITTER = 0, + HOST_TWITTER = 0, HOST_IDENTICA = 1, + HOST_CUSTOM = 2 }; enum action { - ACTION_UPDATE = 0, + ACTION_UPDATE = 0, ACTION_FRIENDS = 1, - ACTION_USER = 2, + ACTION_USER = 2, ACTION_REPLIES = 4, - ACTION_PUBLIC = 8, + ACTION_PUBLIC = 8, ACTION_UNKNOWN = 16 }; @@ -72,9 +77,11 @@ struct session { char *homedir; char *logfile; char *user; + char *hosturl; int bash; int shrink_urls; int dry_run; + int page; enum host host; enum action action; }; @@ -88,28 +95,32 @@ struct bti_curl_buffer { static void display_help(void) { fprintf(stdout, "bti - send tweet to twitter or identi.ca\n"); - fprintf(stdout, "Version: " BTI_VERSION "\n"); + fprintf(stdout, "Version: " VERSION "\n"); fprintf(stdout, "Usage:\n"); fprintf(stdout, " bti [options]\n"); fprintf(stdout, "options are:\n"); fprintf(stdout, " --account accountname\n"); fprintf(stdout, " --password password\n"); fprintf(stdout, " --action action\n"); - fprintf(stdout, " ('update', 'friends', 'public', 'replies' or 'user')\n"); + fprintf(stdout, " ('update', 'friends', 'public', 'replies' " + "or 'user')\n"); fprintf(stdout, " --user screenname\n"); fprintf(stdout, " --proxy PROXY:PORT\n"); fprintf(stdout, " --host HOST\n"); fprintf(stdout, " --logfile logfile\n"); fprintf(stdout, " --shrink-urls\n"); + fprintf(stdout, " --page PAGENUMBER\n"); fprintf(stdout, " --bash\n"); fprintf(stdout, " --debug\n"); + fprintf(stdout, " --verbose\n"); + fprintf(stdout, " --dry-run\n"); fprintf(stdout, " --version\n"); fprintf(stdout, " --help\n"); } static void display_version(void) { - fprintf(stdout, "bti - version %s\n", BTI_VERSION); + fprintf(stdout, "bti - version %s\n", VERSION); } static struct session *session_alloc(void) @@ -133,6 +144,7 @@ static void session_free(struct session *session) free(session->time); free(session->homedir); free(session->user); + free(session->hosturl); free(session); } @@ -164,17 +176,14 @@ static void bti_curl_buffer_free(struct bti_curl_buffer *buffer) free(buffer); } -static const char *twitter_user_url = "http://twitter.com/statuses/user_timeline/"; -static const char *twitter_update_url = "https://twitter.com/statuses/update.xml"; -static const char *twitter_public_url = "http://twitter.com/statuses/public_timeline.xml"; -static const char *twitter_friends_url = "https://twitter.com/statuses/friends_timeline.xml"; -static const char *twitter_replies_url = "http://twitter.com/statuses/replies.xml"; +static const char *twitter_host = "https://twitter.com/statuses"; +static const char *identica_host = "https://identi.ca/api/statuses"; -static const char *identica_user_url = "http://identi.ca/api/statuses/user_timeline/"; -static const char *identica_update_url = "http://identi.ca/api/statuses/update.xml"; -static const char *identica_public_url = "http://identi.ca/api/statuses/public_timeline.xml"; -static const char *identica_friends_url = "http://identi.ca/api/statuses/friends_timeline.xml"; -static const char *identica_replies_url = "http://identi.ca/api/statuses/replies.xml"; +static const char *user_uri = "/user_timeline/"; +static const char *update_uri = "/update.xml"; +static const char *public_uri = "/public_timeline.xml"; +static const char *friends_uri = "/friends_timeline.xml"; +static const char *replies_uri = "/replies.xml"; static CURL *curl_init(void) { @@ -191,15 +200,18 @@ static CURL *curl_init(void) return curl; } -void parse_statuses(xmlDocPtr doc, xmlNodePtr current) +static void parse_statuses(xmlDocPtr doc, xmlNodePtr current) { xmlChar *text = NULL; xmlChar *user = NULL; + xmlChar *created = NULL; xmlNodePtr userinfo; current = current->xmlChildrenNode; while (current != NULL) { if (current->type == XML_ELEMENT_NODE) { + if (!xmlStrcmp(current->name, (const xmlChar *)"created_at")) + created = xmlNodeListGetString(doc, current->xmlChildrenNode, 1); if (!xmlStrcmp(current->name, (const xmlChar *)"text")) text = xmlNodeListGetString(doc, current->xmlChildrenNode, 1); if (!xmlStrcmp(current->name, (const xmlChar *)"user")) { @@ -213,12 +225,20 @@ void parse_statuses(xmlDocPtr doc, xmlNodePtr current) userinfo = userinfo->next; } } - if (user && text) { - printf("[%s] %s\n", user, text); + + if (user && text && created) { + if (verbose) + printf("[%s] (%.16s) %s\n", + user, created, text); + else + printf("[%s] %s\n", + user, text); xmlFree(user); xmlFree(text); + xmlFree(created); user = NULL; text = NULL; + created = NULL; } } current = current->next; @@ -231,8 +251,9 @@ static void parse_timeline(char *document) { xmlDocPtr doc; xmlNodePtr current; - doc = xmlReadMemory(document, strlen(document), "timeline.xml", NULL, XML_PARSE_NOERROR); + doc = xmlReadMemory(document, strlen(document), "timeline.xml", + NULL, XML_PARSE_NOERROR); if (doc == NULL) return; @@ -260,7 +281,8 @@ static void parse_timeline(char *document) return; } -size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *userp) +static size_t curl_callback(void *buffer, size_t size, size_t nmemb, + void *userp) { struct bti_curl_buffer *curl_buf = userp; size_t buffer_size = size * nmemb; @@ -289,10 +311,9 @@ size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *userp) static int send_request(struct session *session) { + char endpoint[100]; char user_password[500]; char data[500]; - /* is there usernames longer than 22 chars? */ - char user_url[70]; struct bti_curl_buffer *curl_buf; CURL *curl = NULL; CURLcode res; @@ -329,67 +350,38 @@ static int send_request(struct session *session) curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); slist = curl_slist_append(slist, "Expect:"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); - switch (session->host) { - case HOST_TWITTER: - curl_easy_setopt(curl, CURLOPT_URL, twitter_update_url); - break; - case HOST_IDENTICA: - curl_easy_setopt(curl, CURLOPT_URL, identica_update_url); - break; - } + + sprintf(endpoint, "%s%s", session->hosturl, update_uri); + curl_easy_setopt(curl, CURLOPT_URL, endpoint); curl_easy_setopt(curl, CURLOPT_USERPWD, user_password); break; case ACTION_FRIENDS: snprintf(user_password, sizeof(user_password), "%s:%s", session->account, session->password); - switch (session->host) { - case HOST_TWITTER: - curl_easy_setopt(curl, CURLOPT_URL, twitter_friends_url); - break; - case HOST_IDENTICA: - curl_easy_setopt(curl, CURLOPT_URL, identica_friends_url); - break; - } + sprintf(endpoint, "%s%s?page=%d", session->hosturl, + friends_uri, session->page); + curl_easy_setopt(curl, CURLOPT_URL, endpoint); curl_easy_setopt(curl, CURLOPT_USERPWD, user_password); break; case ACTION_USER: - switch (session->host) { - case HOST_TWITTER: - sprintf(user_url, "%s%s.xml", twitter_user_url, session->user); - curl_easy_setopt(curl, CURLOPT_URL, user_url); - break; - case HOST_IDENTICA: - sprintf(user_url, "%s%s.xml", identica_user_url, session->user); - curl_easy_setopt(curl, CURLOPT_URL, user_url); - break; - } + sprintf(endpoint, "%s%s%s.xml?page=%d", session->hosturl, user_uri, + session->user, session->page); + curl_easy_setopt(curl, CURLOPT_URL, endpoint); break; case ACTION_REPLIES: snprintf(user_password, sizeof(user_password), "%s:%s", session->account, session->password); - switch (session->host) { - case HOST_TWITTER: - curl_easy_setopt(curl, CURLOPT_URL, twitter_replies_url); - break; - case HOST_IDENTICA: - curl_easy_setopt(curl, CURLOPT_URL, identica_replies_url); - break; - } + sprintf(endpoint, "%s%s?page=%d", session->hosturl, replies_uri, session->page); + curl_easy_setopt(curl, CURLOPT_URL, endpoint); curl_easy_setopt(curl, CURLOPT_USERPWD, user_password); break; case ACTION_PUBLIC: - switch (session->host) { - case HOST_TWITTER: - curl_easy_setopt(curl, CURLOPT_URL, twitter_public_url); - break; - case HOST_IDENTICA: - curl_easy_setopt(curl, CURLOPT_URL, identica_public_url); - break; - } + sprintf(endpoint, "%s%s?page=%d", session->hosturl, public_uri, session->page); + curl_easy_setopt(curl, CURLOPT_URL, endpoint); break; default: @@ -411,7 +403,8 @@ static int send_request(struct session *session) if (!session->dry_run) { res = curl_easy_perform(curl); if (res && !session->bash) { - fprintf(stderr, "error(%d) trying to perform operation\n", res); + fprintf(stderr, "error(%d) trying to perform " + "operation\n", res); return -EINVAL; } } @@ -509,6 +502,13 @@ static void parse_configfile(struct session *session) !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; + } } while (!feof(config_file)); if (password) @@ -516,10 +516,16 @@ static void parse_configfile(struct session *session) if (account) session->account = account; if (host) { - if (strcasecmp(host, "twitter") == 0) + if (strcasecmp(host, "twitter") == 0) { session->host = HOST_TWITTER; - if (strcasecmp(host, "identica") == 0) + session->hosturl = strdup(twitter_host); + } else if (strcasecmp(host, "identica") == 0) { session->host = HOST_IDENTICA; + session->hosturl = strdup(identica_host); + } else { + session->host = HOST_CUSTOM; + session->hosturl = strdup(host); + } free(host); } if (proxy) { @@ -544,9 +550,8 @@ static void parse_configfile(struct session *session) session->action = ACTION_UNKNOWN; free(action); } - if (user) { + if (user) session->user = user; - } session->shrink_urls = shrink_urls; /* Free buffer and close file. */ @@ -580,7 +585,7 @@ static void log_session(struct session *session, int retval) host = "identi.ca"; break; default: - host = "unknown"; + host = session->hosturl; break; } @@ -634,7 +639,10 @@ static char *get_string_from_stdin(void) static int find_urls(const char *tweet, int **pranges) { - // magic obtained from http://www.geekpedia.com/KB65_How-to-validate-an-URL-using-RegEx-in-Csharp.html + /* + * magic obtained from + * http://www.geekpedia.com/KB65_How-to-validate-an-URL-using-RegEx-in-Csharp.html + */ static const char *re_magic = "(([a-zA-Z][0-9a-zA-Z+\\-\\.]*:)/{1,3}" "[0-9a-zA-Z;/~?:@&=+$\\.\\-_'()%]+)" @@ -655,23 +663,24 @@ static int find_urls(const char *tweet, int **pranges) &errptr, &erroffset, NULL); if (!re) { fprintf(stderr, "pcre_compile @%u: %s\n", erroffset, errptr); - exit (1); + exit(1); } tweetlen = strlen(tweet); - for (startoffset=0; startoffset 0) { // parent + if (pid > 0) { + /* parent */ close(in[0]); close(out[1]); close(err[1]); @@ -731,7 +741,8 @@ static int popenRWE(int *rwepipe, const char *exe, const char *const argv[]) rwepipe[1] = out[0]; rwepipe[2] = err[0]; return pid; - } else if (pid == 0) { // child + } else if (pid == 0) { + /* child */ close(in[1]); close(out[0]); close(err[0]); @@ -742,7 +753,7 @@ static int popenRWE(int *rwepipe, const char *exe, const char *const argv[]) close(2); rc = dup(err[1]); - execvp(exe, (char**)argv); + execvp(exe, (char **)argv); exit(1); } else goto error_fork; @@ -799,7 +810,7 @@ static char *shrink_one_url(int *rwepipe, char *big) while (smalllen && isspace(small[smalllen-1])) small[--smalllen] = 0; - free (big); + free(big); return small; error_free_small: @@ -832,7 +843,7 @@ static char *shrink_urls(char *text) if (!rcount) return text; - for (i=0; i= long_url_len) { - // the short url ended up being too long or unavailable + /* The short url ended up being too long + * or unavailable */ if (inofs) { strncpy(text + outofs, text + inofs, not_url_len + long_url_len); @@ -855,21 +867,21 @@ static char *shrink_urls(char *text) outofs += not_url_len + long_url_len; } else { - // copy the unmodified block + /* copy the unmodified block */ strncpy(text + outofs, text + inofs, not_url_len); inofs += not_url_len; outofs += not_url_len; - // copy the new url + /* copy the new url */ strncpy(text + outofs, url, short_url_len); inofs += long_url_len; outofs += short_url_len; } - free (url); + free(url); } - // copy the last block after the last match + /* copy the last block after the last match */ if (inofs) { int tail = inlen - inofs; if (tail) { @@ -891,6 +903,7 @@ int main(int argc, char *argv[], char *envp[]) { static const struct option options[] = { { "debug", 0, NULL, 'd' }, + { "verbose", 0, NULL, 'V' }, { "account", 1, NULL, 'a' }, { "password", 1, NULL, 'p' }, { "host", 1, NULL, 'H' }, @@ -902,6 +915,7 @@ int main(int argc, char *argv[], char *envp[]) { "help", 0, NULL, 'h' }, { "bash", 0, NULL, 'b' }, { "dry-run", 0, NULL, 'n' }, + { "page", 1, NULL, 'g' }, { "version", 0, NULL, 'v' }, { } }; @@ -912,8 +926,10 @@ int main(int argc, char *argv[], char *envp[]) int option; char *http_proxy; time_t t; + int page_nr; debug = 0; + verbose = 0; rl_bind_key('\t', rl_insert); session = session_alloc(); @@ -944,7 +960,7 @@ int main(int argc, char *argv[], char *envp[]) parse_configfile(session); while (1) { - option = getopt_long_only(argc, argv, "dqe:p:P:H:a:A:u:h", + option = getopt_long_only(argc, argv, "dp:P:H:a:A:u:hg:snVv", options, NULL); if (option == -1) break; @@ -952,12 +968,20 @@ int main(int argc, char *argv[], char *envp[]) case 'd': debug = 1; break; + case 'V': + verbose = 1; + break; case 'a': if (session->account) free(session->account); session->account = strdup(optarg); dbg("account = %s\n", session->account); break; + case 'g': + page_nr = atoi(optarg); + dbg("page = %d\n", page_nr); + session->page = page_nr; + break; case 'p': if (session->password) free(session->password); @@ -1001,10 +1025,18 @@ int main(int argc, char *argv[], char *envp[]) session->shrink_urls = 1; break; case 'H': - if (strcasecmp(optarg, "twitter") == 0) + if (session->hosturl) + free(session->hosturl); + if (strcasecmp(optarg, "twitter") == 0) { session->host = HOST_TWITTER; - if (strcasecmp(optarg, "identica") == 0) + session->hosturl = strdup(twitter_host); + } else if (strcasecmp(optarg, "identica") == 0) { session->host = HOST_IDENTICA; + session->hosturl = strdup(identica_host); + } else { + session->host = HOST_CUSTOM; + session->hosturl = strdup(optarg); + } dbg("host = %d\n", session->host); break; case 'b': @@ -1025,9 +1057,17 @@ int main(int argc, char *argv[], char *envp[]) } } + /* + * Show the version to make it easier to determine what + * is going on here + */ + if (debug) + display_version(); + if (session->action == ACTION_UNKNOWN) { fprintf(stderr, "Unknown action, valid actions are:\n"); - fprintf(stderr, "'update', 'friends', 'public', 'replies' or 'user'.\n"); + fprintf(stderr, "'update', 'friends', 'public', " + "'replies' or 'user'.\n"); goto exit; } @@ -1056,7 +1096,7 @@ int main(int argc, char *argv[], char *envp[]) session->tweet = zalloc(strlen(tweet) + 10); if (session->bash) - sprintf(session->tweet, "$ %s", tweet); + sprintf(session->tweet, "%c %s", getuid() ? '$' : '#', tweet); else sprintf(session->tweet, "%s", tweet); @@ -1067,6 +1107,8 @@ int main(int argc, char *argv[], char *envp[]) if (!session->user) session->user = strdup(session->account); + if (session->page == 0) + session->page = 1; dbg("account = %s\n", session->account); dbg("password = %s\n", session->password); dbg("host = %d\n", session->host);