/* * 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.3 2010/10/19 18:29:03 adamdunkels Exp $ */ /** * \file * Contiki interface to posting Twitter messages * \author * Adam Dunkels */ #include "contiki.h" #include "contiki-net.h" #include "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, uip_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(); } /*---------------------------------------------------------------------------*/