Fork with custom headers and basic auth.
Fork of HTTPClient by
Embed:
(wiki syntax)
Show/hide line numbers
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 }
Generated on Mon Jul 18 2022 07:40:55 by 1.7.2