/[debian]/bti/branches/upstream/current/bti.c
ViewVC logotype

Diff of /bti/branches/upstream/current/bti.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1650 by gregoa, Thu Mar 12 15:49:54 2009 UTC revision 1651 by gregoa, Tue Mar 24 18:01:39 2009 UTC
# Line 1  Line 1 
1  /*  /*
2   * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>   * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
3     * Copyright (C) 2009 Bart Trojanowski <bart@jukie.net>
4   *   *
5   * This program is free software; you can redistribute it and/or modify it   * This program is free software; you can redistribute it and/or modify it
6   * under the terms of the GNU General Public License as published by the   * under the terms of the GNU General Public License as published by the
# Line 26  Line 27 
27  #include <unistd.h>  #include <unistd.h>
28  #include <time.h>  #include <time.h>
29  #include <sys/stat.h>  #include <sys/stat.h>
30    #include <sys/types.h>
31    #include <sys/wait.h>
32  #include <curl/curl.h>  #include <curl/curl.h>
33  #include <readline/readline.h>  #include <readline/readline.h>
34  #include <libxml/xmlmemory.h>  #include <libxml/xmlmemory.h>
35  #include <libxml/parser.h>  #include <libxml/parser.h>
36  #include <libxml/tree.h>  #include <libxml/tree.h>
37    #include <pcre.h>
38  #include "bti_version.h"  #include "bti_version.h"
39    
40    
# Line 69  struct session { Line 73  struct session {
73          char *logfile;          char *logfile;
74          char *user;          char *user;
75          int bash;          int bash;
76            int shrink_urls;
77            int dry_run;
78            int page;
79          enum host host;          enum host host;
80          enum action action;          enum action action;
81  };  };
# Line 89  static void display_help(void) Line 96  static void display_help(void)
96          fprintf(stdout, "  --account accountname\n");          fprintf(stdout, "  --account accountname\n");
97          fprintf(stdout, "  --password password\n");          fprintf(stdout, "  --password password\n");
98          fprintf(stdout, "  --action action\n");          fprintf(stdout, "  --action action\n");
99          fprintf(stdout, "    ('update', 'friends', 'public', 'replies' or 'user')\n");          fprintf(stdout, "    ('update', 'friends', 'public', 'replies' "
100                    "or 'user')\n");
101          fprintf(stdout, "  --user screenname\n");          fprintf(stdout, "  --user screenname\n");
102          fprintf(stdout, "  --proxy PROXY:PORT\n");          fprintf(stdout, "  --proxy PROXY:PORT\n");
103          fprintf(stdout, "  --host HOST\n");          fprintf(stdout, "  --host HOST\n");
104          fprintf(stdout, "  --logfile logfile\n");          fprintf(stdout, "  --logfile logfile\n");
105            fprintf(stdout, "  --shrink-urls\n");
106            fprintf(stdout, "  --page PAGENUMBER\n");
107          fprintf(stdout, "  --bash\n");          fprintf(stdout, "  --bash\n");
108          fprintf(stdout, "  --debug\n");          fprintf(stdout, "  --debug\n");
109          fprintf(stdout, "  --version\n");          fprintf(stdout, "  --version\n");
# Line 224  static void parse_timeline(char *documen Line 234  static void parse_timeline(char *documen
234  {  {
235          xmlDocPtr doc;          xmlDocPtr doc;
236          xmlNodePtr current;          xmlNodePtr current;
         doc = xmlReadMemory(document, strlen(document), "timeline.xml", NULL, XML_PARSE_NOERROR);  
237    
238            doc = xmlReadMemory(document, strlen(document), "timeline.xml",
239                                NULL, XML_PARSE_NOERROR);
240          if (doc == NULL)          if (doc == NULL)
241                  return;                  return;
242    
# Line 324  static int send_request(struct session * Line 335  static int send_request(struct session *
335                  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);                  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
336                  switch (session->host) {                  switch (session->host) {
337                  case HOST_TWITTER:                  case HOST_TWITTER:
338                          curl_easy_setopt(curl, CURLOPT_URL, twitter_update_url);                          curl_easy_setopt(curl, CURLOPT_URL,
339                                             twitter_update_url);
340                          break;                          break;
341                  case HOST_IDENTICA:                  case HOST_IDENTICA:
342                          curl_easy_setopt(curl, CURLOPT_URL, identica_update_url);                          curl_easy_setopt(curl, CURLOPT_URL,
343                                             identica_update_url);
344                          break;                          break;
345                  }                  }
346                  curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);                  curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
# Line 338  static int send_request(struct session * Line 351  static int send_request(struct session *
351                           session->account, session->password);                           session->account, session->password);
352                  switch (session->host) {                  switch (session->host) {
353                  case HOST_TWITTER:                  case HOST_TWITTER:
354                          curl_easy_setopt(curl, CURLOPT_URL, twitter_friends_url);                          sprintf(user_url, "%s?page=%d", twitter_friends_url, session->page);
355                            curl_easy_setopt(curl, CURLOPT_URL, user_url);
356                          break;                          break;
357                  case HOST_IDENTICA:                  case HOST_IDENTICA:
358                          curl_easy_setopt(curl, CURLOPT_URL, identica_friends_url);                          sprintf(user_url, "%s?page=%d", identica_friends_url, session->page);
359                            curl_easy_setopt(curl, CURLOPT_URL, user_url);
360                          break;                          break;
361                  }                  }
362                  curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);                  curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
# Line 350  static int send_request(struct session * Line 365  static int send_request(struct session *
365          case ACTION_USER:          case ACTION_USER:
366                  switch (session->host) {                  switch (session->host) {
367                  case HOST_TWITTER:                  case HOST_TWITTER:
368                          sprintf(user_url, "%s%s.xml", twitter_user_url, session->user);                          sprintf(user_url, "%s%s.xml?page=%d", twitter_user_url, session->user, session->page);
369                          curl_easy_setopt(curl, CURLOPT_URL, user_url);                          curl_easy_setopt(curl, CURLOPT_URL, user_url);
370                          break;                          break;
371                  case HOST_IDENTICA:                  case HOST_IDENTICA:
372                          sprintf(user_url, "%s%s.xml", identica_user_url, session->user);                          sprintf(user_url, "%s%s.xml?page=%d", identica_user_url, session->user, session->page);
373                          curl_easy_setopt(curl, CURLOPT_URL, user_url);                          curl_easy_setopt(curl, CURLOPT_URL, user_url);
374                          break;                          break;
375                  }                  }
# Line 365  static int send_request(struct session * Line 380  static int send_request(struct session *
380                           session->account, session->password);                           session->account, session->password);
381                  switch (session->host) {                  switch (session->host) {
382                  case HOST_TWITTER:                  case HOST_TWITTER:
383                          curl_easy_setopt(curl, CURLOPT_URL, twitter_replies_url);                          sprintf(user_url, "%s?page=%d", twitter_replies_url, session->page);
384                            curl_easy_setopt(curl, CURLOPT_URL, user_url);
385                          break;                          break;
386                  case HOST_IDENTICA:                  case HOST_IDENTICA:
387                          curl_easy_setopt(curl, CURLOPT_URL, identica_replies_url);                          sprintf(user_url, "%s?page=%d", identica_replies_url, session->page);
388                            curl_easy_setopt(curl, CURLOPT_URL, user_url);
389                          break;                          break;
390                  }                  }
391                  curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);                  curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
# Line 377  static int send_request(struct session * Line 394  static int send_request(struct session *
394          case ACTION_PUBLIC:          case ACTION_PUBLIC:
395                  switch (session->host) {                  switch (session->host) {
396                  case HOST_TWITTER:                  case HOST_TWITTER:
397                          curl_easy_setopt(curl, CURLOPT_URL, twitter_public_url);                          sprintf(user_url, "%s?page=%d", twitter_public_url, session->page);
398                            curl_easy_setopt(curl, CURLOPT_URL, user_url);
399                          break;                          break;
400                  case HOST_IDENTICA:                  case HOST_IDENTICA:
401                          curl_easy_setopt(curl, CURLOPT_URL, identica_public_url);                          sprintf(user_url, "%s?page=%d", identica_public_url, session->page);
402                            curl_easy_setopt(curl, CURLOPT_URL, user_url);
403                          break;                          break;
404                  }                  }
405    
# Line 401  static int send_request(struct session * Line 420  static int send_request(struct session *
420    
421          curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);          curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
422          curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_buf);          curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_buf);
423          res = curl_easy_perform(curl);          if (!session->dry_run) {
424          if (res && !session->bash) {                  res = curl_easy_perform(curl);
425                  fprintf(stderr, "error(%d) trying to perform operation\n", res);                  if (res && !session->bash) {
426                  return -EINVAL;                          fprintf(stderr, "error(%d) trying to perform "
427                                    "operation\n", res);
428                            return -EINVAL;
429                    }
430          }          }
431    
432          curl_easy_cleanup(curl);          curl_easy_cleanup(curl);
# Line 427  static void parse_configfile(struct sess Line 449  static void parse_configfile(struct sess
449          char *action = NULL;          char *action = NULL;
450          char *user = NULL;          char *user = NULL;
451          char *file;          char *file;
452            int shrink_urls = 0;
453    
454          /* config file is ~/.bti  */          /* config file is ~/.bti  */
455          file = alloca(strlen(session->homedir) + 7);          file = alloca(strlen(session->homedir) + 7);
# Line 492  static void parse_configfile(struct sess Line 515  static void parse_configfile(struct sess
515                          c += 5;                          c += 5;
516                          if (c[0] != '\0')                          if (c[0] != '\0')
517                                  user = strdup(c);                                  user = strdup(c);
518                    } else if (!strncasecmp(c, "shrink-urls", 11) &&
519                                    (c[11] == '=')) {
520                            c += 12;
521                            if (!strncasecmp(c, "true", 4) ||
522                                            !strncasecmp(c, "yes", 3))
523                                    shrink_urls = 1;
524                  }                  }
525          } while (!feof(config_file));          } while (!feof(config_file));
526    
# Line 528  static void parse_configfile(struct sess Line 557  static void parse_configfile(struct sess
557                          session->action = ACTION_UNKNOWN;                          session->action = ACTION_UNKNOWN;
558                  free(action);                  free(action);
559          }          }
560          if (user) {          if (user)
561                  session->user = user;                  session->user = user;
562          }          session->shrink_urls = shrink_urls;
563    
564          /* Free buffer and close file.  */          /* Free buffer and close file.  */
565          free(line);          free(line);
# Line 615  static char *get_string_from_stdin(void) Line 644  static char *get_string_from_stdin(void)
644          return string;          return string;
645  }  }
646    
647    static int find_urls(const char *tweet, int **pranges)
648    {
649            /*
650             * magic obtained from
651             * http://www.geekpedia.com/KB65_How-to-validate-an-URL-using-RegEx-in-Csharp.html
652             */
653            static const char *re_magic =
654                    "(([a-zA-Z][0-9a-zA-Z+\\-\\.]*:)/{1,3}"
655                    "[0-9a-zA-Z;/~?:@&=+$\\.\\-_'()%]+)"
656                    "(#[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)?";
657            pcre *re;
658            const char *errptr;
659            int erroffset;
660            int ovector[10] = {0,};
661            const size_t ovsize = sizeof(ovector)/sizeof(*ovector);
662            int startoffset, tweetlen;
663            int i, rc;
664            int rbound = 10;
665            int rcount = 0;
666            int *ranges = malloc(sizeof(int) * rbound);
667    
668            re = pcre_compile(re_magic,
669                            PCRE_NO_AUTO_CAPTURE,
670                            &errptr, &erroffset, NULL);
671            if (!re) {
672                    fprintf(stderr, "pcre_compile @%u: %s\n", erroffset, errptr);
673                    exit(1);
674            }
675    
676            tweetlen = strlen(tweet);
677            for (startoffset = 0; startoffset < tweetlen; ) {
678    
679                    rc = pcre_exec(re, NULL, tweet, strlen(tweet), startoffset, 0,
680                                    ovector, ovsize);
681                    if (rc == PCRE_ERROR_NOMATCH)
682                            break;
683    
684                    if (rc < 0) {
685                            fprintf(stderr, "pcre_exec @%u: %s\n",
686                                    erroffset, errptr);
687                            exit(1);
688                    }
689    
690                    for (i = 0; i < rc; i += 2) {
691                            if ((rcount+2) == rbound) {
692                                    rbound *= 2;
693                                    ranges = realloc(ranges, sizeof(int) * rbound);
694                            }
695    
696                            ranges[rcount++] = ovector[i];
697                            ranges[rcount++] = ovector[i+1];
698                    }
699    
700                    startoffset = ovector[1];
701            }
702    
703            pcre_free(re);
704    
705            *pranges = ranges;
706            return rcount;
707    }
708    
709    /**
710     * bidirectional popen() call
711     *
712     * @param rwepipe - int array of size three
713     * @param exe - program to run
714     * @param argv - argument list
715     * @return pid or -1 on error
716     *
717     * The caller passes in an array of three integers (rwepipe), on successful
718     * execution it can then write to element 0 (stdin of exe), and read from
719     * element 1 (stdout) and 2 (stderr).
720     */
721    static int popenRWE(int *rwepipe, const char *exe, const char *const argv[])
722    {
723            int in[2];
724            int out[2];
725            int err[2];
726            int pid;
727            int rc;
728    
729            rc = pipe(in);
730            if (rc < 0)
731                    goto error_in;
732    
733            rc = pipe(out);
734            if (rc < 0)
735                    goto error_out;
736    
737            rc = pipe(err);
738            if (rc < 0)
739                    goto error_err;
740    
741            pid = fork();
742            if (pid > 0) {
743                    /* parent */
744                    close(in[0]);
745                    close(out[1]);
746                    close(err[1]);
747                    rwepipe[0] = in[1];
748                    rwepipe[1] = out[0];
749                    rwepipe[2] = err[0];
750                    return pid;
751            } else if (pid == 0) {
752                    /* child */
753                    close(in[1]);
754                    close(out[0]);
755                    close(err[0]);
756                    close(0);
757                    rc = dup(in[0]);
758                    close(1);
759                    rc = dup(out[1]);
760                    close(2);
761                    rc = dup(err[1]);
762    
763                    execvp(exe, (char **)argv);
764                    exit(1);
765            } else
766                    goto error_fork;
767    
768            return pid;
769    
770    error_fork:
771            close(err[0]);
772            close(err[1]);
773    error_err:
774            close(out[0]);
775            close(out[1]);
776    error_out:
777            close(in[0]);
778            close(in[1]);
779    error_in:
780            return -1;
781    }
782    
783    static int pcloseRWE(int pid, int *rwepipe)
784    {
785            int rc, status;
786            close(rwepipe[0]);
787            close(rwepipe[1]);
788            close(rwepipe[2]);
789            rc = waitpid(pid, &status, 0);
790            return status;
791    }
792    
793    static char *shrink_one_url(int *rwepipe, char *big)
794    {
795            int biglen = strlen(big);
796            char *small;
797            int smalllen;
798            int rc;
799    
800            rc = dprintf(rwepipe[0], "%s\n", big);
801            if (rc < 0)
802                    return big;
803    
804            smalllen = biglen + 128;
805            small = malloc(smalllen);
806            if (!small)
807                    return big;
808    
809            rc = read(rwepipe[1], small, smalllen);
810            if (rc < 0 || rc > biglen)
811                    goto error_free_small;
812    
813            if (strncmp(small, "http://", 7))
814                    goto error_free_small;
815    
816            smalllen = rc;
817            while (smalllen && isspace(small[smalllen-1]))
818                            small[--smalllen] = 0;
819    
820            free(big);
821            return small;
822    
823    error_free_small:
824            free(small);
825            return big;
826    }
827    
828    static char *shrink_urls(char *text)
829    {
830            int *ranges;
831            int rcount;
832            int i;
833            int inofs = 0;
834            int outofs = 0;
835            const char *const shrink_args[] = {
836                    "bti-shrink-urls",
837                    NULL
838            };
839            int shrink_pid;
840            int shrink_pipe[3];
841            int inlen = strlen(text);
842    
843            dbg("before len=%u\n", inlen);
844    
845            shrink_pid = popenRWE(shrink_pipe, shrink_args[0], shrink_args);
846            if (shrink_pid < 0)
847                    return text;
848    
849            rcount = find_urls(text, &ranges);
850            if (!rcount)
851                    return text;
852    
853            for (i = 0; i < rcount; i += 2) {
854                    int url_start = ranges[i];
855                    int url_end = ranges[i+1];
856                    int long_url_len = url_end - url_start;
857                    char *url = strndup(text + url_start, long_url_len);
858                    int short_url_len;
859                    int not_url_len = url_start - inofs;
860    
861                    dbg("long  url[%u]: %s\n", long_url_len, url);
862                    url = shrink_one_url(shrink_pipe, url);
863                    short_url_len = url ? strlen(url) : 0;
864                    dbg("short url[%u]: %s\n", short_url_len, url);
865    
866                    if (!url || short_url_len >= long_url_len) {
867                            /* The short url ended up being too long
868                             * or unavailable */
869                            if (inofs) {
870                                    strncpy(text + outofs, text + inofs,
871                                                    not_url_len + long_url_len);
872                            }
873                            inofs += not_url_len + long_url_len;
874                            outofs += not_url_len + long_url_len;
875    
876                    } else {
877                            /* copy the unmodified block */
878                            strncpy(text + outofs, text + inofs, not_url_len);
879                            inofs += not_url_len;
880                            outofs += not_url_len;
881    
882                            /* copy the new url */
883                            strncpy(text + outofs, url, short_url_len);
884                            inofs += long_url_len;
885                            outofs += short_url_len;
886                    }
887    
888                    free(url);
889            }
890    
891            /* copy the last block after the last match */
892            if (inofs) {
893                    int tail = inlen - inofs;
894                    if (tail) {
895                            strncpy(text + outofs, text + inofs, tail);
896                            outofs += tail;
897                    }
898            }
899    
900            free(ranges);
901    
902            (void)pcloseRWE(shrink_pid, shrink_pipe);
903    
904            text[outofs] = 0;
905            dbg("after len=%u\n", outofs);
906            return text;
907    }
908    
909  int main(int argc, char *argv[], char *envp[])  int main(int argc, char *argv[], char *envp[])
910  {  {
911          static const struct option options[] = {          static const struct option options[] = {
# Line 626  int main(int argc, char *argv[], char *e Line 917  int main(int argc, char *argv[], char *e
917                  { "action", 1, NULL, 'A' },                  { "action", 1, NULL, 'A' },
918                  { "user", 1, NULL, 'u' },                  { "user", 1, NULL, 'u' },
919                  { "logfile", 1, NULL, 'L' },                  { "logfile", 1, NULL, 'L' },
920                    { "shrink-urls", 0, NULL, 's' },
921                  { "help", 0, NULL, 'h' },                  { "help", 0, NULL, 'h' },
922                  { "bash", 0, NULL, 'b' },                  { "bash", 0, NULL, 'b' },
923                    { "dry-run", 0, NULL, 'n' },
924                    { "page", 1, NULL, 'g' },
925                  { "version", 0, NULL, 'v' },                  { "version", 0, NULL, 'v' },
926                  { }                  { }
927          };          };
# Line 638  int main(int argc, char *argv[], char *e Line 932  int main(int argc, char *argv[], char *e
932          int option;          int option;
933          char *http_proxy;          char *http_proxy;
934          time_t t;          time_t t;
935            int page_nr;
936    
937          debug = 0;          debug = 0;
938          rl_bind_key('\t', rl_insert);          rl_bind_key('\t', rl_insert);
# Line 670  int main(int argc, char *argv[], char *e Line 965  int main(int argc, char *argv[], char *e
965          parse_configfile(session);          parse_configfile(session);
966    
967          while (1) {          while (1) {
968                  option = getopt_long_only(argc, argv, "dqe:p:P:H:a:A:u:h",                  option = getopt_long_only(argc, argv, "dqe:p:P:H:a:A:u:hg:",
969                                            options, NULL);                                            options, NULL);
970                  if (option == -1)                  if (option == -1)
971                          break;                          break;
# Line 684  int main(int argc, char *argv[], char *e Line 979  int main(int argc, char *argv[], char *e
979                          session->account = strdup(optarg);                          session->account = strdup(optarg);
980                          dbg("account = %s\n", session->account);                          dbg("account = %s\n", session->account);
981                          break;                          break;
982                    case 'g':
983                            page_nr = atoi(optarg);
984                            dbg("page = %d\n", page_nr);
985                            session->page = page_nr;
986                            break;
987                  case 'p':                  case 'p':
988                          if (session->password)                          if (session->password)
989                                  free(session->password);                                  free(session->password);
# Line 723  int main(int argc, char *argv[], char *e Line 1023  int main(int argc, char *argv[], char *e
1023                          session->logfile = strdup(optarg);                          session->logfile = strdup(optarg);
1024                          dbg("logfile = %s\n", session->logfile);                          dbg("logfile = %s\n", session->logfile);
1025                          break;                          break;
1026                    case 's':
1027                            session->shrink_urls = 1;
1028                            break;
1029                  case 'H':                  case 'H':
1030                          if (strcasecmp(optarg, "twitter") == 0)                          if (strcasecmp(optarg, "twitter") == 0)
1031                                  session->host = HOST_TWITTER;                                  session->host = HOST_TWITTER;
# Line 736  int main(int argc, char *argv[], char *e Line 1039  int main(int argc, char *argv[], char *e
1039                  case 'h':                  case 'h':
1040                          display_help();                          display_help();
1041                          goto exit;                          goto exit;
1042                    case 'n':
1043                            session->dry_run = 1;
1044                            break;
1045                  case 'v':                  case 'v':
1046                          display_version();                          display_version();
1047                          goto exit;                          goto exit;
# Line 747  int main(int argc, char *argv[], char *e Line 1053  int main(int argc, char *argv[], char *e
1053    
1054          if (session->action == ACTION_UNKNOWN) {          if (session->action == ACTION_UNKNOWN) {
1055                  fprintf(stderr, "Unknown action, valid actions are:\n");                  fprintf(stderr, "Unknown action, valid actions are:\n");
1056                  fprintf(stderr, "'update', 'friends', 'public', 'replies' or 'user'.\n");                  fprintf(stderr, "'update', 'friends', 'public', "
1057                            "'replies' or 'user'.\n");
1058                  goto exit;                  goto exit;
1059          }          }
1060    
# Line 771  int main(int argc, char *argv[], char *e Line 1078  int main(int argc, char *argv[], char *e
1078                          return -1;                          return -1;
1079                  }                  }
1080    
1081                    if (session->shrink_urls)
1082                            tweet = shrink_urls(tweet);
1083    
1084                  session->tweet = zalloc(strlen(tweet) + 10);                  session->tweet = zalloc(strlen(tweet) + 10);
1085                  if (session->bash)                  if (session->bash)
1086                          sprintf(session->tweet, "$ %s", tweet);                          sprintf(session->tweet, "$ %s", tweet);
# Line 784  int main(int argc, char *argv[], char *e Line 1094  int main(int argc, char *argv[], char *e
1094          if (!session->user)          if (!session->user)
1095                  session->user = strdup(session->account);                  session->user = strdup(session->account);
1096    
1097            if (session->page == 0)
1098                    session->page = 1;
1099          dbg("account = %s\n", session->account);          dbg("account = %s\n", session->account);
1100          dbg("password = %s\n", session->password);          dbg("password = %s\n", session->password);
1101          dbg("host = %d\n", session->host);          dbg("host = %d\n", session->host);

Legend:
Removed from v.1650  
changed lines
  Added in v.1651

  ViewVC Help
Powered by ViewVC 1.1.26