From a3101e14de63407ea5552a2df1dc41880cf3f25b Mon Sep 17 00:00:00 2001 From: adamdunkels Date: Mon, 11 May 2009 16:57:49 +0000 Subject: [PATCH] Contiki program that posts messages to the Twitter microblogging service --- apps/twitter/Makefile.twitter | 2 + apps/twitter/twitter.c | 286 ++++++++++++++++++++++++++++++++++ apps/twitter/twitter.h | 39 +++++ 3 files changed, 327 insertions(+) create mode 100644 apps/twitter/Makefile.twitter create mode 100644 apps/twitter/twitter.c create mode 100644 apps/twitter/twitter.h diff --git a/apps/twitter/Makefile.twitter b/apps/twitter/Makefile.twitter new file mode 100644 index 000000000..d2e7d3f85 --- /dev/null +++ b/apps/twitter/Makefile.twitter @@ -0,0 +1,2 @@ +twitter_src = twitter.c +twitter_dsc = diff --git a/apps/twitter/twitter.c b/apps/twitter/twitter.c new file mode 100644 index 000000000..7a93825cc --- /dev/null +++ b/apps/twitter/twitter.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2009, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * $Id: twitter.c,v 1.1 2009/05/11 16:57:49 adamdunkels Exp $ + */ + +/** + * \file + * Contiki interface to posting Twitter messages + * \author + * Adam Dunkels + */ + +#include "contiki.h" +#include "contiki-net.h" +#include "shell.h" +#include "serial-shell.h" + +#include "twitter.h" + +#include +#include + +#define MAX_USERNAME_PASSWORD 32 +#define MAX_MESSAGE 160 +#define MAX_LENGTH 10 + +struct twitter_state { + unsigned char timer; + struct psock sin, sout; + char lengthstr[MAX_LENGTH]; + char base64_username_password[MAX_USERNAME_PASSWORD]; + char message[MAX_MESSAGE]; + uint8_t inputbuf[UIP_TCP_MSS]; + uip_ipaddr_t addr; +}; + +struct twitter_state conn; + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +PROCESS(twitter_process, "Twitter client"); +/*---------------------------------------------------------------------------*/ +static uint8_t +base64_encode_6bits(uint8_t c) +{ + if(c <= 25) { + return c + 'A'; + } else if(c <= 51) { + return c - 26 + 'a'; + } else if(c <= 61) { + return c - 52 + '0'; + } else if(c == 62) { + return '+'; + } else if(c == 63) { + return '/'; + } + /* This shouldn't happen because only 6 bits of data should be + passed to this function. */ + return '='; +} +/*---------------------------------------------------------------------------*/ +static void +base64_encode_24bits(const uint8_t inputdata[], char outputdata[], int len) +{ + switch(len) { + case 0: + outputdata[0] = outputdata[1] = outputdata[2] = outputdata[3] = '='; + break; + case 1: + outputdata[0] = base64_encode_6bits((inputdata[0] >> 2) & 0x3f); + outputdata[1] = base64_encode_6bits((((inputdata[0] << 4) & 0x30))); + outputdata[2] = outputdata[3] = '='; + break; + case 2: + outputdata[0] = base64_encode_6bits((inputdata[0] >> 2) & 0x3f); + outputdata[1] = base64_encode_6bits((((inputdata[0] << 4) & 0x30) | + (inputdata[1] >> 4)) & 0x3f); + outputdata[2] = base64_encode_6bits((((inputdata[1] << 2) & 0x3f))); + outputdata[3] = '='; + break; + case 3: + default: + outputdata[0] = base64_encode_6bits((inputdata[0] >> 2) & 0x3f); + outputdata[1] = base64_encode_6bits((((inputdata[0] << 4) & 0x30) | + (inputdata[1] >> 4)) & 0x3f); + outputdata[2] = base64_encode_6bits((((inputdata[1] << 2) & 0x3c) | + (inputdata[2] >> 6)) & 0x3f); + outputdata[3] = base64_encode_6bits((inputdata[2]) & 0x3f); + break; + } +} +/*---------------------------------------------------------------------------*/ +int +twitter_post(const uint8_t *username_password, const char *msg) +{ + int len; + int i, j; + struct twitter_state *s; + + process_exit(&twitter_process); + + /* s = (struct twitter_state *)memb_alloc(&conns);*/ + s = &conn; + if(s == NULL) { + PRINTF("Could not allocate memory for the tweet\n"); + return 0; + } + PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1); + PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1); + s->timer = 0; + + /* dec64 encode username:password pair */ + len = strlen((char *)username_password); + j = 0; + for(i = 0; i < len; i += 3) { + base64_encode_24bits(&username_password[i], &s->base64_username_password[j], + len - i); + j += 4; + } + s->base64_username_password[j] = 0; + + /* Copy the status message, and avoid the leading whitespace. */ + strcpy(s->message, "status="); + strcpy(&s->message[7], msg); + + /* PRINTF("username_password '%s'\n", s->base64_username_password); + PRINTF("message '%s'\n", s->message);*/ + + /* Spawn process to deal with TCP connection */ + process_start(&twitter_process, (char *)s); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +handle_output(struct twitter_state *s) +{ + PSOCK_BEGIN(&s->sout); + /* Send POST header */ + + PSOCK_SEND_STR(&s->sout, "POST /statuses/update.json HTTP/1.1\r\n"); + + /* Send Authorization header */ + PSOCK_SEND_STR(&s->sout, "Authorization: Basic "); + PSOCK_SEND_STR(&s->sout, s->base64_username_password); + PSOCK_SEND_STR(&s->sout, "\r\n"); + + /* Send Agent header */ + PSOCK_SEND_STR(&s->sout, "User-Agent: Contiki 2.x\r\n"); + PSOCK_SEND_STR(&s->sout, "Host: twitter.com\r\n"); + PSOCK_SEND_STR(&s->sout, "Accept: */*\r\n"); + + /* Send Content length header */ + PSOCK_SEND_STR(&s->sout, "Content-Length: "); + snprintf(s->lengthstr, sizeof(s->lengthstr), "%d", strlen(s->message)); + PSOCK_SEND_STR(&s->sout, s->lengthstr); + PSOCK_SEND_STR(&s->sout, "\r\n"); + + /* Send Content type header */ + PSOCK_SEND_STR(&s->sout, + "Content-Type: application/x-www-form-urlencoded\r\n\r\n"); + + + /* Send status message */ + PSOCK_SEND_STR(&s->sout, s->message); + + /* Close connection */ + PSOCK_CLOSE(&s->sout); + PSOCK_EXIT(&s->sout); + PSOCK_END(&s->sout); +} +/*---------------------------------------------------------------------------*/ +static int +handle_input(struct twitter_state *s) +{ + PSOCK_BEGIN(&s->sin); + + /* We don't care about input data for now */ + + PSOCK_END(&s->sin); +} +/*---------------------------------------------------------------------------*/ +static void +handle_connection(struct twitter_state *s) +{ + handle_input(s); + handle_output(s); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(twitter_process, ev, data) +{ + struct twitter_state *s = data; + struct uip_conn *conn; + + PROCESS_BEGIN(); + + /* Lookup host "twitter.com" */ + + /* XXX for now, just use 128.121.146.228 */ + uip_ipaddr(&s->addr, 128,121,146,228); + + + /* Open a TCP connection to port 80 on twitter.com */ + conn = tcp_connect(&s->addr, htons(80), s); + if(conn == NULL) { + PRINTF("Could not open TCP connection\n"); + /* memb_free(&conns, s);*/ + PROCESS_EXIT(); + } + + while(1) { + PROCESS_WAIT_EVENT(); + + if(ev == tcpip_event) { + struct twitter_state *s = (struct twitter_state *)data; + + if(uip_closed() || uip_aborted() || uip_timedout()) { + if(uip_closed()) { + PRINTF("Connection closed\n"); + } else { + PRINTF("Connection aborted/timedout\n"); + } + /* if(s != NULL) { + memb_free(&conns, s); + }*/ + PROCESS_EXIT(); + } else if(uip_connected()) { + handle_connection(s); + s->timer = 0; + } else if(s != NULL) { + if(uip_poll()) { + ++s->timer; + if(s->timer >= 20) { + PRINTF("Timed out due to inactivity\n"); + uip_abort(); + PROCESS_EXIT(); + /* memb_free(&conns, s);*/ + } + } else { + s->timer = 0; + } + handle_connection(s); + } else { + PRINTF("Abort because s == NULL\n"); + uip_abort(); + PROCESS_EXIT(); + } + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/apps/twitter/twitter.h b/apps/twitter/twitter.h new file mode 100644 index 000000000..b07422b0f --- /dev/null +++ b/apps/twitter/twitter.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2009, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * $Id: twitter.h,v 1.1 2009/05/11 16:57:49 adamdunkels Exp $ + */ + +#ifndef __TWITTER_H__ +#define __TWITTER_H__ + +int twitter_post(const uint8_t *username_password, const char *message); + +#endif /* __TWITTER_H__ */