Fork with custom headers and basic auth.

Dependents:   MbedSmartRest

Fork of HTTPClient by Kazushi Mukaiyama

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HTTPClient.cpp Source File

HTTPClient.cpp

00001 /* HTTPClient.cpp */
00002 /* Copyright (C) 2012 mbed.org, MIT License
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00005  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00006  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00007  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00008  * furnished to do so, subject to the following conditions:
00009  *
00010  * The above copyright notice and this permission notice shall be included in all copies or
00011  * substantial portions of the Software.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00014  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00015  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00016  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00017  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00018  */
00019 
00020 //Debug is disabled by default
00021 #if 0
00022 //Enable debug
00023 #include <cstdio>
00024 #define DBG(x, ...) std::printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__); 
00025 #define WARN(x, ...) std::printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__); 
00026 #define ERR(x, ...) std::printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__); 
00027 
00028 #else
00029 //Disable debug
00030 #define DBG(x, ...) 
00031 #define WARN(x, ...)
00032 #define ERR(x, ...) 
00033 
00034 #endif
00035 
00036 #define HTTP_PORT 80
00037 
00038 #define OK 0
00039 
00040 #define MIN(x,y) (((x)<(y))?(x):(y))
00041 #define MAX(x,y) (((x)>(y))?(x):(y))
00042 
00043 #define CHUNK_SIZE 128
00044 
00045 #include <cstring>
00046 
00047 #include "HTTPClient.h"
00048 
00049 HTTPClient::HTTPClient() :
00050 m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
00051 {
00052 
00053 }
00054 
00055 HTTPClient::~HTTPClient()
00056 {
00057 
00058 }
00059 
00060 void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification
00061 {
00062   m_basicAuthUser = user;
00063   m_basicAuthPassword = password;
00064 }
00065 
00066 void HTTPClient::customHeaders(const char **headers, size_t pairs)
00067 {
00068     m_customHeaders = headers;
00069     m_nCustomHeaders = pairs;
00070 }
00071 
00072 
00073 HTTPResult HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00074 {
00075   return connect(url, HTTP_GET, NULL, pDataIn, timeout);
00076 }
00077 
00078 HTTPResult HTTPClient::get(const char* url, char* result, size_t maxResultLen, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00079 {
00080   HTTPText str(result, maxResultLen);
00081   return get(url, &str, timeout);
00082 }
00083 
00084 HTTPResult HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00085 {
00086   return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
00087 }
00088 
00089 HTTPResult HTTPClient::put(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00090 {
00091   return connect(url, HTTP_PUT, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
00092 }
00093 
00094 HTTPResult HTTPClient::del(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00095 {
00096   return connect(url, HTTP_DELETE, NULL, pDataIn, timeout);
00097 }
00098 
00099 
00100 int HTTPClient::getHTTPResponseCode()
00101 {
00102   return m_httpResponseCode;
00103 }
00104 
00105 #define CHECK_CONN_ERR(ret) \
00106   do{ \
00107     if(ret) { \
00108       m_sock.close(); \
00109       ERR("Connection error (%d)", ret); \
00110       return HTTP_CONN; \
00111     } \
00112   } while(0)
00113 
00114 #define PRTCL_ERR() \
00115   do{ \
00116     m_sock.close(); \
00117     ERR("Protocol error"); \
00118     return HTTP_PRTCL; \
00119   } while(0)
00120 
00121 HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
00122 { 
00123 puts("Connect called.");
00124   m_httpResponseCode = 0; //Invalidate code
00125   m_timeout = timeout;
00126   
00127   pDataIn->writeReset();
00128   if( pDataOut )
00129   {
00130     pDataOut->readReset();
00131   }
00132 
00133   char scheme[8];
00134   uint16_t port;
00135   char host[32];
00136   char path[64];
00137   //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
00138   HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
00139   if(res != HTTP_OK)
00140   {
00141     ERR("parseURL returned %d", res);
00142     return res;
00143   }
00144 
00145   if(port == 0) //TODO do handle HTTPS->443
00146   {
00147     port = 80;
00148   }
00149 
00150   DBG("Scheme: %s", scheme);
00151   DBG("Host: %s", host);
00152   DBG("Port: %d", port);
00153   DBG("Path: %s", path);
00154 
00155   //Connect
00156   DBG("Connecting socket to server");
00157   int ret = m_sock.connect(host, port);
00158   if (ret < 0)
00159   {
00160     m_sock.close();
00161     ERR("Could not connect");
00162     return HTTP_CONN;
00163   }
00164 
00165   //Send request
00166   DBG("Sending request");
00167   char buf[CHUNK_SIZE];
00168   const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":(method==HTTP_DELETE)?"DELETE":"";
00169   snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request
00170   ret = send(buf);
00171   if(ret)
00172   {
00173     m_sock.close();
00174     ERR("Could not write request");
00175     return HTTP_CONN;
00176   }
00177   
00178   // send authorization
00179   if ((m_basicAuthUser != NULL) && (m_basicAuthPassword != NULL)) {
00180     strcpy(buf, "Authorization: Basic ");
00181     createauth(m_basicAuthUser, m_basicAuthPassword, buf+strlen(buf), sizeof(buf)-strlen(buf));
00182     strcat(buf, "\r\n");
00183     
00184     ret = send(buf);
00185     CHECK_CONN_ERR(ret);
00186   }
00187 
00188   //Send all headers
00189   for (size_t nh = 0; nh < m_nCustomHeaders; ++nh) {
00190     snprintf(buf, sizeof(buf), "%s: %s\r\n", m_customHeaders[nh], m_customHeaders[nh+1]);
00191     ret = send(buf);
00192     CHECK_CONN_ERR(ret);
00193   }
00194 
00195   //Send default headers
00196   DBG("Sending headers");
00197   if( pDataOut != NULL )
00198   {
00199     if( pDataOut->getIsChunked() )
00200     {
00201       ret = send("Transfer-Encoding: chunked\r\n");
00202       CHECK_CONN_ERR(ret);
00203     }
00204     else
00205     {
00206       snprintf(buf, sizeof(buf), "Content-Length: %lu\r\n", pDataOut->getDataLen());
00207       ret = send(buf);
00208       CHECK_CONN_ERR(ret);
00209     }
00210     char type[48];
00211     if( pDataOut->getDataType(type, 48) == HTTP_OK )
00212     {
00213       snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type);
00214       ret = send(buf);
00215       CHECK_CONN_ERR(ret);
00216     }
00217   }
00218   
00219   //Close headers
00220   DBG("Headers sent");
00221   ret = send("\r\n");
00222   CHECK_CONN_ERR(ret);
00223 
00224   size_t trfLen;
00225   
00226   //Send data (if available)
00227   if( pDataOut != NULL )
00228   {
00229     DBG("Sending data");
00230     while(true)
00231     {
00232       size_t writtenLen = 0;
00233       pDataOut->read(buf, CHUNK_SIZE, &trfLen);
00234       if( pDataOut->getIsChunked() )
00235       {
00236         //Write chunk header
00237         char chunkHeader[16];
00238         snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding
00239         ret = send(chunkHeader);
00240         CHECK_CONN_ERR(ret);
00241       }
00242       else if( trfLen == 0 )
00243       {
00244         break;
00245       }
00246       if( trfLen != 0 )
00247       {
00248         ret = send(buf, trfLen);
00249         CHECK_CONN_ERR(ret);
00250       }
00251 
00252       if( pDataOut->getIsChunked()  )
00253       {
00254         ret = send("\r\n"); //Chunk-terminating CRLF
00255         CHECK_CONN_ERR(ret);
00256       }
00257       else
00258       {
00259         writtenLen += trfLen;
00260         if( writtenLen >= pDataOut->getDataLen() )
00261         {
00262           break;
00263         }
00264       }
00265 
00266       if( trfLen == 0 )
00267       {
00268         break;
00269       }
00270     }
00271 
00272   }
00273   
00274   //Receive response
00275   DBG("Receiving response");
00276   ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
00277   CHECK_CONN_ERR(ret);
00278 
00279   buf[trfLen] = '\0';
00280 
00281   char* crlfPtr = strstr(buf, "\r\n");
00282   if(crlfPtr == NULL)
00283   {
00284     PRTCL_ERR();
00285   }
00286 
00287   int crlfPos = crlfPtr - buf;
00288   buf[crlfPos] = '\0';
00289 
00290   //Parse HTTP response
00291   if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 )
00292   {
00293     //Cannot match string, error
00294     ERR("Not a correct HTTP answer : %s\n", buf);
00295     PRTCL_ERR();
00296   }
00297 
00298   if( (m_httpResponseCode < 200) || (m_httpResponseCode >= 300) )
00299   {
00300     //Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers 
00301     WARN("Response code %d", m_httpResponseCode);
00302     PRTCL_ERR();
00303   }
00304 
00305   DBG("Reading headers");
00306 
00307   memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
00308   trfLen -= (crlfPos + 2);
00309 
00310   size_t recvContentLength = 0;
00311   bool recvChunked = false;
00312   //Now get headers
00313   while( true )
00314   {
00315     crlfPtr = strstr(buf, "\r\n");
00316     if(crlfPtr == NULL)
00317     {
00318       if( trfLen < CHUNK_SIZE - 1 )
00319       {
00320         size_t newTrfLen;
00321         ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00322         trfLen += newTrfLen;
00323         buf[trfLen] = '\0';
00324         DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
00325         CHECK_CONN_ERR(ret);
00326         continue;
00327       }
00328       else
00329       {
00330         PRTCL_ERR();
00331       }
00332     }
00333 
00334     crlfPos = crlfPtr - buf;
00335 
00336     if(crlfPos == 0) //End of headers
00337     {
00338       DBG("Headers read");
00339       memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
00340       trfLen -= 2;
00341       break;
00342     }
00343 
00344     buf[crlfPos] = '\0';
00345 
00346     char key[32];
00347     char value[32];
00348 
00349     key[31] = '\0';
00350     value[31] = '\0';
00351 
00352     int n = sscanf(buf, "%31[^:]: %31[^\r\n]", key, value);
00353     if ( n == 2 )
00354     {
00355       DBG("Read header : %s: %s\n", key, value);
00356       if( !strcmp(key, "Content-Length") )
00357       {
00358         sscanf(value, "%d", &recvContentLength);
00359         pDataIn->setDataLen(recvContentLength);
00360       }
00361       else if( !strcmp(key, "Transfer-Encoding") )
00362       {
00363         if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") )
00364         {
00365           recvChunked = true;
00366           pDataIn->setIsChunked(true);
00367         }
00368       }
00369       else if( !strcmp(key, "Content-Type") )
00370       {
00371         pDataIn->setDataType(value);
00372       }
00373 
00374       memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
00375       trfLen -= (crlfPos + 2);
00376 
00377     }
00378     else
00379     {
00380       ERR("Could not parse header");
00381       PRTCL_ERR();
00382     }
00383 
00384   }
00385 
00386   //Receive data
00387   DBG("Receiving data");
00388   while(true)
00389   {
00390     size_t readLen = 0;
00391 
00392     if( recvChunked )
00393     {
00394       //Read chunk header
00395       bool foundCrlf;
00396       do
00397       {
00398         foundCrlf = false;
00399         crlfPos=0;
00400         buf[trfLen]=0;
00401         if(trfLen >= 2)
00402         {
00403           for(; crlfPos < trfLen - 2; crlfPos++)
00404           {
00405             if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' )
00406             {
00407               foundCrlf = true;
00408               break;
00409             }
00410           }
00411         }
00412         if(!foundCrlf) //Try to read more
00413         {
00414           if( trfLen < CHUNK_SIZE )
00415           {
00416             size_t newTrfLen;
00417             ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00418             trfLen += newTrfLen;
00419             CHECK_CONN_ERR(ret);
00420             continue;
00421           }
00422           else
00423           {
00424             PRTCL_ERR();
00425           }
00426         }
00427       } while(!foundCrlf);
00428       buf[crlfPos] = '\0';
00429       int n = sscanf(buf, "%x", &readLen);
00430       if(n!=1)
00431       {
00432         ERR("Could not read chunk length");
00433         PRTCL_ERR();
00434       }
00435 
00436       memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
00437       trfLen -= (crlfPos + 2);
00438 
00439       if( readLen == 0 )
00440       {
00441         //Last chunk
00442         break;
00443       }
00444     }
00445     else
00446     {
00447       readLen = recvContentLength;
00448     }
00449 
00450     DBG("Retrieving %d bytes", readLen);
00451 
00452     do
00453     {
00454       pDataIn->write(buf, MIN(trfLen, readLen));
00455       if( trfLen > readLen )
00456       {
00457         memmove(buf, &buf[readLen], trfLen - readLen);
00458         trfLen -= readLen;
00459         readLen = 0;
00460       }
00461       else
00462       {
00463         readLen -= trfLen;
00464       }
00465 
00466       if(readLen)
00467       {
00468         ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen);
00469         CHECK_CONN_ERR(ret);
00470       }
00471     } while(readLen);
00472 
00473     if( recvChunked )
00474     {
00475       if(trfLen < 2)
00476       {
00477         size_t newTrfLen;
00478         //Read missing chars to find end of chunk
00479         ret = recv(buf + trfLen, 2 - trfLen, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00480         CHECK_CONN_ERR(ret);
00481         trfLen += newTrfLen;
00482       }
00483       if( (buf[0] != '\r') || (buf[1] != '\n') )
00484       {
00485         ERR("Format error");
00486         PRTCL_ERR();
00487       }
00488       memmove(buf, &buf[2], trfLen - 2);
00489       trfLen -= 2;
00490     }
00491     else
00492     {
00493       break;
00494     }
00495 
00496   }
00497 
00498   m_sock.close();
00499   DBG("Completed HTTP transaction");
00500 
00501   return HTTP_OK;
00502 }
00503 
00504 HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure
00505 {
00506   DBG("Trying to read between %d and %d bytes", minLen, maxLen);
00507   size_t readLen = 0;
00508       
00509   if(!m_sock.is_connected())
00510   {
00511     WARN("Connection was closed by server");
00512     return HTTP_CLOSED; //Connection was closed by server 
00513   }
00514     
00515   int ret;
00516   while(readLen < maxLen)
00517   {
00518     if(readLen < minLen)
00519     {
00520       DBG("Trying to read at most %d bytes [Blocking]", minLen - readLen);
00521       m_sock.set_blocking(true, m_timeout);
00522       ret = m_sock.receive_all(buf + readLen, minLen - readLen);
00523     }
00524     else
00525     {
00526       DBG("Trying to read at most %d bytes [Not blocking]", maxLen - readLen);
00527       m_sock.set_blocking(false, 0);
00528       ret = m_sock.receive(buf + readLen, maxLen - readLen);
00529     }
00530     
00531     if( ret > 0)
00532     {
00533       readLen += ret;
00534     }
00535     else if( ret == 0 )
00536     {
00537       break;
00538     }
00539     else
00540     {
00541       if(!m_sock.is_connected())
00542       {
00543         ERR("Connection error (recv returned %d)", ret);
00544         *pReadLen = readLen;
00545         return HTTP_CONN;
00546       }
00547       else
00548       {
00549         break;      
00550       }
00551     }
00552     
00553     if(!m_sock.is_connected())
00554     {
00555       break;
00556     }
00557   }
00558   DBG("Read %d bytes", readLen);
00559   *pReadLen = readLen;
00560   return HTTP_OK;
00561 }
00562 
00563 HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure
00564 {
00565   if(len == 0)
00566   {
00567     len = strlen(buf);
00568   }
00569   DBG("Trying to write %d bytes", len);
00570   size_t writtenLen = 0;
00571     
00572   if(!m_sock.is_connected())
00573   {
00574     WARN("Connection was closed by server");
00575     return HTTP_CLOSED; //Connection was closed by server 
00576   }
00577   
00578   m_sock.set_blocking(true, m_timeout);
00579   int ret = m_sock.send_all(buf, len);
00580   if(ret > 0)
00581   {
00582     writtenLen += ret;
00583   }
00584   else if( ret == 0 )
00585   {
00586     WARN("Connection was closed by server");
00587     return HTTP_CLOSED; //Connection was closed by server
00588   }
00589   else
00590   {
00591     ERR("Connection error (send returned %d)", ret);
00592     return HTTP_CONN;
00593   }
00594   
00595   DBG("Written %d bytes", writtenLen);
00596   return HTTP_OK;
00597 }
00598 
00599 HTTPResult HTTPClient::parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen) //Parse URL
00600 {
00601   char* schemePtr = (char*) url;
00602   char* hostPtr = (char*) strstr(url, "://");
00603   if(hostPtr == NULL)
00604   {
00605     WARN("Could not find host");
00606     return HTTP_PARSE; //URL is invalid
00607   }
00608 
00609   if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char
00610   {
00611     WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
00612     return HTTP_PARSE;
00613   }
00614   memcpy(scheme, schemePtr, hostPtr - schemePtr);
00615   scheme[hostPtr - schemePtr] = '\0';
00616 
00617   hostPtr+=3;
00618 
00619   size_t hostLen = 0;
00620 
00621   char* portPtr = strchr(hostPtr, ':');
00622   if( portPtr != NULL )
00623   {
00624     hostLen = portPtr - hostPtr;
00625     portPtr++;
00626     if( sscanf(portPtr, "%hu", port) != 1)
00627     {
00628       WARN("Could not find port");
00629       return HTTP_PARSE;
00630     }
00631   }
00632   else
00633   {
00634     *port=0;
00635   }
00636   char* pathPtr = strchr(hostPtr, '/');
00637   if( hostLen == 0 )
00638   {
00639     hostLen = pathPtr - hostPtr;
00640   }
00641 
00642   if( maxHostLen < hostLen + 1 ) //including NULL-terminating char
00643   {
00644     WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
00645     return HTTP_PARSE;
00646   }
00647   memcpy(host, hostPtr, hostLen);
00648   host[hostLen] = '\0';
00649 
00650   size_t pathLen;
00651   char* fragmentPtr = strchr(hostPtr, '#');
00652   if(fragmentPtr != NULL)
00653   {
00654     pathLen = fragmentPtr - pathPtr;
00655   }
00656   else
00657   {
00658     pathLen = strlen(pathPtr);
00659   }
00660 
00661   if( maxPathLen < pathLen + 1 ) //including NULL-terminating char
00662   {
00663     WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
00664     return HTTP_PARSE;
00665   }
00666   memcpy(path, pathPtr, pathLen);
00667   path[pathLen] = '\0';
00668 
00669   return HTTP_OK;
00670 }
00671 
00672 void HTTPClient::createauth (const char *user, const char *pwd, char *buf, int len) {
00673     char tmp[80];
00674  
00675     snprintf(tmp, sizeof(tmp), "%s:%s", user, pwd);
00676     base64enc(tmp, strlen(tmp), &buf[strlen(buf)], len - strlen(buf));
00677 }
00678 
00679 // Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
00680 int HTTPClient::base64enc(const char *input, unsigned int length, char *output, int len) {
00681   static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00682   unsigned int c, c1, c2, c3;
00683  
00684   if (len < ((((length-1)/3)+1)<<2)) return -1;
00685   for(unsigned int i = 0, j = 0; i<length; i+=3,j+=4) {
00686     c1 = ((((unsigned char)*((unsigned char *)&input[i]))));
00687     c2 = (length>i+1)?((((unsigned char)*((unsigned char *)&input[i+1])))):0;
00688     c3 = (length>i+2)?((((unsigned char)*((unsigned char *)&input[i+2])))):0;
00689  
00690     c = ((c1 & 0xFC) >> 2);
00691     output[j+0] = base64[c];
00692     c = ((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4);
00693     output[j+1] = base64[c];
00694     c = ((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6);
00695     output[j+2] = (length>i+1)?base64[c]:'=';
00696     c = (c3 & 0x3F);
00697     output[j+3] = (length>i+2)?base64[c]:'=';
00698   }
00699   output[(((length-1)/3)+1)<<2] = '\0';
00700   return 0;
00701 }