]> ToastFreeware Gitweb - gregoa/bti.git/blobdiff - bti.c
Add support for api page numbers in read mode
[gregoa/bti.git] / bti.c
diff --git a/bti.c b/bti.c
index 2a94236881e612da136ec5f320d9ce59b63d2c94..68e1539035c39240e4bf2e09e27ee0cfddebde5c 100644 (file)
--- a/bti.c
+++ b/bti.c
@@ -51,9 +51,12 @@ enum host {
 };
 
 enum action {
-     ACTION_UPDATE = 0,
-     ACTION_PUBLIC = 1,
-     ACTION_FRIENDS = 2,
+       ACTION_UPDATE = 0,
+       ACTION_FRIENDS = 1,
+       ACTION_USER = 2,
+       ACTION_REPLIES = 4,
+       ACTION_PUBLIC = 8,
+       ACTION_UNKNOWN = 16
 };
 
 struct session {
@@ -64,7 +67,9 @@ struct session {
        char *time;
        char *homedir;
        char *logfile;
+       char *user;
        int bash;
+       int page;
        enum host host;
        enum action action;
 };
@@ -77,18 +82,20 @@ struct bti_curl_buffer {
 
 static void display_help(void)
 {
-       fprintf(stdout, "bti - send tweet to twitter\n");
+       fprintf(stdout, "bti - send tweet to twitter or identi.ca\n");
        fprintf(stdout, "Version: " BTI_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, "  --user screenname\n");
        fprintf(stdout, "  --proxy PROXY:PORT\n");
        fprintf(stdout, "  --host HOST\n");
        fprintf(stdout, "  --logfile logfile\n");
        fprintf(stdout, "  --bash\n");
-       fprintf(stdout, "  --action action\n");
        fprintf(stdout, "  --debug\n");
        fprintf(stdout, "  --version\n");
        fprintf(stdout, "  --help\n");
@@ -119,6 +126,7 @@ static void session_free(struct session *session)
        free(session->proxy);
        free(session->time);
        free(session->homedir);
+       free(session->user);
        free(session);
 }
 
@@ -150,13 +158,17 @@ 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 *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 CURL *curl_init(void)
 {
@@ -175,31 +187,34 @@ static CURL *curl_init(void)
 
 void parse_statuses(xmlDocPtr doc, xmlNodePtr current)
 {
-       xmlChar *text;
-       xmlChar *user;
+       xmlChar *text = NULL;
+       xmlChar *user = NULL;
        xmlNodePtr userinfo;
+
        current = current->xmlChildrenNode;
        while (current != NULL) {
                if (current->type == XML_ELEMENT_NODE) {
-                       if (!xmlStrcmp(current->name, (const xmlChar *)"text")) {
+                       if (!xmlStrcmp(current->name, (const xmlChar *)"text"))
                                text = xmlNodeListGetString(doc, current->xmlChildrenNode, 1);
-                               printf("%s", text);
-                               xmlFree(text);
-                       }
                        if (!xmlStrcmp(current->name, (const xmlChar *)"user")) {
                                userinfo = current->xmlChildrenNode;
                                while (userinfo != NULL) {
                                        if ((!xmlStrcmp(userinfo->name, (const xmlChar *)"screen_name"))) {
+                                               if (user)
+                                                       xmlFree(user);
                                                user = xmlNodeListGetString(doc, userinfo->xmlChildrenNode, 1);
-                                               printf(" [%s]\n", user);
-                                               xmlFree(user);
                                        }
-
                                        userinfo = userinfo->next;
                                }
                        }
+                       if (user && text) {
+                               printf("[%s] %s\n", user, text);
+                               xmlFree(user);
+                               xmlFree(text);
+                               user = NULL;
+                               text = NULL;
+                       }
                }
-
                current = current->next;
        }
 
@@ -258,8 +273,7 @@ size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *userp)
        curl_buf->data = temp;
        memcpy(&curl_buf->data[curl_buf->length], (char *)buffer, buffer_size);
        curl_buf->length += buffer_size;
-       if ((curl_buf->action == ACTION_FRIENDS) ||
-               (curl_buf->action == ACTION_PUBLIC))
+       if (curl_buf->action)
                parse_timeline(curl_buf->data);
 
        dbg("%s\n", curl_buf->data);
@@ -271,6 +285,8 @@ static int send_request(struct session *session)
 {
        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;
@@ -323,10 +339,41 @@ static int send_request(struct session *session)
                         session->account, session->password);
                switch (session->host) {
                case HOST_TWITTER:
-                       curl_easy_setopt(curl, CURLOPT_URL, twitter_friends_url);
+                       sprintf(user_url, "%s?page=%d", twitter_friends_url, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, user_url);
+                       break;
+               case HOST_IDENTICA:
+                       sprintf(user_url, "%s?page=%d", identica_friends_url, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, user_url);
+                       break;
+               }
+               curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
+
+               break;
+       case ACTION_USER:
+               switch (session->host) {
+               case HOST_TWITTER:
+                       sprintf(user_url, "%s%s.xml?page=%d", twitter_user_url, session->user, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, user_url);
+                       break;
+               case HOST_IDENTICA:
+                       sprintf(user_url, "%s%s.xml?page=%d", identica_user_url, session->user, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, user_url);
+                       break;
+               }
+
+               break;
+       case ACTION_REPLIES:
+               snprintf(user_password, sizeof(user_password), "%s:%s",
+                        session->account, session->password);
+               switch (session->host) {
+               case HOST_TWITTER:
+                       sprintf(user_url, "%s?page=%d", twitter_replies_url, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, user_url);
                        break;
                case HOST_IDENTICA:
-                       curl_easy_setopt(curl, CURLOPT_URL, identica_friends_url);
+                       sprintf(user_url, "%s?page=%d", identica_replies_url, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, user_url);
                        break;
                }
                curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
@@ -335,13 +382,17 @@ static int send_request(struct session *session)
        case ACTION_PUBLIC:
                switch (session->host) {
                case HOST_TWITTER:
-                       curl_easy_setopt(curl, CURLOPT_URL, twitter_public_url);
+                       sprintf(user_url, "%s?page=%d", twitter_public_url, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, user_url);
                        break;
                case HOST_IDENTICA:
-                       curl_easy_setopt(curl, CURLOPT_URL, identica_public_url);
+                       sprintf(user_url, "%s?page=%d", identica_public_url, session->page);
+                       curl_easy_setopt(curl, CURLOPT_URL, user_url);
                        break;
                }
 
+               break;
+       default:
                break;
        }
 
@@ -381,6 +432,7 @@ static void parse_configfile(struct session *session)
        char *proxy = NULL;
        char *logfile = NULL;
        char *action = NULL;
+       char *user = NULL;
        char *file;
 
        /* config file is ~/.bti  */
@@ -442,6 +494,11 @@ static void parse_configfile(struct session *session)
                        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);
                }
        } while (!feof(config_file));
 
