initial commit of bti, everything's working now
authorGreg Kroah-Hartman <gregkh@suse.de>
Mon, 19 May 2008 21:59:05 +0000 (14:59 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 19 May 2008 21:59:05 +0000 (14:59 -0700)
Makefile [new file with mode: 0644]
bti.1 [new file with mode: 0644]
bti.c [new file with mode: 0644]
bti.xml [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..75ccb37
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,112 @@
+#
+# Copyright (C) 2006 Greg Kroah-Hartman <greg@kroah.com>
+#
+# 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
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+VERSION = 001
+
+PROGRAM = bti
+
+CORE_OBJS = \
+       bti.o
+
+GEN_HEADERS = \
+       bti_version.h
+
+MAN_PAGES = \
+       bti.1
+
+CROSS_COMPILE ?=
+CC = $(CROSS_COMPILE)gcc
+LD = $(CROSS_COMPILE)gcc
+AR = $(CROSS_COMPILE)ar
+
+override CFLAGS        += -g -Wall -pipe -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -O2
+
+WARNINGS       = -Wstrict-prototypes -Wsign-compare -Wshadow \
+                 -Wchar-subscripts -Wmissing-declarations -Wnested-externs \
+                 -Wpointer-arith -Wcast-align -Wsign-compare -Wmissing-prototypes
+CFLAGS         += $(WARNINGS)
+LDFLAGS                += -Wl,-warn-common,--as-needed
+
+
+ifeq ($(strip $(V)),)
+       E = @echo
+       Q = @
+else
+       E = @\#
+       Q =
+endif
+export E Q
+
+
+# We need -lcurl for the curl stuff
+# We need -lsocket and -lnsl when on Solaris
+# We need -lssl and -lcrypto when using libcurl with SSL support
+# We need -lpthread for the pthread example
+#LIB_OBJS = -lcurl -lnsl -lssl -lcrypto
+LIB_OBJS = -lcurl -lnsl
+
+all:   $(PROGRAM) $(MAN_PAGES)
+
+# "Static Pattern Rule" to build all programs
+bti: %: $(HEADERS) $(GEN_HEADERS) $(CORE_OBJS)
+       $(E) "  LD      " $@
+       $(Q) $(LD) $(LDFLAGS) $(CORE_OBJS) -o $@ $(LIB_OBJS)
+
+
+# build the objects
+%.o: %.c $(HEADERS) $(GEN_HEADERS)
+       $(E) "  CC      " $@
+       $(Q) $(CC) -c $(CFLAGS) $< -o $@
+
+
+bti_version.h:
+       $(E) "  GENHDR  " $@
+       $(Q) echo "/* Generated by make. */" > $@
+       $(Q) echo \#define BTI_VERSION  \"$(VERSION)\" >> $@
+
+
+# man pages
+%.1: %.xml
+       $(E) "  XMLTO   " $@
+       $(Q) xmlto man $?
+.PRECIOUS: %.3
+
+clean:
+       $(E) "  CLEAN   "
+       $(Q) - find . -type f -name '*.orig' -print0 | xargs -0r rm -f
+       $(Q) - find . -type f -name '*.rej' -print0 | xargs -0r rm -f
+       $(Q) - find . -type f -name '*~' -print0 | xargs -0r rm -f
+       $(Q) - find . -type f -name '*.[oas]' -print0 | xargs -0r rm -f
+       $(Q) - find . -type f -name "*.gcno" -print0 | xargs -0r rm -f
+       $(Q) - find . -type f -name "*.gcda" -print0 | xargs -0r rm -f
+       $(Q) - find . -type f -name "*.gcov" -print0 | xargs -0r rm -f
+       $(Q) - rm -f core $(PROGRAM) $(GEN_HEADERS)
+.PHONY: clean
+
+release:
+       $(Q) - rm -f bti-$(VERSION).tar.gz
+       $(Q) - rm -f bti-$(VERSION).tar.bz2
+       head -1 ChangeLog | grep -q "to v$(VERSION)"
+       head -1 RELEASE-NOTES | grep -q "bti $(VERSION)"
+       git commit -a -m "release $(VERSION)"
+       cat .git/refs/heads/master > .git/refs/tags/$(VERSION)
+       @ echo
+       git-archive --format=tar --prefix=bti-$(VERSION)/ HEAD | gzip -9v > bti-$(VERSION).tar.gz
+       git-archive --format=tar --prefix=bti-$(VERSION)/ HEAD | bzip2 -9v > bti-$(VERSION).tar.bz2
+.PHONY: release
+
diff --git a/bti.1 b/bti.1
new file mode 100644 (file)
index 0000000..5e3b52a
--- /dev/null
+++ b/bti.1
@@ -0,0 +1,50 @@
+.\"     Title: bti
+.\"    Author: 
+.\" Generator: DocBook XSL Stylesheets v1.73.2 <http://docbook.sf.net/>
+.\"      Date: May 2008
+.\"    Manual: bti
+.\"    Source: bti
+.\"
+.TH "BTI" "1" "May 2008" "bti" "bti"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+bti - send a tweet to twitter.com from stdinput
+.SH "SYNOPSIS"
+.HP 4
+\fBbti\fR [\fB\-\-account\ account\fR] [\fB\-\-password\ password\fR] [\fB\-\-quiet\fR] [\fB\-\-debug\fR] [\fB\-\-version\fR] [\fB\-\-help\fR]
+.SH "DESCRIPTION"
+.PP
+bti sends a twitter message to twitter\.com\.
+.SH "OPTIONS"
+.PP
+\fB\-\-account account\fR
+.RS 4
+Specify the twitter\.com account name\.
+.RE
+.PP
+\fB\-\-password password\fR
+.RS 4
+Specify the password of your twitter\.com account\.
+.RE
+.PP
+\fB\-\-debug\fR
+.RS 4
+Print a whole bunch of debugging messages to stdout\.
+.RE
+.PP
+\fB\-\-version\fR
+.RS 4
+Print version number\.
+.RE
+.PP
+\fB\-\-help\fR
+.RS 4
+Print help text\.
+.RE
+.SH "AUTHOR"
+.PP
+Written by Greg Kroah\-Hartman
+<greg@kroah\.com>\.
diff --git a/bti.c b/bti.c
new file mode 100644 (file)
index 0000000..11ba091
--- /dev/null
+++ b/bti.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <curl/curl.h>
+#include "bti_version.h"
+
+
+#define zalloc(size)   calloc(size, 1)
+
+#define dbg(format, arg...)                                            \
+       do {                                                            \
+               if (debug)                                              \
+                       printf("%s: " format , __func__ , ## arg );     \
+       } while (0)
+
+
+static int debug = 0;
+
+struct session {
+       char *password;
+       char *account;
+       char *tweet;
+       int quiet;
+};
+
+struct bti_curl_buffer {
+       char *data;
+       int length;
+};
+
+static void display_help(void)
+{
+       fprintf(stdout, "bti - send tweet to twitter\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, "  --bash\n");
+       fprintf(stdout, "  --quiet\n");
+       fprintf(stdout, "  --debug\n");
+       fprintf(stdout, "  --help\n");
+}
+
+static char *get_string_from_stdin(void)
+{
+       char *temp;
+       char *string;
+
+       string = zalloc(100);
+       if (!string)
+               return NULL;
+
+       if (!fgets(string, 99, stdin))
+               return NULL;
+       temp = strchr(string, '\n');
+       *temp = '\0';
+       return string;
+}
+
+static struct session *session_alloc(void)
+{
+       struct session *session;
+
+       session = zalloc(sizeof(*session));
+       if (!session)
+               return NULL;
+       return session;
+}
+
+static void session_free(struct session *session)
+{
+       if (!session)
+               return;
+       free(session->password);
+       free(session->account);
+       free(session->tweet);
+       free(session);
+}
+
+static struct bti_curl_buffer *bti_curl_buffer_alloc(void)
+{
+       struct bti_curl_buffer *buffer;
+
+       buffer = zalloc(sizeof(*buffer));
+       if (!buffer)
+               return NULL;
+
+       /* start out with a data buffer of 1 byte to
+        * make the buffer fill logic simpler */
+       buffer->data = zalloc(1);
+       if (!buffer->data) {
+               free(buffer);
+               return NULL;
+       }
+       buffer->length = 0;
+       return buffer;
+}
+
+static void bti_curl_buffer_free(struct bti_curl_buffer *buffer)
+{
+       if (!buffer)
+               return;
+       free(buffer->data);
+       free(buffer);
+}
+
+static const char *twitter_url = "https://twitter.com/statuses/update.xml";
+
+static CURL *curl_init(void)
+{
+       CURL *curl;
+
+       curl = curl_easy_init();
+       if (!curl) {
+               fprintf(stderr, "Can not init CURL!\n");
+               return NULL;
+       }
+       /* some ssl sanity checks on the connection we are making */
+       curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
+       curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
+       return curl;
+}
+
+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;
+       char *temp;
+
+       if ((!buffer) || (!buffer_size) || (!curl_buf))
+               return -EINVAL;
+
+       /* add to the data we already have */
+       temp = zalloc(curl_buf->length + buffer_size + 1);
+       if (!temp)
+               return -ENOMEM;
+
+       memcpy(temp, curl_buf->data, curl_buf->length);
+       free(curl_buf->data);
+       curl_buf->data = temp;
+       memcpy(&curl_buf->data[curl_buf->length], (char *)buffer, buffer_size);
+       curl_buf->length += buffer_size;
+
+       dbg("%s\n", curl_buf->data);
+
+       return buffer_size;
+}
+
+static int send_tweet(struct session *session)
+{
+       char user_password[500];
+       char data[500];
+       struct bti_curl_buffer *curl_buf;
+       CURL *curl = NULL;
+       CURLcode res;
+       struct curl_httppost *formpost = NULL;
+       struct curl_httppost *lastptr = NULL;
+
+       if (!session)
+               return -EINVAL;
+
+       curl_buf = bti_curl_buffer_alloc();
+       if (!curl_buf)
+               return -ENOMEM;
+
+       snprintf(user_password, sizeof(user_password), "%s:%s",
+                session->account, session->password);
+       snprintf(data, sizeof(data), "status=\"%s\"", session->tweet);
+
+       curl = curl_init();
+       if (!curl)
+               return -EINVAL;
+
+       curl_formadd(&formpost, &lastptr,
+                    CURLFORM_COPYNAME, "status",
+                    CURLFORM_COPYCONTENTS, session->tweet,
+                    CURLFORM_END);
+
+       curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
+       curl_easy_setopt(curl, CURLOPT_URL, twitter_url);
+       if (debug)
+               curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+       curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
+
+       dbg("user_password = %s\n", user_password);
+       dbg("data = %s\n", data);
+
+       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
+       curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_buf);
+       res = curl_easy_perform(curl);
+       if (res) {
+               printf("error(%d) trying to send tweet\n", res);
+               return -EINVAL;
+       }
+
+       curl_easy_cleanup(curl);
+       curl_formfree(formpost);
+       bti_curl_buffer_free(curl_buf);
+       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 *file;
+       char *home = getenv("HOME");
+
+       /* config file is ~/.bti  */
+       file = alloca(strlen(home) + 7);
+
+       sprintf(file, "%s/.bti", home);
+
+       config_file = fopen(file, "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);
+               }
+       } while (!feof(config_file));
+
+       if (password)
+               session->password = password;
+       if (account)
+               session->account= account;
+
+       /* Free buffer and close file.  */
+       free(line);
+       fclose(config_file);
+}
+
+int main(int argc, char *argv[], char *envp[])
+{
+       static const struct option options[] = {
+               { "debug", 0, NULL, 'd' },
+               { "account", 1, NULL, 'a' },
+               { "password", 1, NULL, 'p' },
+               { "bash", 1, NULL, 'b' },
+               { "help", 0, NULL, 'h' },
+               { "quiet", 0, NULL, 'q' },
+               { }
+       };
+       struct session *session;
+       int retval;
+       int option;
+
+       session = session_alloc();
+       if (!session) {
+               fprintf(stderr, "no more memory...\n");
+               return -1;
+       }
+
+       curl_global_init(CURL_GLOBAL_ALL);
+       parse_configfile(session);
+
+       while (1) {
+               option = getopt_long_only(argc, argv, "dqe:p:a:h",
+                                         options, NULL);
+               if (option == -1)
+                       break;
+               switch (option) {
+               case 'd':
+                       debug = 1;
+                       break;
+               case 'a':
+                       if (session->account)
+                               free(session->account);
+                       session->account = strdup(optarg);
+                       dbg("account = %s\n", session->account);
+                       break;
+               case 'p':
+                       if (session->password)
+                               free(session->password);
+                       session->password = strdup(optarg);
+                       dbg("password = %s\n", session->password);
+                       break;
+               case 'q':
+                       session->quiet = 1;
+                       break;
+               case 'h':
+                       display_help();
+                       goto exit;
+               default:
+                       display_help();
+                       goto exit;
+               }
+       }
+
+       if (!session->account) {
+               fprintf(stdout, "Enter twitter account: ");
+               session->account = get_string_from_stdin();
+       }
+
+       if (!session->password) {
+               fprintf(stdout, "Enter twitter password: ");
+               session->password = get_string_from_stdin();
+       }
+
+       session->tweet = get_string_from_stdin();
+
+       if (strlen(session->tweet) == 0) {
+               dbg("no tweet?\n");
+               return -1;
+       }
+
+       dbg("account = %s\n", session->account);
+       dbg("password = %s\n", session->password);
+       dbg("tweet = %s\n", session->tweet);
+
+       retval = send_tweet(session);
+       if (retval) {
+               fprintf(stderr, "tweet failed\n");
+               return -1;
+       }
+
+       session_free(session);
+exit:
+       return 0;
+}
diff --git a/bti.xml b/bti.xml
new file mode 100644 (file)
index 0000000..cbda742
--- /dev/null
+++ b/bti.xml
@@ -0,0 +1,87 @@
+<?xml version='1.0'?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<article>
+  <section>
+    <title>bti</title>
+    <refentry>
+      <refentryinfo>
+        <title>bti</title>
+        <date>May 2008</date>
+        <productname>bti</productname>
+      </refentryinfo>
+
+      <refmeta>
+        <refentrytitle>bti</refentrytitle>
+        <manvolnum>1</manvolnum>
+        <refmiscinfo class="version"></refmiscinfo>
+      </refmeta>
+
+      <refnamediv>
+        <refname>bti</refname><refpurpose>send a tweet to twitter.com from stdinput</refpurpose>
+      </refnamediv>
+
+      <refsynopsisdiv>
+        <cmdsynopsis>
+          <command>bti</command>
+          <arg><option>--account account</option></arg>
+          <arg><option>--password password</option></arg>
+         <arg><option>--quiet</option></arg>
+          <arg><option>--debug</option></arg>
+          <arg><option>--version</option></arg>
+          <arg><option>--help</option></arg>
+        </cmdsynopsis>
+      </refsynopsisdiv>
+
+      <refsect1><title>DESCRIPTION</title>
+        <para>bti sends a twitter message to twitter.com.
+       </para>
+      </refsect1>
+
+      <refsect1><title>OPTIONS</title>
+        <variablelist>
+          <varlistentry>
+            <term><option>--account account</option></term>
+            <listitem>
+              <para>
+               Specify the twitter.com account name.
+             </para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><option>--password password</option></term>
+            <listitem>
+              <para>
+               Specify the password of your twitter.com account.
+              </para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><option>--debug</option></term>
+            <listitem>
+              <para>Print a whole bunch of debugging messages to stdout.</para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><option>--version</option></term>
+            <listitem>
+              <para>Print version number.</para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><option>--help</option></term>
+            <listitem>
+              <para>Print help text.</para>
+            </listitem>
+          </varlistentry>
+        </variablelist>
+      </refsect1>
+
+      <refsect1><title>AUTHOR</title>
+        <para>Written by Greg Kroah-Hartman <email>greg@kroah.com</email>.</para>
+      </refsect1>
+
+    </refentry>
+  </section>
+</article>