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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers TwitPic.cpp Source File

TwitPic.cpp

00001 /*
00002   TwitPic.cpp - TwitPic Image Uploader for mbed
00003   Copyright (c) novachild 2011. All right reserved.
00004 
00005   This library is distributed in the hope that it will be useful,
00006   but WITHOUT ANY WARRANTY; without even the implied warranty of
00007   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00008 */
00009 
00010 #include "TwitPic.h"
00011 #include "string.h"
00012 
00013 #define MIN(a, b)  (a < b ? a : b)
00014 #define MAX(a, b)  (a > b ? a : b)
00015 
00016 #define SENDING_BUFFER_SIZE 4096 //must be large enough to hold all parameters: I wouldn't go under 2 KB.
00017 #define RECEIVING_BUFFER_SIZE 1024 //for holding respones (early ones will be discared, 1KB should do)
00018 #define SENDING_CHUNK_SIZE 1024 //amount to send over the socket at once.
00019 
00020 #define TWITPIC_URL                     "api.twitpic.com"
00021 #define TWITPIC_UPLOAD_API_URL            "/1/upload.xml"
00022 #define TWITPIC_UPLOAD_AND_POST_API_URL "/1/uploadAndPost.xml"
00023 
00024 #define BOUNDARY                        "----------0dcb31395642"
00025 #define HEADER                            "--" BOUNDARY
00026 #define FOOTER                            "--" BOUNDARY "--"
00027 
00028 #define DEBUG 1
00029 #define TRACE 1
00030 
00031 
00032 const char* m_twitpic_api_key; //twitpic API key
00033 const char* m_consumer_key; //Consumer key
00034 const char* m_consumer_secret; //Consumer secret
00035 const char* m_access_token; //Access Token
00036 const char* m_access_token_secret; //Access Token secret
00037 const char* twitterMessage;
00038 
00039 bool post = false;
00040 
00041 bool sentHeaders = false;
00042 bool sentParameters = false;
00043 bool sentImageData = false;
00044 bool sentFooter = false;
00045 bool finishedSending = false;
00046 
00047 bool positiveResponse = false;
00048 bool readyToReturn = false;
00049 bool stillPolling = false;
00050 
00051 char sendBuf[SENDING_BUFFER_SIZE];
00052 char recvBuf[RECEIVING_BUFFER_SIZE];
00053 
00054 int imgSize=0;
00055 int writing_position = 0;
00056 int errorCode = 0;
00057 int preCalcedContentLength=0;
00058 
00059 int totalSentData=0;
00060 
00061 FILE *imgFile;
00062 
00063 
00064 TwitPic::TwitPic(const char *twitpic_api_key, const char *consumer_key, const char *consumer_secret, const char *access_token, const char *access_token_secret) {
00065 
00066     m_twitpic_api_key = twitpic_api_key;
00067     m_consumer_key = consumer_key;
00068     m_consumer_secret = consumer_secret;
00069     m_access_token = access_token;
00070     m_access_token_secret = access_token_secret;
00071     socket.setOnEvent(this, &TwitPic::handleTCPSocketEvents);
00072 
00073     preCalcedContentLength =  7 * (sizeof(HEADER)-1) +
00074                               24* (sizeof("\r\n")-1) +
00075                               6 * (sizeof("Content-Disposition: form-data; name=\"") - 1) +
00076                               sizeof("key") +
00077                               sizeof("consumer_token") +
00078                               sizeof("consumer_secret") +
00079                               sizeof("oauth_token")  +
00080                               sizeof("oauth_secret")  +
00081                               sizeof("message") +
00082                               strlen(m_twitpic_api_key) +
00083                               strlen(m_consumer_key) +
00084                               strlen(m_consumer_secret) +
00085                               strlen(m_access_token) +
00086                               strlen(m_access_token_secret) +
00087                               sizeof("\r\nContent-Disposition: form-data; name=\"media\"; filename=\"image.jpg\"\r\n")-1 +
00088                               sizeof("Content-Type: image/jpeg\r\n")-1 +
00089                               sizeof("Content-Transfer-Encoding: binary\r\n\r\n")-1 +
00090                               sizeof("\r\n")-1 +
00091                               sizeof(FOOTER) - 1 +
00092                               sizeof("\r\n") - 1;
00093                              
00094 }
00095 
00096 int TwitPic::upload(const char *message,
00097                     FILE *img,
00098                     bool toPost) {
00099 
00100     if (TRACE) printf("-----------------------TRACE: upload\n");
00101     post = toPost;
00102     imgFile = img;
00103     twitterMessage = message;
00104 
00105     DNSResolver resolver;
00106     server.setIp(resolver.resolveName("api.twitpic.com"));
00107     server.setPort(80);
00108 
00109     //Calculate image size
00110     fseek (imgFile , 0 , SEEK_END);
00111     imgSize = ftell (img);
00112     rewind (imgFile);
00113 
00114 
00115 
00116     sentHeaders = false;
00117     sentParameters = false;
00118     sentImageData = false;
00119     sentFooter = false;
00120     positiveResponse = false;
00121     finishedSending = false;
00122     readyToReturn = false;
00123     writing_position = 0;
00124 
00125     if (DEBUG) printf("Opening connection to api.twitpic.com\n");
00126     TCPSocketErr err = socket.connect(server);
00127     if (err < 0) {
00128         if (DEBUG) printf("Error connecting socket: %i",err);
00129         return err;
00130     }
00131 
00132     if (DEBUG) printf("Beginning network polling cycle.\n");
00133     int countAfterSent = 0;
00134     while (! readyToReturn) {
00135         if (TRACE && !stillPolling) printf("-----------------------TRACE: Net::poll()\n");
00136         stillPolling = true;
00137         Net::poll();
00138         if (finishedSending) countAfterSent++;
00139         if (countAfterSent > 1000000) {
00140             if (DEBUG) printf("Polled 1000000 times and got no response. Quitting.\n");
00141             positiveResponse = false;
00142             readyToReturn = true;
00143         }
00144     }
00145 
00146     if (positiveResponse) {
00147         printf("Image uploaded.\n");
00148         return 0;
00149     } else {
00150         printf("Image not successfully uploaded\n");
00151         return errorCode;
00152     }
00153 
00154 }
00155 
00156 int TwitPic::uploadAndPost(const char *message,
00157                            FILE *imgFile) {
00158     return upload(message, imgFile, true);
00159 }
00160 
00161 void TwitPic::handleTCPSocketEvents(TCPSocketEvent ev) {
00162     stillPolling = false;
00163     if (TRACE) printf("-----------------------TRACE: handleTCPSocketEvent\n");
00164     switch (ev) {
00165         case TCPSOCKET_ERROR:
00166             printf("-----TCPSocket: Unknown Error\n");
00167             goto error;
00168         case TCPSOCKET_CONABRT:
00169             printf("-----TCPSocket: Connection aborted.\n");
00170             goto error;
00171         case TCPSOCKET_CONRST:
00172             printf("-----TCPSocket: Connection reset.\n");
00173             goto error;
00174         case TCPSOCKET_CONTIMEOUT:
00175             printf("-----TCPSocket: Connection timed out.\n");
00176             goto error;
00177         case TCPSOCKET_DISCONNECTED:
00178             printf("-----TCPSocket: Connection disconnected.\n");
00179 error:
00180             readyToReturn = true;
00181             errorCode = ev;
00182             positiveResponse = false;
00183             break;
00184         case TCPSOCKET_CONNECTED:
00185             if (DEBUG) printf("-----TCPSocket: Connected.\r\n");
00186             sendNextChunk();
00187             break;
00188         case TCPSOCKET_READABLE:
00189             if (DEBUG) printf("-----TCPSocket: Readable.\r\n");
00190             getResponse();
00191             break;
00192         case TCPSOCKET_WRITEABLE:
00193             if (DEBUG) printf("-----TCPSOCKET: Writeable.\r\n");
00194             if (writing_position < SENDING_BUFFER_SIZE) {
00195                 if (!sentHeaders) {
00196                     sendHeaders();
00197                 } else if (!sentParameters) {
00198                     sendParameters();
00199                 } else if (!sentImageData) {
00200                     sendImageData();
00201                 } else if (!sentFooter) {
00202                     sendFooter();
00203                 }
00204             }
00205             if (writing_position > 0) sendNextChunk();
00206     }
00207 }
00208 
00209 void TwitPic::sendHeaders() {
00210     char* positionPointer;
00211     char imgSizeAsString[25];
00212     int neededSize = 0;
00213     int availableSize = 0;
00214 
00215     if (TRACE) printf("-----------------------TRACE: sendHeaders\n");
00216 
00217     sprintf(imgSizeAsString,"%i",preCalcedContentLength + +strlen(twitterMessage) + imgSize);
00218 
00219     if (post) {
00220         neededSize = sizeof(TWITPIC_UPLOAD_AND_POST_API_URL) -1;
00221     } else {
00222         neededSize = sizeof(TWITPIC_UPLOAD_API_URL)-1;
00223     }
00224     neededSize +=     sizeof("POST ") -1 +
00225                       sizeof(" HTTP/1.0\r\n")-1 +
00226                       sizeof("Content-Type: multipart/form-data; boundary=")-1 +
00227                       sizeof(BOUNDARY)-1 +
00228                       sizeof("\r\nContent-Length: ") - 1 +
00229                       strlen(imgSizeAsString) +
00230                       sizeof("\r\nExpect: 100-continue") -1 +
00231                       sizeof("\r\nHost: api.twitpic.com") - 1 +
00232                       sizeof("\r\n\r\n") - 1;
00233 
00234     availableSize = SENDING_BUFFER_SIZE - writing_position;
00235 
00236 
00237     //for this guy, only add to sendBuf if you can add everything
00238     if (availableSize < neededSize) return;
00239 
00240     positionPointer = sendBuf;
00241     positionPointer += writing_position;
00242     strcpy(positionPointer, "POST ");
00243     positionPointer += 5;
00244     if (post) {
00245         strcpy(positionPointer, TWITPIC_UPLOAD_AND_POST_API_URL);
00246         positionPointer += sizeof(TWITPIC_UPLOAD_AND_POST_API_URL)-1;
00247     } else {
00248         strcpy(positionPointer, TWITPIC_UPLOAD_API_URL);
00249         positionPointer += sizeof(TWITPIC_UPLOAD_API_URL)-1;
00250     }
00251     strcpy(positionPointer, " HTTP/1.1\r\n");
00252     positionPointer += 11;
00253     strcpy(positionPointer, "Host: api.twitpic.com\r\n");
00254     positionPointer+= sizeof("Host: api.twitpic.com\r\n")-1;
00255     strcpy(positionPointer, "Content-Length: ");
00256     positionPointer += sizeof("Content-Length: ") - 1;
00257     strcpy(positionPointer, imgSizeAsString);
00258     positionPointer += strlen(imgSizeAsString);
00259     strcpy(positionPointer,"\r\nExpect: 100-continue");
00260     positionPointer+=sizeof("\r\nExpect: 100-continue")-1;
00261     strcpy(positionPointer, "\r\nContent-Type: multipart/form-data; boundary=");
00262     positionPointer += sizeof("\r\nContent-Type: multipart/form-data; boundary=")-1;
00263     strcpy(positionPointer, BOUNDARY);
00264     positionPointer += sizeof(BOUNDARY)-1;
00265     strcpy(positionPointer, "\r\n\r\n");
00266     positionPointer += 4;
00267 
00268     writing_position += neededSize;
00269     if (DEBUG) printf("Wrote headers to buffer. Size = %i\n. Image size reported: %s\n",neededSize, imgSizeAsString);
00270     sentHeaders = true;
00271 }
00272 
00273 void TwitPic::sendParameters() {
00274     char* positionPointer;
00275     int neededSize = 0;
00276     int availableSize = 0;
00277     int actualsize = 0;
00278     char headerline[] = "Content-Disposition: form-data; name=\"";
00279 
00280     if (TRACE) printf("-----------------------TRACE: sendParameters\n");
00281     neededSize = 7 * (sizeof(HEADER)-1) +
00282                  24* (sizeof("\r\n")-1) +
00283                  6 * (sizeof(headerline) - 1) +
00284                  sizeof("key") +
00285                  sizeof("consumer_token") +
00286                  sizeof("consumer_secret") +
00287                  sizeof("oauth_token") +
00288                  sizeof("oauth_secret") +
00289                  sizeof("message") +
00290                  strlen(m_twitpic_api_key) +
00291                  strlen(m_consumer_key) +
00292                  strlen(m_consumer_secret) +
00293                  strlen(m_access_token) +
00294                  strlen(m_access_token_secret) +
00295                  strlen(twitterMessage) +
00296                  sizeof("\r\nContent-Disposition: form-data; name=\"media\"; filename=\"image.jpg\"\r\n")-1 +
00297                  sizeof("Content-Type: image/jpeg\r\n")-1 +
00298                  sizeof("Content-Transfer-Encoding: binary\r\n\r\n")-1;
00299 
00300 
00301     availableSize = SENDING_BUFFER_SIZE - writing_position;
00302 
00303 
00304     //for this guy, only add to sendBuf if you can add everything
00305     if (availableSize < neededSize) return;
00306 
00307     positionPointer = sendBuf;
00308     positionPointer+=writing_position;
00309 
00310     //API Key
00311     strcpy(positionPointer, HEADER);
00312     positionPointer += sizeof(HEADER)-1;
00313     actualsize += sizeof(HEADER) -1;
00314     strcpy(positionPointer, "\r\n");
00315     positionPointer += 2;
00316     actualsize += 2;
00317 
00318     strcpy(positionPointer, headerline);
00319     positionPointer += strlen(headerline);
00320     actualsize += strlen(headerline);
00321     strcpy(positionPointer, "key\"\r\n\r\n");
00322     positionPointer += sizeof("key\"\r\n\r\n")-1;
00323     actualsize += sizeof("key\"\r\n\r\n")-1;
00324     strcpy(positionPointer, m_twitpic_api_key);
00325     positionPointer += strlen(m_twitpic_api_key);
00326     actualsize +=strlen(m_twitpic_api_key);
00327     strcpy(positionPointer, "\r\n");
00328     positionPointer+=2;
00329     actualsize +=2;
00330     //Consumer Key
00331     strcpy(positionPointer, HEADER);
00332     positionPointer += sizeof(HEADER)-1;
00333     actualsize += sizeof(HEADER) -1;
00334     strcpy(positionPointer, "\r\n");
00335     positionPointer += 2;
00336     actualsize += 2;
00337 
00338     strcpy(positionPointer, headerline);
00339     positionPointer += strlen(headerline);
00340     actualsize += strlen(headerline);
00341     strcpy(positionPointer, "consumer_token\"\r\n\r\n");
00342     positionPointer += sizeof("consumer_token\"\r\n\r\n")-1;
00343     actualsize += sizeof("consumer_token\"\r\n\r\n")-1;
00344     strcpy(positionPointer, m_consumer_key);
00345     positionPointer += strlen(m_consumer_key);
00346     actualsize += strlen(m_consumer_key);
00347     strcpy(positionPointer, "\r\n");
00348     positionPointer+=2;
00349     actualsize+=2;
00350 
00351     //Consumer Secret
00352     strcpy(positionPointer, HEADER);
00353     positionPointer += sizeof(HEADER)-1;
00354     actualsize += sizeof(HEADER) -1;
00355     strcpy(positionPointer, "\r\n");
00356     positionPointer += 2;
00357     actualsize += 2;
00358 
00359     strcpy(positionPointer, headerline);
00360     positionPointer += strlen(headerline);
00361     actualsize += strlen(headerline);
00362     strcpy(positionPointer, "consumer_secret\"\r\n\r\n");
00363     positionPointer += sizeof("consumer_secret\"\r\n\r\n")-1;
00364     actualsize +=sizeof("consumer_secret\"\r\n\r\n")-1;
00365     strcpy(positionPointer, m_consumer_secret);
00366     positionPointer += strlen(m_consumer_secret);
00367     actualsize += strlen(m_consumer_secret);
00368     strcpy(positionPointer, "\r\n");
00369     positionPointer+=2;
00370     actualsize +=2;
00371 
00372     //OAuth Token
00373     strcpy(positionPointer, HEADER);
00374     positionPointer += sizeof(HEADER)-1;
00375     actualsize += sizeof(HEADER) -1;
00376     strcpy(positionPointer, "\r\n");
00377     positionPointer += 2;
00378     actualsize += 2;
00379 
00380     strcpy(positionPointer, headerline);
00381     positionPointer += strlen(headerline);
00382     actualsize += strlen(headerline);
00383     strcpy(positionPointer, "oauth_token\"\r\n\r\n");
00384     positionPointer += sizeof("oauth_token\"\r\n\r\n")-1;
00385     actualsize += sizeof("oauth_token\"\r\n\r\n")-1;
00386     strcpy(positionPointer, m_access_token);
00387     positionPointer += strlen(m_access_token);
00388     actualsize += strlen(m_access_token);
00389     strcpy(positionPointer, "\r\n");
00390     positionPointer+=2;
00391     actualsize += 2;
00392 
00393     //OAuth Secret
00394     strcpy(positionPointer, HEADER);
00395     positionPointer += sizeof(HEADER)-1;
00396     actualsize += sizeof(HEADER) -1;
00397     strcpy(positionPointer, "\r\n");
00398     positionPointer += 2;
00399     actualsize += 2;
00400 
00401     strcpy(positionPointer, headerline);
00402     positionPointer += strlen(headerline);
00403     actualsize += strlen(headerline);
00404     strcpy(positionPointer, "oauth_secret\"\r\n\r\n");
00405     positionPointer += sizeof("oauth_secret\"\r\n\r\n")-1;
00406     actualsize += sizeof("oauth_secret\"\r\n\r\n")-1;
00407     strcpy(positionPointer, m_access_token_secret);
00408     positionPointer += strlen(m_access_token_secret);
00409     actualsize += strlen(m_access_token_secret);
00410     strcpy(positionPointer, "\r\n");
00411     positionPointer+=2;
00412     actualsize +=2;
00413 
00414     //Twitter Message
00415     strcpy(positionPointer, HEADER);
00416     positionPointer += sizeof(HEADER)-1;
00417     actualsize += sizeof(HEADER) -1;
00418     strcpy(positionPointer, "\r\n");
00419     positionPointer += 2;
00420     actualsize += 2;
00421 
00422     strcpy(positionPointer, headerline);
00423     positionPointer += strlen(headerline);
00424     actualsize += strlen(headerline);
00425     strcpy(positionPointer, "message\"\r\n\r\n");
00426     positionPointer += sizeof("message\"\r\n\r\n")-1;
00427     actualsize += sizeof("message\"\r\n\r\n")-1;
00428     strcpy(positionPointer, twitterMessage);
00429     positionPointer += strlen(twitterMessage);
00430     actualsize += strlen(twitterMessage);
00431     strcpy(positionPointer, "\r\n");
00432     positionPointer+=2;
00433     actualsize += 2;
00434 
00435     strcpy(positionPointer,HEADER);
00436     positionPointer += sizeof(HEADER)-1;
00437     actualsize += sizeof(HEADER)-1;
00438     strcpy(positionPointer,"\r\nContent-Disposition: form-data; name=\"media\"; filename=\"image.jpg\"\r\n");
00439     positionPointer += sizeof("\r\nContent-Disposition: form-data; name=\"media\"; filename=\"image.jpg\"\r\n")-1;
00440     actualsize += sizeof("\r\nContent-Disposition: form-data; name=\"media\"; filename=\"image.jpg\"\r\n")-1;
00441     strcpy(positionPointer,"Content-Type: image/jpeg\r\n");
00442     positionPointer += sizeof("Content-Type: image/jpeg\r\n")-1;
00443     actualsize += sizeof("Content-Type: image/jpeg\r\n")-1;
00444     strcpy(positionPointer,"Content-Transfer-Encoding: binary\r\n\r\n");
00445     positionPointer += sizeof("Content-Transfer-Encoding: binary\r\n\r\n")-1;
00446     actualsize+= sizeof("Content-Transfer-Encoding: binary\r\n\r\n")-1;
00447 
00448     if (actualsize != neededSize) printf("ERROR in parameters: Calculated size: %i. Actual size: %i.\n", neededSize,actualsize);
00449     writing_position += actualsize;
00450     if (DEBUG) printf("Wrote parameters to buffer.\n");
00451     sentParameters = true;
00452 
00453 }
00454 void TwitPic::sendImageData() {
00455     int availableSize;
00456     int amountRead=0;
00457     char* positionPointer;
00458 
00459     if (TRACE) printf("-----------------------TRACE: sendImageData\n");
00460     
00461     if (writing_position > 0) return;
00462     //fseek(imgFile,-2000,SEEK_END);
00463     positionPointer = sendBuf;
00464     positionPointer += writing_position;
00465     availableSize = SENDING_BUFFER_SIZE - writing_position;
00466     amountRead = fread(positionPointer, 1, availableSize, imgFile);
00467     writing_position += amountRead;
00468     if (DEBUG) printf("Wrote %i bytes of image to buffer.\n",amountRead);
00469     if (feof(imgFile)) {
00470         if (DEBUG) printf("Finished writing file to buffer.\n");
00471         sentImageData = true;
00472     }
00473 }
00474 void TwitPic::sendFooter() {
00475     int availableSize;
00476     int neededSize;
00477     char* positionPointer;
00478     
00479     if (TRACE) printf("-----------------------TRACE: sendFooter\n");
00480     
00481     availableSize = SENDING_BUFFER_SIZE - writing_position;
00482     neededSize = sizeof("\r\n")-1 +
00483                  sizeof(FOOTER) - 1 +
00484                  sizeof("\r\n") - 1;
00485     if (availableSize < neededSize) return;
00486 
00487     positionPointer = sendBuf;
00488     positionPointer += writing_position;
00489     strcpy(positionPointer,"\r\n");
00490     positionPointer+=2;
00491     strcpy(positionPointer, FOOTER);
00492     positionPointer+=sizeof(FOOTER)-1;
00493     strcpy(positionPointer,"\r\n");
00494     positionPointer+=2;
00495 
00496     writing_position+=neededSize;
00497     if (DEBUG) printf("Wrote footer to buffer.\n");
00498     sentFooter=true;
00499 }
00500 void TwitPic::sendNextChunk() {
00501     int amountToSend;
00502     int amountSent;
00503     char* bufPointer;
00504     if (TRACE) printf("-----------------------TRACE: sendNextChunk\n");
00505     //TODO: try both ways of doint this: looped and not.
00506     do {
00507         if (writing_position == 0) {
00508             if (!sentHeaders) {
00509                 sendHeaders();
00510             } else if (!sentParameters) {
00511                 sendParameters();
00512             } else if (!sentImageData) {
00513                 sendImageData();
00514             } else if (!sentFooter) {
00515                 sendFooter();
00516             }
00517         }
00518         amountToSend = MIN(writing_position,SENDING_CHUNK_SIZE);
00519         if (sentFooter && amountToSend == 0) {
00520             finishedSending = true;
00521             //getResponse();
00522         }
00523 
00524         amountSent = socket.send(sendBuf, amountToSend);
00525         totalSentData += amountSent;
00526 
00527         if (DEBUG) printf("---Total sent data: %i bytes\n",totalSentData);
00528 
00529         bufPointer = sendBuf + amountSent;
00530         memmove((void*)sendBuf, (void*)bufPointer, writing_position - amountSent);
00531         writing_position -= amountSent;
00532     } while (!finishedSending && amountSent == amountToSend); //if sent less than intended, stop sending until WRITEABLE.
00533 }
00534 void TwitPic::getResponse() {
00535     int amountReceived;
00536     char* okSpot;
00537 
00538     if (TRACE) printf("-----------------------TRACE: getResponse\n");
00539     if (DEBUG) printf("Checking for response\n");
00540     amountReceived = socket.recv(recvBuf, RECEIVING_BUFFER_SIZE-1);
00541     recvBuf[amountReceived]='\0';
00542     if (amountReceived > 0 && DEBUG) printf("Received response: %s\n",recvBuf);
00543 
00544     okSpot = strstr(recvBuf,"200 OK");
00545     if (okSpot != NULL) {
00546         positiveResponse = true;
00547         readyToReturn = true;
00548     } else if (finishedSending && amountReceived > 0) {
00549         positiveResponse = false;
00550         readyToReturn = true;
00551     }
00552 }
00553 
00554 void TwitPic::flushResponse() {
00555 //no longer needed
00556 }