/** * parse a single line of the TivoConnect protocol into a 'kkv' structure * "foo=bar\x0a" * "AAABCCCDDDD" */ static ptrdiff_t do_parse_kv(char *buf, size_t len, kkv *k) { const char *orig = buf; /* save original address */ size_t l = memcspn(buf, len, "=\x0a", 2); k->keystr.start = buf; k->keystr.len = l; strlower(k->keystr.start, k->keystr.len); k->key = str2key(&k->keystr); buf += l, len -= l; l = memspn(buf, len, "=", 1); buf += l, len -= l; k->val.start = buf; l = memcspn(buf, len, "\x0a", 1); k->val.len = l; buf += l, len -= l; l = memspn(buf, len, "\x0a", 1); buf += l, len -= l; return buf - orig; }
/** * 'buf' points at first char in headers, parse them into 'head' and * return bytes consumed * @note this function is used by other protocols, such as SSDP */ size_t http_parse_headers(char *buf, size_t len, http_headers *head) { struct head_kv *kv = head->h; const char *orig = buf, *end = buf + len; size_t skip = 2; head->cnt = 0; /* FIXME: make sure we don't go further than 'len' */ #if 0 printf("%s len=%u buf=%.*s\n", __func__, (unsigned)len, (unsigned)len, buf); #endif while ( buf < end && 2 == skip && head->cnt < sizeof head->h / sizeof head->h[0] ) { /* "<A><: ><B><\r\n>" */ ptrlen_list *pl = &kv->val; ptrlen *v = pl->p; pl->cnt = 0; kv->key.start = buf; kv->key.len = memcspn(buf, len, ": \r\n", 4); buf += kv->key.len; len -= kv->key.len; skip = memspn(buf, len, ": ", 2); buf += skip; len -= skip; v->start = buf; v->len = memcspn(buf, len, "\r\n", 2); pl->cnt++; buf += v->len; len -= v->len; skip = memspn(buf, len, "\r\n", 2); buf += skip; len -= skip; head->cnt++; v++; kv++; } return (size_t)(buf - orig); }
// FIXME: "GET /common/jquery.autocomplete.js HTTP/1.1\x0d\x0a" -> HTTP req meth=HTT uri=/1.1 304 Not Modified static ptrdiff_t do_parse_req(char *buf, size_t len, http_req *r) { const char *orig = buf; size_t l = memcspn(buf, len, " \r\n", 3); if (l > 0) { /* method */ r->meth.start = buf; r->meth.len = l; buf += l, len -= l; /* skip whitespace */ l = memspn(buf, len, " ", 1); buf += l, len -= l; /* uri */ l = memcspn(buf, len, " \r\n", 3); r->uri.start = buf; r->uri.len = l; buf += l, len -= l; /* skip whitespace */ l = memspn(buf, len, " ", 1); buf += l, len -= l; /* version */ l = memcspn(buf, len, "\r\n", 2); r->ver.start = buf; r->ver.len = l; printf("HTTP meth=<%.*s> uri=<%.*s> ver=<%.*s> (%u)\n", (int)r->meth.len, r->meth.start, (int)r->uri.len, r->uri.start, (int)r->ver.len, r->ver.start, r->ver.len); buf += l, len -= l; /* skip newline */ /* NOTE: we must skip only a single set of "\r\n" */ if (len >= 2 && '\r' == buf[0] && '\n' == buf[1]) buf += 2, len -= 2; } #if 1 /* debug */ printf("%s ver=<%.*s> buf=<%.*s>\n", __func__, (int)r->ver.len, r->ver.start, 20, buf); printf("%s -> %u\n", __func__, (unsigned)(buf-orig)); #endif return buf - orig; }
int RemoteCameraHttp::GetResponse() { #if HAVE_LIBPCRE if ( method == REGEXP ) { const char *header = 0; int header_len = 0; const char *http_version = 0; int status_code = 0; const char *status_mesg = 0; const char *connection_type = ""; int content_length = 0; const char *content_type = ""; const char *content_boundary = ""; const char *subheader = 0; int subheader_len = 0; //int subcontent_length = 0; //const char *subcontent_type = ""; while ( true ) { switch( state ) { case HEADER : { static RegExpr *header_expr = 0; static RegExpr *status_expr = 0; static RegExpr *connection_expr = 0; static RegExpr *content_length_expr = 0; static RegExpr *content_type_expr = 0; int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { Error( "Unable to read header data" ); return( -1 ); } if ( !header_expr ) header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL ); if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 ) { header = header_expr->MatchString( 1 ); header_len = header_expr->MatchLength( 1 ); Debug( 4, "Captured header (%d bytes):\n'%s'", header_len, header ); if ( !status_expr ) status_expr = new RegExpr( "^HTTP/(1\\.[01]) +([0-9]+) +(.+?)\r?\n", PCRE_CASELESS ); if ( status_expr->Match( header, header_len ) < 4 ) { Error( "Unable to extract HTTP status from header" ); return( -1 ); } http_version = status_expr->MatchString( 1 ); status_code = atoi( status_expr->MatchString( 2 ) ); status_mesg = status_expr->MatchString( 3 ); if ( status_code < 200 || status_code > 299 ) { Error( "Invalid response status %d: %s", status_code, status_mesg ); return( -1 ); } Debug( 3, "Got status '%d' (%s), http version %s", status_code, status_mesg, http_version ); if ( !connection_expr ) connection_expr = new RegExpr( "Connection: ?(.+?)\r?\n", PCRE_CASELESS ); if ( connection_expr->Match( header, header_len ) == 2 ) { connection_type = connection_expr->MatchString( 1 ); Debug( 3, "Got connection '%s'", connection_type ); } if ( !content_length_expr ) content_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); if ( content_length_expr->Match( header, header_len ) == 2 ) { content_length = atoi( content_length_expr->MatchString( 1 ) ); Debug( 3, "Got content length '%d'", content_length ); } if ( !content_type_expr ) content_type_expr = new RegExpr( "Content-type: ?(.+?)(?:; ?boundary=(.+?))?\r?\n", PCRE_CASELESS ); if ( content_type_expr->Match( header, header_len ) >= 2 ) { content_type = content_type_expr->MatchString( 1 ); Debug( 3, "Got content type '%s'\n", content_type ); if ( content_type_expr->MatchCount() > 2 ) { content_boundary = content_type_expr->MatchString( 2 ); Debug( 3, "Got content boundary '%s'", content_boundary ); } } if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) { // Single image mode = SINGLE_IMAGE; format = JPEG; state = CONTENT; } else if ( !strcasecmp( content_type, "image/x-rgb" ) ) { // Single image mode = SINGLE_IMAGE; format = X_RGB; state = CONTENT; } else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) { // Single image mode = SINGLE_IMAGE; format = X_RGBZ; state = CONTENT; } else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) { // Image stream, so start processing if ( !content_boundary[0] ) { Error( "No content boundary found in header '%s'", header ); return( -1 ); } mode = MULTI_IMAGE; state = SUBHEADER; } //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) //{ //// MPEG stream, coming soon! //} else { Error( "Unrecognised content type '%s'", content_type ); return( -1 ); } buffer.consume( header_len ); } else { Debug( 3, "Unable to extract header from stream, retrying" ); //return( -1 ); } break; } case SUBHEADER : { static RegExpr *subheader_expr = 0; static RegExpr *subcontent_length_expr = 0; static RegExpr *subcontent_type_expr = 0; if ( !subheader_expr ) { char subheader_pattern[256] = ""; snprintf( subheader_pattern, sizeof(subheader_pattern), "^((?:\r?\n){0,2}?(?:--)?%s\r?\n.+?\r?\n\r?\n)", content_boundary ); subheader_expr = new RegExpr( subheader_pattern, PCRE_DOTALL ); } if ( subheader_expr->Match( (char *)buffer, (int)buffer ) == 2 ) { subheader = subheader_expr->MatchString( 1 ); subheader_len = subheader_expr->MatchLength( 1 ); Debug( 4, "Captured subheader (%d bytes):'%s'", subheader_len, subheader ); if ( !subcontent_length_expr ) subcontent_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); if ( subcontent_length_expr->Match( subheader, subheader_len ) == 2 ) { content_length = atoi( subcontent_length_expr->MatchString( 1 ) ); Debug( 3, "Got subcontent length '%d'", content_length ); } if ( !subcontent_type_expr ) subcontent_type_expr = new RegExpr( "Content-type: ?(.+?)\r?\n", PCRE_CASELESS ); if ( subcontent_type_expr->Match( subheader, subheader_len ) == 2 ) { content_type = subcontent_type_expr->MatchString( 1 ); Debug( 3, "Got subcontent type '%s'", content_type ); } buffer.consume( subheader_len ); state = CONTENT; } else { Debug( 3, "Unable to extract subheader from stream, retrying" ); int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { return( -1 ); } } break; } case CONTENT : { if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) { format = JPEG; } else if ( !strcasecmp( content_type, "image/x-rgb" ) ) { format = X_RGB; } else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) { format = X_RGBZ; } else { Error( "Found unsupported content type '%s'", content_type ); return( -1 ); } if ( content_length ) { while ( buffer.size() < (unsigned int)content_length ) { int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { Error( "Unable to read content" ); return( -1 ); } } Debug( 3, "Got end of image by length, content-length = %d", content_length ); } else { while ( !content_length ) { int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { if ( mode == MULTI_IMAGE ) { Error( "Connection dropped by remote end" ); return( 0 ); } } else if ( buffer_len < 0 ) { Error( "Unable to read content" ); return( -1 ); } static RegExpr *content_expr = 0; if ( buffer_len ) { if ( mode == MULTI_IMAGE ) { if ( !content_expr ) { char content_pattern[256] = ""; snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary ); content_expr = new RegExpr( content_pattern, PCRE_DOTALL ); } if ( content_expr->Match( buffer, buffer.size() ) == 2 ) { content_length = content_expr->MatchLength( 1 ); Debug( 3, "Got end of image by pattern, content-length = %d", content_length ); } } } else { content_length = buffer.size(); Debug( 3, "Got end of image by closure, content-length = %d", content_length ); if ( mode == SINGLE_IMAGE ) { if ( !content_expr ) { content_expr = new RegExpr( "^(.+?)(?:\r?\n){1,2}?$", PCRE_DOTALL ); } if ( content_expr->Match( buffer, buffer.size() ) == 2 ) { content_length = content_expr->MatchLength( 1 ); Debug( 3, "Trimmed end of image, new content-length = %d", content_length ); } } } } } if ( mode == SINGLE_IMAGE ) { state = HEADER; Disconnect(); } else { state = SUBHEADER; } Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() ); return( content_length ); } case HEADERCONT : case SUBHEADERCONT : { // Ignore break; } } } } else #endif // HAVE_LIBPCRE { if ( method == REGEXP ) { Warning( "Unable to use netcam regexps as not compiled with libpcre" ); } static const char *http_match = "HTTP/"; static const char *connection_match = "Connection:"; static const char *content_length_match = "Content-length:"; static const char *content_type_match = "Content-type:"; static const char *boundary_match = "boundary="; static int http_match_len = 0; static int connection_match_len = 0; static int content_length_match_len = 0; static int content_type_match_len = 0; static int boundary_match_len = 0; if ( !http_match_len ) http_match_len = strlen( http_match ); if ( !connection_match_len ) connection_match_len = strlen( connection_match ); if ( !content_length_match_len ) content_length_match_len = strlen( content_length_match ); if ( !content_type_match_len ) content_type_match_len = strlen( content_type_match ); if ( !boundary_match_len ) boundary_match_len = strlen( boundary_match ); static int n_headers; //static char *headers[32]; static int n_subheaders; //static char *subheaders[32]; static char *http_header; static char *connection_header; static char *content_length_header; static char *content_type_header; static char *boundary_header; static char subcontent_length_header[32]; static char subcontent_type_header[64]; static char http_version[16]; static char status_code[16]; //static int status; static char status_mesg[256]; static char connection_type[32]; static int content_length; static char content_type[32]; static char content_boundary[64]; static int content_boundary_len; while ( true ) { switch( state ) { case HEADER : { n_headers = 0; http_header = 0; connection_header = 0; content_length_header = 0; content_type_header = 0; http_version[0] = '\0'; status_code [0]= '\0'; //status = 0; status_mesg [0]= '\0'; connection_type [0]= '\0'; content_length = 0; content_type[0] = '\0'; content_boundary[0] = '\0'; content_boundary_len = 0; } case HEADERCONT : { int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { Error( "Unable to read header" ); return( -1 ); } char *crlf = 0; char *header_ptr = (char *)buffer; int header_len = buffer.size(); bool all_headers = false; while( true ) { int crlf_len = memspn( header_ptr, "\r\n", header_len ); if ( n_headers ) { if ( (crlf_len == 2 && !strncmp( header_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( header_ptr, "\r\n\r\n", crlf_len )) ) { *header_ptr = '\0'; header_ptr += crlf_len; header_len -= buffer.consume( header_ptr-(char *)buffer ); all_headers = true; break; } } if ( crlf_len ) { if ( header_len == crlf_len ) { break; } else { *header_ptr = '\0'; header_ptr += crlf_len; header_len -= buffer.consume( header_ptr-(char *)buffer ); } } Debug( 6, "%s", header_ptr ); if ( (crlf = mempbrk( header_ptr, "\r\n", header_len )) ) { //headers[n_headers++] = header_ptr; n_headers++; if ( !http_header && (strncasecmp( header_ptr, http_match, http_match_len ) == 0) ) { http_header = header_ptr+http_match_len; Debug( 6, "Got http header '%s'", header_ptr ); } else if ( !connection_header && (strncasecmp( header_ptr, connection_match, connection_match_len) == 0) ) { connection_header = header_ptr+connection_match_len; Debug( 6, "Got connection header '%s'", header_ptr ); } else if ( !content_length_header && (strncasecmp( header_ptr, content_length_match, content_length_match_len) == 0) ) { content_length_header = header_ptr+content_length_match_len; Debug( 6, "Got content length header '%s'", header_ptr ); } else if ( !content_type_header && (strncasecmp( header_ptr, content_type_match, content_type_match_len) == 0) ) { content_type_header = header_ptr+content_type_match_len; Debug( 6, "Got content type header '%s'", header_ptr ); } else { Debug( 6, "Got ignored header '%s'", header_ptr ); } header_ptr = crlf; header_len -= buffer.consume( header_ptr-(char *)buffer ); } else { // No end of line found break; } } if ( all_headers ) { char *start_ptr, *end_ptr; if ( !http_header ) { Error( "Unable to extract HTTP status from header" ); return( -1 ); } start_ptr = http_header; end_ptr = start_ptr+strspn( start_ptr, "10." ); memset( http_version, 0, sizeof(http_version) ); strncpy( http_version, start_ptr, end_ptr-start_ptr ); start_ptr = end_ptr; start_ptr += strspn( start_ptr, " " ); end_ptr = start_ptr+strspn( start_ptr, "0123456789" ); memset( status_code, 0, sizeof(status_code) ); strncpy( status_code, start_ptr, end_ptr-start_ptr ); int status = atoi( status_code ); start_ptr = end_ptr; start_ptr += strspn( start_ptr, " " ); strcpy( status_mesg, start_ptr ); if ( status < 200 || status > 299 ) { Error( "Invalid response status %s: %s", status_code, status_mesg ); return( -1 ); } Debug( 3, "Got status '%d' (%s), http version %s", status, status_mesg, http_version ); if ( connection_header ) { memset( connection_type, 0, sizeof(connection_type) ); start_ptr = connection_header + strspn( connection_header, " " ); strcpy( connection_type, start_ptr ); Debug( 3, "Got connection '%s'", connection_type ); } if ( content_length_header ) { start_ptr = content_length_header + strspn( content_length_header, " " ); content_length = atoi( start_ptr ); Debug( 3, "Got content length '%d'", content_length ); } if ( content_type_header ) { memset( content_type, 0, sizeof(content_type) ); start_ptr = content_type_header + strspn( content_type_header, " " ); if ( (end_ptr = strchr( start_ptr, ';' )) ) { strncpy( content_type, start_ptr, end_ptr-start_ptr ); Debug( 3, "Got content type '%s'", content_type ); start_ptr = end_ptr + strspn( end_ptr, "; " ); if ( strncasecmp( start_ptr, boundary_match, boundary_match_len ) == 0 ) { start_ptr += boundary_match_len; start_ptr += strspn( start_ptr, "-" ); content_boundary_len = sprintf( content_boundary, "--%s", start_ptr ); Debug( 3, "Got content boundary '%s'", content_boundary ); } else { Error( "No content boundary found in header '%s'", content_type_header ); } } else { strcpy( content_type, start_ptr ); Debug( 3, "Got content type '%s'", content_type ); } } if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) { // Single image mode = SINGLE_IMAGE; format = JPEG; state = CONTENT; } else if ( !strcasecmp( content_type, "image/x-rgb" ) ) { // Single image mode = SINGLE_IMAGE; format = X_RGB; state = CONTENT; } else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) { // Single image mode = SINGLE_IMAGE; format = X_RGBZ; state = CONTENT; } else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) { // Image stream, so start processing if ( !content_boundary[0] ) { Error( "No content boundary found in header '%s'", content_type_header ); return( -1 ); } mode = MULTI_IMAGE; state = SUBHEADER; } //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) //{ //// MPEG stream, coming soon! //} else { Error( "Unrecognised content type '%s'", content_type ); return( -1 ); } } else { Debug( 3, "Unable to extract entire header from stream, continuing" ); state = HEADERCONT; //return( -1 ); } break; } case SUBHEADER : { n_subheaders = 0; boundary_header = 0; subcontent_length_header[0] = '\0'; subcontent_type_header[0] = '\0'; content_length = 0; content_type[0] = '\0'; } case SUBHEADERCONT : { char *crlf = 0; char *subheader_ptr = (char *)buffer; int subheader_len = buffer.size(); bool all_headers = false; while( true ) { int crlf_len = memspn( subheader_ptr, "\r\n", subheader_len ); if ( n_subheaders ) { if ( (crlf_len == 2 && !strncmp( subheader_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( subheader_ptr, "\r\n\r\n", crlf_len )) ) { *subheader_ptr = '\0'; subheader_ptr += crlf_len; subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); all_headers = true; break; } } if ( crlf_len ) { if ( subheader_len == crlf_len ) { break; } else { *subheader_ptr = '\0'; subheader_ptr += crlf_len; subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); } } Debug( 6, "%d: %s", subheader_len, subheader_ptr ); if ( (crlf = mempbrk( subheader_ptr, "\r\n", subheader_len )) ) { //subheaders[n_subheaders++] = subheader_ptr; n_subheaders++; if ( !boundary_header && (strncasecmp( subheader_ptr, content_boundary, content_boundary_len ) == 0) ) { boundary_header = subheader_ptr; Debug( 4, "Got boundary subheader '%s'", subheader_ptr ); } else if ( !subcontent_length_header[0] && (strncasecmp( subheader_ptr, content_length_match, content_length_match_len) == 0) ) { strncpy( subcontent_length_header, subheader_ptr+content_length_match_len, sizeof(subcontent_length_header) ); *(subcontent_length_header+strcspn( subcontent_length_header, "\r\n" )) = '\0'; Debug( 4, "Got content length subheader '%s'", subcontent_length_header ); } else if ( !subcontent_type_header[0] && (strncasecmp( subheader_ptr, content_type_match, content_type_match_len) == 0) ) { strncpy( subcontent_type_header, subheader_ptr+content_type_match_len, sizeof(subcontent_type_header) ); *(subcontent_type_header+strcspn( subcontent_type_header, "\r\n" )) = '\0'; Debug( 4, "Got content type subheader '%s'", subcontent_type_header ); } else { Debug( 6, "Got ignored subheader '%s' found", subheader_ptr ); } subheader_ptr = crlf; subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); } else { // No line end found break; } } if ( all_headers && boundary_header ) { char *start_ptr/*, *end_ptr*/; Debug( 3, "Got boundary '%s'", boundary_header ); if ( subcontent_length_header[0] ) { start_ptr = subcontent_length_header + strspn( subcontent_length_header, " " ); content_length = atoi( start_ptr ); Debug( 3, "Got subcontent length '%d'", content_length ); } if ( subcontent_type_header[0] ) { memset( content_type, 0, sizeof(content_type) ); start_ptr = subcontent_type_header + strspn( subcontent_type_header, " " ); strcpy( content_type, start_ptr ); Debug( 3, "Got subcontent type '%s'", content_type ); } state = CONTENT; } else { Debug( 3, "Unable to extract subheader from stream, retrying" ); int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { Error( "Unable to read subheader" ); return( -1 ); } state = SUBHEADERCONT; } break; } case CONTENT : { if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) { format = JPEG; } else if ( !strcasecmp( content_type, "image/x-rgb" ) ) { format = X_RGB; } else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) { format = X_RGBZ; } else { Error( "Found unsupported content type '%s'", content_type ); return( -1 ); } if ( format == JPEG && buffer.size() >= 2 ) { if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) { Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); return( -1 ); } } if ( content_length ) { while ( buffer.size() < (unsigned int)content_length ) { //int buffer_len = ReadData( buffer, content_length-buffer.size() ); int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { Error( "Unable to read content" ); return( -1 ); } } Debug( 3, "Got end of image by length, content-length = %d", content_length ); } else { int content_pos = 0; while ( !content_length ) { int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { if ( mode == MULTI_IMAGE ) { Error( "Connection dropped by remote end" ); return( 0 ); } } else if ( buffer_len < 0 ) { Error( "Unable to read content" ); return( -1 ); } int buffer_size = buffer.size(); if ( buffer_len ) { if ( mode == MULTI_IMAGE ) { while ( char *start_ptr = (char *)memstr( (char *)buffer+content_pos, "\r\n--", buffer_size-content_pos ) ) { content_length = start_ptr - (char *)buffer; Debug( 3, "Got end of image by pattern (crlf--), content-length = %d", content_length ); break; } } } else { content_length = buffer_size; Debug( 3, "Got end of image by closure, content-length = %d", content_length ); if ( mode == SINGLE_IMAGE ) { char *end_ptr = (char *)buffer+buffer_size; while( *end_ptr == '\r' || *end_ptr == '\n' ) { content_length--; end_ptr--; } if ( end_ptr != ((char *)buffer+buffer_size) ) { Debug( 3, "Trimmed end of image, new content-length = %d", content_length ); } } } } } if ( mode == SINGLE_IMAGE ) { state = HEADER; Disconnect(); } else { state = SUBHEADER; } if ( format == JPEG && buffer.size() >= 2 ) { if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) { Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); return( -1 ); } } Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() ); return( content_length ); } } } } return( 0 ); }
int cURLCamera::Capture( Image &image ) { bool frameComplete = false; /* MODE_STREAM specific variables */ bool SubHeadersParsingComplete = false; unsigned int frame_content_length = 0; std::string frame_content_type; bool need_more_data = false; int nRet; /* Grab the mutex to ensure exclusive access to the shared data */ lock(); while ( !frameComplete ) { /* If the work thread did a reset, reset our local variables */ if ( bReset ) { SubHeadersParsingComplete = false; frame_content_length = 0; frame_content_type.clear(); need_more_data = false; bReset = false; } if ( mode == MODE_UNSET ) { /* Don't have a mode yet. Sleep while waiting for data */ nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); if ( nRet != 0 ) { Error("Failed waiting for available data condition variable: %s",strerror(nRet)); return -20; } } if ( mode == MODE_STREAM ) { /* Subheader parsing */ while( !SubHeadersParsingComplete && !need_more_data ) { size_t crlf_start, crlf_end, crlf_size; std::string subheader; /* Check if the buffer contains something */ if ( databuffer.empty() ) { /* Empty buffer, wait for data */ need_more_data = true; break; } /* Find crlf start */ crlf_start = memcspn(databuffer,"\r\n",databuffer.size()); if ( crlf_start == databuffer.size() ) { /* Not found, wait for more data */ need_more_data = true; break; } /* See if we have enough data for determining crlf length */ if ( databuffer.size() < crlf_start+5 ) { /* Need more data */ need_more_data = true; break; } /* Find crlf end and calculate crlf size */ crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5); crlf_size = (crlf_start + crlf_end) - crlf_start; /* Is this the end of a previous stream? (This is just before the boundary) */ if ( crlf_start == 0 ) { databuffer.consume(crlf_size); continue; } /* Check for invalid CRLF size */ if ( crlf_size > 4 ) { Error("Invalid CRLF length"); } /* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */ if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) { /* This is the last header */ SubHeadersParsingComplete = true; } /* Copy the subheader, excluding the crlf */ subheader.assign(databuffer, crlf_start); /* Advance the buffer past this one */ databuffer.consume(crlf_start+crlf_size); Debug(7,"Got subheader: %s",subheader.c_str()); /* Find where the data in this header starts */ size_t subheader_data_start = subheader.rfind(' '); if ( subheader_data_start == std::string::npos ) { subheader_data_start = subheader.find(':'); } /* Extract the data into a string */ std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos); Debug(8,"Got subheader data: %s",subheader_data.c_str()); /* Check the header */ if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) { /* Found the content-length header */ frame_content_length = atoi(subheader_data.c_str()); Debug(6,"Got content-length subheader: %d",frame_content_length); } else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) { /* Found the content-type header */ frame_content_type = subheader_data; Debug(6,"Got content-type subheader: %s",frame_content_type.c_str()); } } /* Attempt to extract the frame */ if(!need_more_data) { if(!SubHeadersParsingComplete) { /* We haven't parsed all headers yet */ need_more_data = true; } else if ( ! frame_content_length ) { /* Invalid frame */ Error("Invalid frame: invalid content length"); } else if ( frame_content_type != "image/jpeg" ) { /* Unsupported frame type */ Error("Unsupported frame: %s",frame_content_type.c_str()); } else if(frame_content_length > databuffer.size()) { /* Incomplete frame, wait for more data */ need_more_data = true; } else { /* All good. decode the image */ image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder); frameComplete = true; } } /* Attempt to get more data */ if(need_more_data) { nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); if(nRet != 0) { Error("Failed waiting for available data condition variable: %s",strerror(nRet)); return -18; } need_more_data = false; } } else if(mode == MODE_SINGLE) { /* Check if we have anything */ if (!single_offsets.empty()) { if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) { /* Extract frame */ image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder); single_offsets.pop_front(); frameComplete = true; } else { /* This shouldn't happen */ Error("Internal error. Attempting recovery"); databuffer.consume(single_offsets.front()); single_offsets.pop_front(); } } else { /* Don't have a frame yet, wait for the request complete condition variable */ nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex); if(nRet != 0) { Error("Failed waiting for request complete condition variable: %s",strerror(nRet)); return -19; } } } else { /* Failed to match content-type */ Fatal("Unable to match Content-Type. Check URL, username and password"); } /* mode */ } /* frameComplete loop */ /* Release the mutex */ unlock(); if(!frameComplete) return -1; return 1; }
long parse_integer(char *str, char **endptr, int len, int base, int *err){ if(len == 0){ len = strlen(str); } //since signed overflow is undefined and I don't really //want to use gmp for this, use an unsigned long to hold the value uint64_t num = 0; int i = 0, negitive = 0, overflow = 0; //ignore leading space while(isspace(str[i]) && ++i < len); if(i < len){ if(str[i] == '-'){ negitive = 1; i++; } } if(i == len){ if(endptr){*endptr = str;} return 0; } #define TO_NUMBER(start, end, base) \ ({uint64_t number = 0, next = 0; \ int j; \ for(j = start; j < end; j++){ \ next = (number * base) + char_to_number(str[j]); \ if(next < number){/*overflow*/ \ overflow = j; \ break; \ } else { \ number = next; \ } \ } \ number;}) //I'm pretty sure this will cause an error if the string is something like //0x abcdefg. It should be read as a 0, but I'm not sure what will happen if(base == 0 && ((i+1) < len)){ if(str[i] == '0' && str[i+1] == 'x'){ base = 16; i+=2; } else if(str[i] == '0' && str[i+1] == 'o'){ base = 8; i+=2; } else if(str[i] == '0' && str[i+1] == 'b'){ base = 2; i+=2; } else { base = 10; } } while(str[i] == '0' && ++i < len);//read leading zeros if(i == len){ if(endptr){*endptr = (str+i);} return 0; } /* Use special cases for 2, 8, 10, and 16 to speed them up. Multiplies in base 2, 8, or 16 become shifts, and for x86 at least a multiply by 10 becomes two lea instructions. */ #define DO_CASE(base) \ if(i <= max_length_per_base[base]){ \ num = TO_NUMBER(start, i, base); \ } \ if(i > max_length_per_base[base] || overflow > 0 || num>LONG_MAX){ \ *err = errno = ERANGE; \ *endptr = (str+i); \ return (negitive ? LONG_MIN : LONG_MAX); \ } int start = i; if(base == 10){ while(isdigit(str[i]) && ++i < len); DO_CASE(base); } else if(base == 16){ while(isxdigit(str[i]) && ++i < len); DO_CASE(base); } else if(base == 8){ while(is_oct_digit(str[i]) && ++i < len); DO_CASE(base); } else if(base == 2){ while(is_binary_digit(str[i]) && ++i < len); DO_CASE(base); } else if(base < 10){ i = memspn((uint8_t*)str + i, len - i, valid_digits, base); DO_CASE(base); } else { i = memspn((uint8_t*)str + i, len -i, valid_digits, 10 + (base-10)*2); DO_CASE(base); } if(endptr){*endptr = (str + i);} return (long)(negitive ? -num : num); }