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

TwitPic.cpp

Committer:
treebykooba
Date:
2011-02-26
Revision:
0:e8e33b39c3cc

File content as of revision 0:e8e33b39c3cc:

/*
  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
}