Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TwitterMbed HTTPClient_SuperTweet HTTPClient_SuperTweet_MPL115A2 cc3000_twitter_demo
Fork of HTTPClient by
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 }
Generated on Wed Jul 13 2022 20:24:27 by
1.7.2