@@ -466,12 +523,21 @@ static void parse_configfile(struct session *session)
        if (action) {
                if (strcasecmp(action, "update") == 0)
                        session->action = ACTION_UPDATE;
-               if (strcasecmp(action, "friends") == 0)
+               else if (strcasecmp(action, "friends") == 0)
                        session->action = ACTION_FRIENDS;
-               if (strcasecmp(action, "public") == 0)
+               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
+                       session->action = ACTION_UNKNOWN;
                free(action);
        }
+       if (user) {
+               session->user = user;
+       }
 
        /* Free buffer and close file.  */
        free(line);
@@ -508,24 +574,54 @@ static void log_session(struct session *session, int retval)
                break;
        }
 
-       if (session->action == ACTION_UPDATE) {
+       switch (session->action) {
+       case ACTION_UPDATE:
                if (retval)
                        fprintf(log_file, "%s: host=%s tweet failed\n",
                                session->time, host);
                else
                        fprintf(log_file, "%s: host=%s tweet=%s\n",
                                session->time, host, session->tweet);
-       } else if (session->action == ACTION_FRIENDS) {
+               break;
+       case ACTION_FRIENDS:
                fprintf(log_file, "%s: host=%s retrieving friends timeline\n",
                        session->time, host);
-       } else if (session->action == ACTION_PUBLIC) {
+               break;
+       case ACTION_USER:
+               fprintf(log_file, "%s: host=%s retrieving %s's timeline\n",
+                       session->time, host, session->user);
+               break;
+       case ACTION_REPLIES:
+               fprintf(log_file, "%s: host=%s retrieving replies\n",
+                       session->time, host);
+               break;
+       case ACTION_PUBLIC:
                fprintf(log_file, "%s: host=%s retrieving public timeline\n",
                        session->time, host);
+               break;
+       default:
+               break;
        }
 
        fclose(log_file);
 }
 
