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.)
Revision 0:e8e33b39c3cc, committed 2011-02-26
- Comitter:
- treebykooba
- Date:
- Sat Feb 26 20:07:01 2011 +0000
- Commit message:
Changed in this revision
TwitPic.cpp | Show annotated file Show diff for this revision Revisions of this file |
TwitPic.h | Show annotated file Show diff for this revision Revisions of this file |
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 +}
diff -r 000000000000 -r e8e33b39c3cc TwitPic.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TwitPic.h Sat Feb 26 20:07:01 2011 +0000 @@ -0,0 +1,52 @@ +/* + TwitPic.h - TwitPic Image Uploader for mbed + Copyright (c) novachild 2011. All rights 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. + + REQUIRES: EthernetNetIf, DNSResolver, some sort of file access (SD, local, etc) + +*/ + +#ifndef TwitPic_h +#define TwitPic_h + +#include "mbed.h" +#include "EthernetNetIf.h" +#include "TCPSocket.h" +#include "host.h" +#include "ipaddr.h" +#include "dnsresolve.h" + +class TwitPic { +public: + TwitPic(const char *twitpic_api_key, const char *consumer_key, const char *consumer_secret, const char *access_token, const char *access_token_secret); + int upload(const char *message, + FILE *img, + bool toPost = false); + + int uploadAndPost(const char *message, + FILE *imgFile); + +private: + Host server; + TCPSocket socket; + + + void handleTCPSocketEvents(TCPSocketEvent ev); + + void sendHeaders(); + void sendParameters(); + void sendImageData(); + void sendFooter(); + void sendNextChunk(); + + void getResponse(); + void flushResponse(); + +}; + + +#endif