Use this library to upload images to twitpic.com. The library creates and uses its own TCPSocket. Requires EthernetNetIf and DNSResolver. (this library actually designed by a friend of mine, novachild, with no mbed license of his own.)

Dependents:   TwitPicExample

Revision:
0:e8e33b39c3cc
diff -r 000000000000 -r e8e33b39c3cc TwitPic.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TwitPic.cpp	Sat Feb 26 20:07:01 2011 +0000
@@ -0,0 +1,556 @@
+/*
+  TwitPic.cpp - TwitPic Image Uploader for mbed
+  Copyright (c) novachild 2011. All right reserved.
+
+  This library 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.
+*/
+
+#include "TwitPic.h"
+#include "string.h"
+
+#define MIN(a, b)  (a < b ? a : b)
+#define MAX(a, b)  (a > b ? a : b)
+
+#define SENDING_BUFFER_SIZE 4096 //must be large enough to hold all parameters: I wouldn't go under 2 KB.
+#define RECEIVING_BUFFER_SIZE 1024 //for holding respones (early ones will be discared, 1KB should do)
+#define SENDING_CHUNK_SIZE 1024 //amount to send over the socket at once.
+
+#define TWITPIC_URL                     "api.twitpic.com"
+#define TWITPIC_UPLOAD_API_URL            "/1/upload.xml"
+#define TWITPIC_UPLOAD_AND_POST_API_URL "/1/uploadAndPost.xml"
+
+#define BOUNDARY                        "----------0dcb31395642"
+#define HEADER                            "--" BOUNDARY
+#define FOOTER                            "--" BOUNDARY "--"
+
+#define DEBUG 1
+#define TRACE 1
+
+
+const char* m_twitpic_api_key; //twitpic API key
+const char* m_consumer_key; //Consumer key
+const char* m_consumer_secret; //Consumer secret
+const char* m_access_token; //Access Token
+const char* m_access_token_secret; //Access Token secret
+const char* twitterMessage;
+
+bool post = false;
+
+bool sentHeaders = false;
+bool sentParameters = false;
+bool sentImageData = false;
+bool sentFooter = false;
+bool finishedSending = false;
+
+bool positiveResponse = false;
+bool readyToReturn = false;
+bool stillPolling = false;
+
+char sendBuf[SENDING_BUFFER_SIZE];
+char recvBuf[RECEIVING_BUFFER_SIZE];
+
+int imgSize=0;
+int writing_position = 0;
+int errorCode = 0;
+int preCalcedContentLength=0;
+
+int totalSentData=0;
+
+FILE *imgFile;
+
+
+TwitPic::TwitPic(const char *twitpic_api_key, const char *consumer_key, const char *consumer_secret, const char *access_token, const char *access_token_secret) {
+
+    m_twitpic_api_key = twitpic_api_key;
+    m_consumer_key = consumer_key;
+    m_consumer_secret = consumer_secret;
+    m_access_token = access_token;
+    m_access_token_secret = access_token_secret;
+    socket.setOnEvent(this, &TwitPic::handleTCPSocketEvents);
+
+    preCalcedContentLength =  7 * (sizeof(HEADER)-1) +
+                              24* (sizeof("\r\n")-1) +
+                              6 * (sizeof("Content-Disposition: form-data; name=\"") - 1) +
+                              sizeof("key") +
+                              sizeof("consumer_token") +
+                              sizeof("consumer_secret") +
+                              sizeof("oauth_token")  +
+                              sizeof("oauth_secret")  +
+                              sizeof("message") +
+                              strlen(m_twitpic_api_key) +
+                              strlen(m_consumer_key) +
+                              strlen(m_consumer_secret) +
+                              strlen(m_access_token) +
+                              strlen(m_access_token_secret) +
+                              sizeof("\r\nContent-Disposition: form-data; name=\"media\"; filename=\"image.jpg\"\r\n")-1 +
+                              sizeof("Content-Type: image/jpeg\r\n")-1 +
+                              sizeof("Content-Transfer-Encoding: binary\r\n\r\n")-1 +
+                              sizeof("\r\n")-1 +
+                              sizeof(FOOTER) - 1 +
+                              sizeof("\r\n") - 1;
+                             
+}
+
+int TwitPic::upload(const char *message,
+                    FILE *img,
+                    bool toPost) {
+
+    if (TRACE) printf("-----------------------TRACE: upload\n");
+    post = toPost;
+    imgFile = img;
+    twitterMessage = message;
+
+    DNSResolver resolver;
+    server.setIp(resolver.resolveName("api.twitpic.com"));
+    server.setPort(80);
+
+    //Calculate image size
+    fseek (imgFile , 0 , SEEK_END);
+    imgSize = ftell (img);
+    rewind (imgFile);
+
+
+
+    sentHeaders = false;
+    sentParameters = false;
+    sentImageData = false;
+    sentFooter = false;
+    positiveResponse = false;
+    finishedSending = false;
+    readyToReturn = false;
+    writing_position = 0;
+
+    if (DEBUG) printf("Opening connection to api.twitpic.com\n");
+    TCPSocketErr err = socket.connect(server);
+    if (err < 0) {
+        if (DEBUG) printf("Error connecting socket: %i",err);
+        return err;
+    }
+
+    if (DEBUG) printf("Beginning network polling cycle.\n");
+    int countAfterSent = 0;
+    while (! readyToReturn) {
+        if (TRACE && !stillPolling) printf("-----------------------TRACE: Net::poll()\n");
+        stillPolling = true;
+        Net::poll();
+        if (finishedSending) countAfterSent++;
+        if (countAfterSent > 1000000) {
+            if (DEBUG) printf("Polled 1000000 times and got no response. Quitting.\n");
+            positiveResponse = false;
+            readyToReturn = true;
+        }
+    }
+
+    if (positiveResponse) {
+        printf("Image uploaded.\n");
+        return 0;
+    } else {
+        printf("Image not successfully uploaded\n");
+        return errorCode;
+    }
+
+}
+
+int TwitPic::uploadAndPost(const char *message,
+                           FILE *imgFile) {
+    return upload(message, imgFile, true);
+}
+
+void TwitPic::handleTCPSocketEvents(TCPSocketEvent ev) {
+    stillPolling = false;
+    if (TRACE) printf("-----------------------TRACE: handleTCPSocketEvent\n");
+    switch (ev) {
+        case TCPSOCKET_ERROR:
+            printf("-----TCPSocket: Unknown Error\n");
+            goto error;
+        case TCPSOCKET_CONABRT:
+            printf("-----TCPSocket: Connection aborted.\n");
+            goto error;
+        case TCPSOCKET_CONRST:
+            printf("-----TCPSocket: Connection reset.\n");
+            goto error;
+        case TCPSOCKET_CONTIMEOUT:
+            printf("-----TCPSocket: Connection timed out.\n");
+            goto error;
+        case TCPSOCKET_DISCONNECTED:
+            printf("-----TCPSocket: Connection disconnected.\n");
+error:
+            readyToReturn = true;
+            errorCode = ev;
+            positiveResponse = false;
+            break;
+        case TCPSOCKET_CONNECTED:
+            if (DEBUG) printf("-----TCPSocket: Connected.\r\n");
+            sendNextChunk();
+            break;
+        case TCPSOCKET_READABLE:
+            if (DEBUG) printf("-----TCPSocket: Readable.\r\n");
+            getResponse();
+            break;
+        case TCPSOCKET_WRITEABLE:
+            if (DEBUG) printf("-----TCPSOCKET: Writeable.\r\n");
+            if (writing_position < SENDING_BUFFER_SIZE) {
+                if (!sentHeaders) {
+                    sendHeaders();
+                } else if (!sentParameters) {
+                    sendParameters();
+                } else if (!sentImageData) {
+                    sendImageData();
+                } else if (!sentFooter) {
+                    sendFooter();
+                }
+            }
+            if (writing_position > 0) sendNextChunk();
+    }
+}
+
+void TwitPic::sendHeaders() {
+    char* positionPointer;
+    char imgSizeAsString[25];
+    int neededSize = 0;
+    int availableSize = 0;
+
+    if (TRACE) printf("-----------------------TRACE: sendHeaders\n");
+
+    sprintf(imgSizeAsString,"%i",preCalcedContentLength + +strlen(twitterMessage) + imgSize);
+
+    if (post) {
+        neededSize = sizeof(TWITPIC_UPLOAD_AND_POST_API_URL) -1;
+    } else {
+        neededSize = sizeof(TWITPIC_UPLOAD_API_URL)-1;
+    }
+    neededSize +=     sizeof("POST ") -1 +
+                      sizeof(" HTTP/1.0\r\n")-1 +
+                      sizeof("Content-Type: multipart/form-data; boundary=")-1 +
+                      sizeof(BOUNDARY)-1 +
+                      sizeof("\r\nContent-Length: ") - 1 +
+                      strlen(imgSizeAsString) +
+                      sizeof("\r\nExpect: 100-continue") -1 +
+                      sizeof("\r\nHost: api.twitpic.com") - 1 +
+                      sizeof("\r\n\r\n") - 1;
+
+    availableSize = SENDING_BUFFER_SIZE - writing_position;
+
+
+    //for this guy, only add to sendBuf if you can add everything
+    if (availableSize < neededSize) return;
+
+    positionPointer = sendBuf;
+    positionPointer += writing_position;
+    strcpy(positionPointer, "POST ");
+    positionPointer += 5;
+    if (post) {
+        strcpy(positionPointer, TWITPIC_UPLOAD_AND_POST_API_URL);
+        positionPointer += sizeof(TWITPIC_UPLOAD_AND_POST_API_URL)-1;
+    } else {
+        strcpy(positionPointer, TWITPIC_UPLOAD_API_URL);
+        positionPointer += sizeof(TWITPIC_UPLOAD_API_URL)-1;
+    }
+    strcpy(positionPointer, " HTTP/1.1\r\n");
+    positionPointer += 11;
+    strcpy(positionPointer, "Host: api.twitpic.com\r\n");
+    positionPointer+= sizeof("Host: api.twitpic.com\r\n")-1;
+    strcpy(positionPointer, "Content-Length: ");
+    positionPointer += sizeof("Content-Length: ") - 1;
+    strcpy(positionPointer, imgSizeAsString);
+    positionPointer += strlen(imgSizeAsString);
+    strcpy(positionPointer,"\r\nExpect: 100-continue");
+    positionPointer+=sizeof("\r\nExpect: 100-continue")-1;
+    strcpy(positionPointer, "\r\nContent-Type: multipart/form-data; boundary=");
+    positionPointer += sizeof("\r\nContent-Type: multipart/form-data; boundary=")-1;
+    strcpy(positionPointer, BOUNDARY);
+    positionPointer += sizeof(BOUNDARY)-1;
+    strcpy(positionPointer, "\r\n\r\n");
+    positionPointer += 4;
+
+    writing_position += neededSize;
+    if (DEBUG) printf("Wrote headers to buffer. Size = %i\n. Image size reported: %s\n",neededSize, imgSizeAsString);
+    sentHeaders = true;
+}
+
+void TwitPic::sendParameters() {
+    char* positionPointer;
+    int neededSize = 0;
+    int availableSize = 0;
+    int actualsize = 0;
+    char headerline[] = "Content-Disposition: form-data; name=\"";
+
+    if (TRACE) printf("-----------------------TRACE: sendParameters\n");
+    neededSize = 7 * (sizeof(HEADER)-1) +
+                 24* (sizeof("\r\n")-1) +
+                 6 * (sizeof(headerline) - 1) +
+                 sizeof("key") +
+                 sizeof("consumer_token") +
+                 sizeof("consumer_secret") +
+                 sizeof("oauth_token") +
+                 sizeof("oauth_secret") +
+                 sizeof("message") +
+                 strlen(m_twitpic_api_key) +
+                 strlen(m_consumer_key) +
+                 strlen(m_consumer_secret) +
+                 strlen(m_access_token) +
+                 strlen(m_access_token_secret) +
+                 strlen(twitterMessage) +
+                 sizeof("\r\nContent-Disposition: form-data; name=\"media\"; filename=\"image.jpg\"\r\n")-1 +
+                 sizeof("Content-Type: image/jpeg\r\n")-1 +
+                 sizeof("Content-Transfer-Encoding: binary\r\n\r\n")-1;
+
+
+    availableSize = SENDING_BUFFER_SIZE - writing_position;
+
+
+    //for this guy, only add to sendBuf if you can add everything
+    if (availableSize < neededSize) return;
+
+    positionPointer = sendBuf;
+    positionPointer+=writing_position;
+
+    //API Key
+    strcpy(positionPointer, HEADER);
+    positionPointer += sizeof(HEADER)-1;
+    actualsize += sizeof(HEADER) -1;
+    strcpy(positionPointer, "\r\n");
+    positionPointer += 2;
+    actualsize += 2;
+
+    strcpy(positionPointer, headerline);
+    positionPointer += strlen(headerline);
+    actualsize += strlen(headerline);
+    strcpy(positionPointer, "key\"\r\n\r\n");
+    positionPointer += sizeof("key\"\r\n\r\n")-1;
+    actualsize += sizeof("key\"\r\n\r\n")-1;
+    strcpy(positionPointer, m_twitpic_api_key);
+    positionPointer += strlen(m_twitpic_api_key);
+    actualsize +=strlen(m_twitpic_api_key);
+    strcpy(positionPointer, "\r\n");
+    positionPointer+=2;
+    actualsize +=2;
+    //Consumer Key
+    strcpy(positionPointer, HEADER);
+    positionPointer += sizeof(HEADER)-1;
+    actualsize += sizeof(HEADER) -1;
+    strcpy(positionPointer, "\r\n");
+    positionPointer += 2;
+    actualsize += 2;
+
+    strcpy(positionPointer, headerline);
+    positionPointer += strlen(headerline);
+    actualsize += strlen(headerline);
+    strcpy(positionPointer, "consumer_token\"\r\n\r\n");
+    positionPointer += sizeof("consumer_token\"\r\n\r\n")-1;
+    actualsize += sizeof("consumer_token\"\r\n\r\n")-1;
+    strcpy(positionPointer, m_consumer_key);
+    positionPointer += strlen(m_consumer_key);
+    actualsize += strlen(m_consumer_key);
+    strcpy(positionPointer, "\r\n");
+    positionPointer+=2;
+    actualsize+=2;
+
+    //Consumer Secret
+    strcpy(positionPointer, HEADER);
+    positionPointer += sizeof(HEADER)-1;
+    actualsize += sizeof(HEADER) -1;
+    strcpy(positionPointer, "\r\n");
+    positionPointer += 2;
+    actualsize += 2;
+
+    strcpy(positionPointer, headerline);
+    positionPointer += strlen(headerline);
+    actualsize += strlen(headerline);
+    strcpy(positionPointer, "consumer_secret\"\r\n\r\n");
+    positionPointer += sizeof("consumer_secret\"\r\n\r\n")-1;
+    actualsize +=sizeof("consumer_secret\"\r\n\r\n")-1;
+    strcpy(positionPointer, m_consumer_secret);
+    positionPointer += strlen(m_consumer_secret);
+    actualsize += strlen(m_consumer_secret);
+    strcpy(positionPointer, "\r\n");
+    positionPointer+=2;
+    actualsize +=2;
+
+    //OAuth Token
+    strcpy(positionPointer, HEADER);
+    positionPointer += sizeof(HEADER)-1;
+    actualsize += sizeof(HEADER) -1;
+    strcpy(positionPointer, "\r\n");
+    positionPointer += 2;
+    actualsize += 2;
+
+    strcpy(positionPointer, headerline);
+    positionPointer += strlen(headerline);
+    actualsize += strlen(headerline);
+    strcpy(positionPointer, "oauth_token\"\r\n\r\n");
+    positionPointer += sizeof("oauth_token\"\r\n\r\n")-1;
+    actualsize += sizeof("oauth_token\"\r\n\r\n")-1;
+    strcpy(positionPointer, m_access_token);
+    positionPointer += strlen(m_access_token);
+    actualsize += strlen(m_access_token);
+    strcpy(positionPointer, "\r\n");
+    positionPointer+=2;
+    actualsize += 2;
+
+    //OAuth Secret
+    strcpy(positionPointer, HEADER);
+    positionPointer += sizeof(HEADER)-1;
+    actualsize += sizeof(HEADER) -1;
+    strcpy(positionPointer, "\r\n");
+    positionPointer += 2;
+    actualsize += 2;
+
+    strcpy(positionPointer, headerline);
+    positionPointer += strlen(headerline);
+    actualsize += strlen(headerline);
+    strcpy(positionPointer, "oauth_secret\"\r\n\r\n");
+    positionPointer += sizeof("oauth_secret\"\r\n\r\n")-1;
+    actualsize += sizeof("oauth_secret\"\r\n\r\n")-1;
+    strcpy(positionPointer, m_access_token_secret);
+    positionPointer += strlen(m_access_token_secret);
+    actualsize += strlen(m_access_token_secret);
+    strcpy(positionPointer, "\r\n");
+    positionPointer+=2;
+    actualsize +=2;
+
+    //Twitter Message
+    strcpy(positionPointer, HEADER);
+    positionPointer += sizeof(HEADER)-1;
+    actualsize += sizeof(HEADER) -1;
+    strcpy(positionPointer, "\r\n");
+    positionPointer += 2;
+    actualsize += 2;
+
+    strcpy(positionPointer, headerline);
+    positionPointer += strlen(headerline);
+    actualsize += strlen(headerline);
+    strcpy(positionPointer, "message\"\r\n\r\n");
+    positionPointer += sizeof("message\"\r\n\r\n")-1;
+    actualsize += sizeof("message\"\r\n\r\n")-1;
+    strcpy(positionPointer, twitterMessage);
+    positionPointer += strlen(twitterMessage);
+    actualsize += strlen(twitterMessage);
+    strcpy(positionPointer, "\r\n");
+    positionPointer+=2;
+    actualsize += 2;
+
+    strcpy(positionPointer,HEADER);
+    positionPointer += sizeof(HEADER)-1;
+    actualsize += sizeof(HEADER)-1;
+    strcpy(positionPointer,"\r\nContent-Disposition: form-data; name=\"media\"; filename=\"image.jpg\"\r\n");
+    positionPointer += sizeof("\r\nContent-Disposition: form-data; name=\"media\"; filename=\"image.jpg\"\r\n")-1;
+    actualsize += sizeof("\r\nContent-Disposition: form-data; name=\"media\"; filename=\"image.jpg\"\r\n")-1;
+    strcpy(positionPointer,"Content-Type: image/jpeg\r\n");
+    positionPointer += sizeof("Content-Type: image/jpeg\r\n")-1;
+    actualsize += sizeof("Content-Type: image/jpeg\r\n")-1;
+    strcpy(positionPointer,"Content-Transfer-Encoding: binary\r\n\r\n");
+    positionPointer += sizeof("Content-Transfer-Encoding: binary\r\n\r\n")-1;
+    actualsize+= sizeof("Content-Transfer-Encoding: binary\r\n\r\n")-1;
+
+    if (actualsize != neededSize) printf("ERROR in parameters: Calculated size: %i. Actual size: %i.\n", neededSize,actualsize);
+    writing_position += actualsize;
+    if (DEBUG) printf("Wrote parameters to buffer.\n");
+    sentParameters = true;
+
+}
+void TwitPic::sendImageData() {
+    int availableSize;
+    int amountRead=0;
+    char* positionPointer;
+
+    if (TRACE) printf("-----------------------TRACE: sendImageData\n");
+    
+    if (writing_position > 0) return;
+    //fseek(imgFile,-2000,SEEK_END);
+    positionPointer = sendBuf;
+    positionPointer += writing_position;
+    availableSize = SENDING_BUFFER_SIZE - writing_position;
+    amountRead = fread(positionPointer, 1, availableSize, imgFile);
+    writing_position += amountRead;
+    if (DEBUG) printf("Wrote %i bytes of image to buffer.\n",amountRead);
+    if (feof(imgFile)) {
+        if (DEBUG) printf("Finished writing file to buffer.\n");
+        sentImageData = true;
+    }
+}
+void TwitPic::sendFooter() {
+    int availableSize;
+    int neededSize;
+    char* positionPointer;
+    
+    if (TRACE) printf("-----------------------TRACE: sendFooter\n");
+    
+    availableSize = SENDING_BUFFER_SIZE - writing_position;
+    neededSize = sizeof("\r\n")-1 +
+                 sizeof(FOOTER) - 1 +
+                 sizeof("\r\n") - 1;
+    if (availableSize < neededSize) return;
+
+    positionPointer = sendBuf;
+    positionPointer += writing_position;
+    strcpy(positionPointer,"\r\n");
+    positionPointer+=2;
+    strcpy(positionPointer, FOOTER);
+    positionPointer+=sizeof(FOOTER)-1;
+    strcpy(positionPointer,"\r\n");
+    positionPointer+=2;
+
+    writing_position+=neededSize;
+    if (DEBUG) printf("Wrote footer to buffer.\n");
+    sentFooter=true;
+}
+void TwitPic::sendNextChunk() {
+    int amountToSend;
+    int amountSent;
+    char* bufPointer;
+    if (TRACE) printf("-----------------------TRACE: sendNextChunk\n");
+    //TODO: try both ways of doint this: looped and not.
+    do {
+        if (writing_position == 0) {
+            if (!sentHeaders) {
+                sendHeaders();
+            } else if (!sentParameters) {
+                sendParameters();
+            } else if (!sentImageData) {
+                sendImageData();
+            } else if (!sentFooter) {
+                sendFooter();
+            }
+        }
+        amountToSend = MIN(writing_position,SENDING_CHUNK_SIZE);
+        if (sentFooter && amountToSend == 0) {
+            finishedSending = true;
+            //getResponse();
+        }
+
+        amountSent = socket.send(sendBuf, amountToSend);
+        totalSentData += amountSent;
+
+        if (DEBUG) printf("---Total sent data: %i bytes\n",totalSentData);
+
+        bufPointer = sendBuf + amountSent;
+        memmove((void*)sendBuf, (void*)bufPointer, writing_position - amountSent);
+        writing_position -= amountSent;
+    } while (!finishedSending && amountSent == amountToSend); //if sent less than intended, stop sending until WRITEABLE.
+}
+void TwitPic::getResponse() {
+    int amountReceived;
+    char* okSpot;
+
+    if (TRACE) printf("-----------------------TRACE: getResponse\n");
+    if (DEBUG) printf("Checking for response\n");
+    amountReceived = socket.recv(recvBuf, RECEIVING_BUFFER_SIZE-1);
+    recvBuf[amountReceived]='\0';
+    if (amountReceived > 0 && DEBUG) printf("Received response: %s\n",recvBuf);
+
+    okSpot = strstr(recvBuf,"200 OK");
+    if (okSpot != NULL) {
+        positiveResponse = true;
+        readyToReturn = true;
+    } else if (finishedSending && amountReceived > 0) {
+        positiveResponse = false;
+        readyToReturn = true;
+    }
+}
+
+void TwitPic::flushResponse() {
+//no longer needed
+}