+static char *get_string_from_stdin(void)
+{
+       char *temp;
+       char *string;
+
+       string = zalloc(1000);
+       if (!string)
+               return NULL;
+
+       if (!fgets(string, 999, stdin))
+               return NULL;
+       temp = strchr(string, '\n');
+       *temp = '\0';
+       return string;
+}
+
 int main(int argc, char *argv[], char *envp[])
 {
        static const struct option options[] = {
@@ -535,9 +631,11 @@ int main(int argc, char *argv[], char *envp[])
                { "host", 1, NULL, 'H' },
                { "proxy", 1, NULL, 'P' },
                { "action", 1, NULL, 'A' },
+               { "user", 1, NULL, 'u' },
                { "logfile", 1, NULL, 'L' },
                { "help", 0, NULL, 'h' },
                { "bash", 0, NULL, 'b' },
+               { "page", 1, NULL, 'g' },
                { "version", 0, NULL, 'v' },
                { }
        };
@@ -548,6 +646,7 @@ int main(int argc, char *argv[], char *envp[])
        int option;
        char *http_proxy;
        time_t t;
+       int page_nr;
 
        debug = 0;
        rl_bind_key('\t', rl_insert);
@@ -580,7 +679,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:h",
+               option = getopt_long_only(argc, argv, "dqe:p:P:H:a:A:u:hg:",
                                          options, NULL);
                if (option == -1)
                        break;
@@ -594,6 +693,11 @@ int main(int argc, char *argv[], char *envp[])
                        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);
@@ -609,12 +713,24 @@ int main(int argc, char *argv[], char *envp[])
                case 'A':
                        if (strcasecmp(optarg, "update") == 0)
                                session->action = ACTION_UPDATE;
-                       if (strcasecmp(optarg, "friends") == 0)
+                       else if (strcasecmp(optarg, "friends") == 0)
                                session->action = ACTION_FRIENDS;
-                       if (strcasecmp(optarg, "public") == 0)
+                       else if (strcasecmp(optarg, "user") == 0)
+                               session->action = ACTION_USER;
+                       else if (strcasecmp(optarg, "replies") == 0)
+                               session->action = ACTION_REPLIES;
+                       else if (strcasecmp(optarg, "public") == 0)
                                session->action = ACTION_PUBLIC;
+                       else
+                               session->action = ACTION_UNKNOWN;
                        dbg("action = %d\n", session->action);
                        break;
+               case 'u':
+                       if (session->user)
+                               free(session->user);
+                       session->user = strdup(optarg);
+                       dbg("user = %s\n", session->user);
+                       break;
                case 'L':
                        if (session->logfile)
                                free(session->logfile);
@@ -643,6 +759,12 @@ int main(int argc, char *argv[], char *envp[])
                }
        }
 
+       if (session->action == ACTION_UNKNOWN) {
+               fprintf(stderr, "Unknown action, valid actions are:\n");
+               fprintf(stderr, "'update', 'friends', 'public', 'replies' or 'user'.\n");
+               goto exit;
+       }
+
        if (!session->account) {
                fprintf(stdout, "Enter twitter account: ");
                session->account = readline(NULL);
@@ -655,7 +777,7 @@ int main(int argc, char *argv[], char *envp[])
 
        if (session->action == ACTION_UPDATE) {
                if (session->bash)
-                       tweet = readline(NULL);
+                       tweet = get_string_from_stdin();
                else
                        tweet = readline("tweet: ");
                if (!tweet || strlen(tweet) == 0) {
@@ -673,6 +795,11 @@ int main(int argc, char *argv[], char *envp[])
                dbg("tweet = %s\n", session->tweet);
        }
 
+       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);