Contiki 3.x
http-post-auth.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009, Swedish Institute of Computer Science.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file
35  * Contiki interface to post to http basic auth services
36  * \author
37  * Adam Dunkels <adam@sics.se>
38  */
39 
40 #include "contiki.h"
41 #include "contiki-net.h"
42 
43 #include "http-post-auth.h"
44 
45 #include <stdio.h>
46 #include <string.h>
47 
48 #define MAX_USERNAME_PASSWORD 32
49 #define MAX_MESSAGE 160
50 #define MAX_LENGTH 10
51 
52 #define HOST_NAME "api.example.org"
53 
54 struct http_post_auth_state {
55  unsigned char timer;
56  struct psock sin, sout;
57  char lengthstr[MAX_LENGTH];
58  char base64_username_password[MAX_USERNAME_PASSWORD];
59  char message[MAX_MESSAGE];
60  uint8_t inputbuf[UIP_TCP_MSS];
61  uip_ipaddr_t addr;
62 };
63 
64 struct http_post_auth_state conn;
65 
66 #ifndef DEBUG
67 #define DEBUG 0
68 #endif
69 #if DEBUG
70 #include <stdio.h>
71 #define PRINTF(...) printf(__VA_ARGS__)
72 #else
73 #define PRINTF(...)
74 #endif
75 
76 PROCESS(http_post_auth_process, "HTTP POST auth client");
77 /*---------------------------------------------------------------------------*/
78 static uint8_t
79 base64_encode_6bits(uint8_t c)
80 {
81  if(c <= 25) {
82  return c + 'A';
83  } else if(c <= 51) {
84  return c - 26 + 'a';
85  } else if(c <= 61) {
86  return c - 52 + '0';
87  } else if(c == 62) {
88  return '+';
89  } else if(c == 63) {
90  return '/';
91  }
92  /* This shouldn't happen because only 6 bits of data should be
93  passed to this function. */
94  return '=';
95 }
96 /*---------------------------------------------------------------------------*/
97 static void
98 base64_encode_24bits(const uint8_t inputdata[], char outputdata[], int len)
99 {
100  switch(len) {
101  case 0:
102  outputdata[0] = outputdata[1] = outputdata[2] = outputdata[3] = '=';
103  break;
104  case 1:
105  outputdata[0] = base64_encode_6bits((inputdata[0] >> 2) & 0x3f);
106  outputdata[1] = base64_encode_6bits((((inputdata[0] << 4) & 0x30)));
107  outputdata[2] = outputdata[3] = '=';
108  break;
109  case 2:
110  outputdata[0] = base64_encode_6bits((inputdata[0] >> 2) & 0x3f);
111  outputdata[1] = base64_encode_6bits((((inputdata[0] << 4) & 0x30) |
112  (inputdata[1] >> 4)) & 0x3f);
113  outputdata[2] = base64_encode_6bits((((inputdata[1] << 2) & 0x3f)));
114  outputdata[3] = '=';
115  break;
116  case 3:
117  default:
118  outputdata[0] = base64_encode_6bits((inputdata[0] >> 2) & 0x3f);
119  outputdata[1] = base64_encode_6bits((((inputdata[0] << 4) & 0x30) |
120  (inputdata[1] >> 4)) & 0x3f);
121  outputdata[2] = base64_encode_6bits((((inputdata[1] << 2) & 0x3c) |
122  (inputdata[2] >> 6)) & 0x3f);
123  outputdata[3] = base64_encode_6bits((inputdata[2]) & 0x3f);
124  break;
125  }
126 }
127 /*---------------------------------------------------------------------------*/
128 int
129 http_post_auth(const uint8_t *username_password, const char *msg)
130 {
131  int len;
132  int i, j;
133  struct http_post_auth_state *s;
134 
135  process_exit(&http_post_auth_process);
136 
137  /* s = (struct http_post_auth_state *)memb_alloc(&conns);*/
138  s = &conn;
139  if(s == NULL) {
140  PRINTF("Could not allocate memory for the tweet\n");
141  return 0;
142  }
143  PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
144  PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
145  s->timer = 0;
146 
147  /* dec64 encode username:password pair */
148  len = strlen((char *)username_password);
149  j = 0;
150  for(i = 0; i < len; i += 3) {
151  base64_encode_24bits(&username_password[i], &s->base64_username_password[j],
152  len - i);
153  j += 4;
154  }
155  s->base64_username_password[j] = 0;
156 
157  /* Copy the status message, and avoid the leading whitespace. */
158  strcpy(s->message, "status=");
159  strcpy(&s->message[7], msg);
160 
161  /* PRINTF("username_password '%s'\n", s->base64_username_password);
162  PRINTF("message '%s'\n", s->message);*/
163 
164  /* Spawn process to deal with TCP connection */
165  process_start(&http_post_auth_process, (void *)s);
166  return 1;
167 }
168 /*---------------------------------------------------------------------------*/
169 static int
170 handle_output(struct http_post_auth_state *s)
171 {
172  PSOCK_BEGIN(&s->sout);
173  /* Send POST header */
174 
175  PSOCK_SEND_STR(&s->sout, "POST /statuses/update.json HTTP/1.1\r\n");
176 
177  /* Send Authorization header */
178  PSOCK_SEND_STR(&s->sout, "Authorization: Basic ");
179  PSOCK_SEND_STR(&s->sout, s->base64_username_password);
180  PSOCK_SEND_STR(&s->sout, "\r\n");
181 
182  /* Send Agent header */
183  PSOCK_SEND_STR(&s->sout, "User-Agent: Contiki 2.x\r\n");
184  PSOCK_SEND_STR(&s->sout, "Host: " HOST_NAME "\r\n");
185  PSOCK_SEND_STR(&s->sout, "Accept: */*\r\n");
186 
187  /* Send Content length header */
188  PSOCK_SEND_STR(&s->sout, "Content-Length: ");
189  snprintf(s->lengthstr, sizeof(s->lengthstr), "%d", strlen(s->message));
190  PSOCK_SEND_STR(&s->sout, s->lengthstr);
191  PSOCK_SEND_STR(&s->sout, "\r\n");
192 
193  /* Send Content type header */
194  PSOCK_SEND_STR(&s->sout,
195  "Content-Type: application/x-www-form-urlencoded\r\n\r\n");
196 
197 
198  /* Send status message */
199  PSOCK_SEND_STR(&s->sout, s->message);
200 
201  /* Close connection */
202  PSOCK_CLOSE(&s->sout);
203  PSOCK_EXIT(&s->sout);
204  PSOCK_END(&s->sout);
205 }
206 /*---------------------------------------------------------------------------*/
207 static int
208 handle_input(struct http_post_auth_state *s)
209 {
210  PSOCK_BEGIN(&s->sin);
211 
212  /* We don't care about input data for now */
213 
214  PSOCK_END(&s->sin);
215 }
216 /*---------------------------------------------------------------------------*/
217 static void
218 handle_connection(struct http_post_auth_state *s)
219 {
220  handle_input(s);
221  handle_output(s);
222 }
223 /*---------------------------------------------------------------------------*/
224 PROCESS_THREAD(http_post_auth_process, ev, data)
225 {
226  struct http_post_auth_state *s = data;
227  struct uip_conn *conn;
228 
229  PROCESS_BEGIN();
230 
231  /* Lookup host */
232 
233  /* XXX for now, just use 128.121.146.228 */
234  uip_ipaddr(&s->addr, 128,121,146,228);
235 
236 
237  /* Open a TCP connection to port 80 */
238  conn = tcp_connect(&s->addr, uip_htons(80), s);
239  if(conn == NULL) {
240  PRINTF("Could not open TCP connection\n");
241  /* memb_free(&conns, s);*/
242  PROCESS_EXIT();
243  }
244 
245  while(1) {
247 
248  if(ev == tcpip_event) {
249  struct http_post_auth_state *s = (struct http_post_auth_state *)data;
250 
251  if(uip_closed() || uip_aborted() || uip_timedout()) {
252  if(uip_closed()) {
253  PRINTF("Connection closed\n");
254  } else {
255  PRINTF("Connection aborted/timedout\n");
256  }
257  /* if(s != NULL) {
258  memb_free(&conns, s);
259  }*/
260  PROCESS_EXIT();
261  } else if(uip_connected()) {
262  handle_connection(s);
263  s->timer = 0;
264  } else if(s != NULL) {
265  if(uip_poll()) {
266  ++s->timer;
267  if(s->timer >= 20) {
268  PRINTF("Timed out due to inactivity\n");
269  uip_abort();
270  PROCESS_EXIT();
271  /* memb_free(&conns, s);*/
272  }
273  } else {
274  s->timer = 0;
275  }
276  handle_connection(s);
277  } else {
278  PRINTF("Abort because s == NULL\n");
279  uip_abort();
280  PROCESS_EXIT();
281  }
282  }
283  }
284 
285  PROCESS_END();
286 }
287 /*---------------------------------------------------------------------------*/
A timer.
Definition: timer.h:86
#define PROCESS_EXIT()
Exit the currently running process.
Definition: process.h:200
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
#define PSOCK_BEGIN(psock)
Start the protosocket protothread in a function.
Definition: psock.h:164
Representation of a uIP TCP connection.
Definition: uip.h:1336
CCIF struct uip_conn * tcp_connect(uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
Open a TCP connection to the specified IP address and port.
#define uip_aborted()
Has the connection been aborted by the other end?
Definition: uip.h:781
#define NULL
The null pointer.
#define uip_poll()
Is the connection being polled by uIP?
Definition: uip.h:817
#define uip_connected()
Has the connection just been connected?
Definition: uip.h:761
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
#define uip_abort()
Abort the current connection.
Definition: uip.h:682
#define PSOCK_SEND_STR(psock, str)
Send a null-terminated string.
Definition: psock.h:197
#define PSOCK_INIT(psock, buffer, buffersize)
Initialize a protosocket.
Definition: psock.h:150
CCIF uint16_t uip_htons(uint16_t val)
Convert a 16-bit quantity from host byte order to network byte order.
Definition: uip6.c:2298
#define PSOCK_CLOSE(psock)
Close a protosocket.
Definition: psock.h:241
The representation of a protosocket.
Definition: psock.h:112
#define PSOCK_END(psock)
Declare the end of a protosocket&#39;s protothread.
Definition: psock.h:348
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition: process.h:141
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
#define PSOCK_EXIT(psock)
Exit the protosocket&#39;s protothread.
Definition: psock.h:320
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:75
#define uip_timedout()
Has the connection timed out?
Definition: uip.h:791
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99
#define uip_closed()
Has the connection been closed by the other end?
Definition: uip.h:771
#define uip_ipaddr(addr, addr0, addr1, addr2, addr3)
Construct an IP address from four bytes.
Definition: uip.h:955
#define UIP_TCP_MSS
The TCP maximum segment size.
Definition: uipopt.h:485
void process_exit(struct process *p)
Cause a process to exit.
Definition: process.c:202