Fork with basicauth enabled.

Dependents:   TwitterMbed HTTPClient_SuperTweet HTTPClient_SuperTweet_MPL115A2 cc3000_twitter_demo

Fork of HTTPClient by Stephen Paulger

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