void ProcessingInstruction::checkStyleSheet() { if (m_target == "xml-stylesheet" && document()->frame() && parentNode() == document()) { // see http://www.w3.org/TR/xml-stylesheet/ // ### support stylesheet included in a fragment of this (or another) document // ### make sure this gets called when adding from javascript bool attrsOk; const HashMap<String, String> attrs = parseAttributes(m_data, attrsOk); if (!attrsOk) return; HashMap<String, String>::const_iterator i = attrs.find("type"); String type; if (i != attrs.end()) type = i->value; m_isCSS = type.isEmpty() || type == "text/css"; #if ENABLE(XSLT) m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" || type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml"); if (!m_isCSS && !m_isXSL) #else if (!m_isCSS) #endif return; String href = attrs.get("href"); String alternate = attrs.get("alternate"); m_alternate = alternate == "yes"; m_title = attrs.get("title"); m_media = attrs.get("media"); if (m_alternate && m_title.isEmpty()) return; if (href.length() > 1 && href[0] == '#') { m_localHref = href.substring(1); #if ENABLE(XSLT) // We need to make a synthetic XSLStyleSheet that is embedded. It needs to be able // to kick off import/include loads that can hang off some parent sheet. if (m_isXSL) { KURL finalURL(ParsedURLString, m_localHref); m_sheet = XSLStyleSheet::createEmbedded(this, finalURL); m_loading = false; } #endif } else { if (m_cachedSheet) { m_cachedSheet->removeClient(this); m_cachedSheet = 0; } String url = document()->completeURL(href).string(); if (!dispatchBeforeLoadEvent(url)) return; m_loading = true; document()->styleSheetCollection()->addPendingSheet(); CachedResourceRequest request(ResourceRequest(document()->completeURL(href))); #if ENABLE(XSLT) if (m_isXSL) m_cachedSheet = document()->cachedResourceLoader()->requestXSLStyleSheet(request); else #endif { String charset = attrs.get("charset"); if (charset.isEmpty()) charset = document()->charset(); request.setCharset(charset); m_cachedSheet = document()->cachedResourceLoader()->requestCSSStyleSheet(request); } if (m_cachedSheet) m_cachedSheet->addClient(this); else { // The request may have been denied if (for example) the stylesheet is local and the document is remote. m_loading = false; document()->styleSheetCollection()->removePendingSheet(); } } } }
String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element, size_t* resultDistance, bool* resultIsInCellAbove) { OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels)); // We stop searching after we've seen this many chars const unsigned int charsSearchedThreshold = 500; // This is the absolute max we search. We allow a little more slop than // charsSearchedThreshold, to make it more likely that we'll search whole nodes. const unsigned int maxCharsSearched = 600; // If the starting element is within a table, the cell that contains it HTMLTableCellElement* startingTableCell = 0; bool searchedCellAbove = false; if (resultDistance) *resultDistance = notFound; if (resultIsInCellAbove) *resultIsInCellAbove = false; // walk backwards in the node tree, until another element, or form, or end of tree int unsigned lengthSearched = 0; Node* n; for (n = element->traversePreviousNode(); n && lengthSearched < charsSearchedThreshold; n = n->traversePreviousNode()) { if (n->hasTagName(formTag) || (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement())) { // We hit another form element or the start of the form - bail out break; } else if (n->hasTagName(tdTag) && !startingTableCell) { startingTableCell = static_cast<HTMLTableCellElement*>(n); } else if (n->hasTagName(trTag) && startingTableCell) { String result = searchForLabelsAboveCell(regExp.get(), startingTableCell, resultDistance); if (!result.isEmpty()) { if (resultIsInCellAbove) *resultIsInCellAbove = true; return result; } searchedCellAbove = true; } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) { // For each text chunk, run the regexp String nodeString = n->nodeValue(); // add 100 for slop, to make it more likely that we'll search whole nodes if (lengthSearched + nodeString.length() > maxCharsSearched) nodeString = nodeString.right(charsSearchedThreshold - lengthSearched); int pos = regExp->searchRev(nodeString); if (pos >= 0) { if (resultDistance) *resultDistance = lengthSearched; return nodeString.substring(pos, regExp->matchedLength()); } lengthSearched += nodeString.length(); } } // If we started in a cell, but bailed because we found the start of the form or the // previous element, we still might need to search the row above us for a label. if (startingTableCell && !searchedCellAbove) { String result = searchForLabelsAboveCell(regExp.get(), startingTableCell, resultDistance); if (!result.isEmpty()) { if (resultIsInCellAbove) *resultIsInCellAbove = true; return result; } } return String(); }
String ESP8266WebServer::_exractParam(String& authReq,const String& param,const char delimit){ int _begin = authReq.indexOf(param); if (_begin==-1) return ""; return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length())); }
// // The MathML specification says: // (http://www.w3.org/TR/MathML/chapter2.html#fund.units) // // "Most presentation elements have attributes that accept values representing // lengths to be used for size, spacing or similar properties. The syntax of a // length is specified as // // number | number unit | namedspace // // There should be no space between the number and the unit of a length." // // "A trailing '%' represents a percent of the default value. The default // value, or how it is obtained, is listed in the table of attributes for each // element. [...] A number without a unit is intepreted as a multiple of the // default value." // // "The possible units in MathML are: // // Unit Description // em an em (font-relative unit traditionally used for horizontal lengths) // ex an ex (font-relative unit traditionally used for vertical lengths) // px pixels, or size of a pixel in the current display // in inches (1 inch = 2.54 centimeters) // cm centimeters // mm millimeters // pt points (1 point = 1/72 inch) // pc picas (1 pica = 12 points) // % percentage of default value" // // The numbers are defined that way: // - unsigned-number: "a string of decimal digits with up to one decimal point // (U+002E), representing a non-negative terminating decimal number (a type of // rational number)" // - number: "an optional prefix of '-' (U+002D), followed by an unsigned // number, representing a terminating decimal number (a type of rational // number)" // bool parseMathMLLength(const String& string, LayoutUnit& lengthValue, const RenderStyle* style, bool allowNegative) { String s = string.simplifyWhiteSpace(); int stringLength = s.length(); if (!stringLength) return false; if (parseMathMLNamedSpace(s, lengthValue, style, allowNegative)) return true; StringBuilder number; String unit; // This verifies whether the negative sign is there. int i = 0; UChar c = s[0]; if (c == '-') { number.append(c); i++; } // This gathers up characters that make up the number. bool gotDot = false; for ( ; i < stringLength; i++) { c = s[i]; // The string is invalid if it contains two dots. if (gotDot && c == '.') return false; if (c == '.') gotDot = true; else if (!isASCIIDigit(c)) { unit = s.substring(i, stringLength - i); // Some authors leave blanks before the unit, but that shouldn't // be allowed, so don't simplifyWhitespace on 'unit'. break; } number.append(c); } // Convert number to floating point bool ok; float floatValue = number.toString().toFloat(&ok); if (!ok) return false; if (floatValue < 0 && !allowNegative) return false; if (unit.isEmpty()) { // no explicit unit, this is a number that will act as a multiplier lengthValue *= floatValue; return true; } if (unit == "%") { lengthValue *= floatValue / 100; return true; } if (unit == "em") { lengthValue = floatValue * style->fontCascade().size(); return true; } if (unit == "ex") { lengthValue = floatValue * style->fontMetrics().xHeight(); return true; } if (unit == "px") { lengthValue = floatValue; return true; } if (unit == "pt") { lengthValue = 4 * (floatValue / 3); return true; } if (unit == "pc") { lengthValue = 16 * floatValue; return true; } if (unit == "in") { lengthValue = 96 * floatValue; return true; } if (unit == "cm") { lengthValue = 96 * (floatValue / 2.54); return true; } if (unit == "mm") { lengthValue = 96 * (floatValue / 25.4); return true; } // unexpected unit return false; }
//============================================================================== String File::parseAbsolutePath (const String& p) { if (p.isEmpty()) return String(); #if JUCE_WINDOWS // Windows.. String path (p.replaceCharacter ('/', '\\')); if (path.startsWithChar (separator)) { if (path[1] != separator) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ jassertfalse; path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path; } } else if (! path.containsChar (':')) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ jassertfalse; return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); } #else // Mac or Linux.. // Yes, I know it's legal for a unix pathname to contain a backslash, but this assertion is here // to catch anyone who's trying to run code that was written on Windows with hard-coded path names. // If that's why you've ended up here, use File::getChildFile() to build your paths instead. jassert ((! p.containsChar ('\\')) || (p.indexOfChar ('/') >= 0 && p.indexOfChar ('/') < p.indexOfChar ('\\'))); String path (p); if (path.startsWithChar ('~')) { if (path[1] == separator || path[1] == 0) { // expand a name of the form "~/abc" path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName() + path.substring (1); } else { // expand a name of type "~dave/abc" const String userName (path.substring (1).upToFirstOccurrenceOf ("/", false, false)); if (struct passwd* const pw = getpwnam (userName.toUTF8())) path = addTrailingSeparator (pw->pw_dir) + path.fromFirstOccurrenceOf ("/", false, false); } } else if (! path.startsWithChar (separator)) { #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS if (! (path.startsWith ("./") || path.startsWith ("../"))) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ jassertfalse; #if JUCE_LOG_ASSERTIONS Logger::writeToLog ("Illegal absolute path: " + path); #endif } #endif return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); } #endif while (path.endsWithChar (separator) && path != separatorString) // careful not to turn a single "/" into an empty string. path = path.dropLastCharacters (1); return path; }
bool ESP8266WebServer::authenticate(const char * username, const char * password){ if(hasHeader(AUTHORIZATION_HEADER)){ String authReq = header(AUTHORIZATION_HEADER); if(authReq.startsWith("Basic")){ authReq = authReq.substring(6); authReq.trim(); char toencodeLen = strlen(username)+strlen(password)+1; char *toencode = new char[toencodeLen + 1]; if(toencode == NULL){ authReq = String(); return false; } char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; if(encoded == NULL){ authReq = String(); delete[] toencode; return false; } sprintf(toencode, "%s:%s", username, password); if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) { authReq = String(); delete[] toencode; delete[] encoded; return true; } delete[] toencode; delete[] encoded; }else if(authReq.startsWith("Digest")){ authReq = authReq.substring(7); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.println(authReq); #endif String _username = _exractParam(authReq,"username=\""); if((!_username.length())||_username!=String(username)){ authReq = String(); return false; } // extracting required parameters for RFC 2069 simpler Digest String _realm = _exractParam(authReq,"realm=\""); String _nonce = _exractParam(authReq,"nonce=\""); String _uri = _exractParam(authReq,"uri=\""); String _response = _exractParam(authReq,"response=\""); String _opaque = _exractParam(authReq,"opaque=\""); if((!_realm.length())||(!_nonce.length())||(!_uri.length())||(!_response.length())||(!_opaque.length())){ authReq = String(); return false; } if((_opaque!=_sopaque)||(_nonce!=_snonce)||(_realm!=_srealm)){ authReq = String(); return false; } // parameters for the RFC 2617 newer Digest String _nc,_cnonce; if(authReq.indexOf("qop=auth") != -1){ _nc = _exractParam(authReq,"nc=",','); _cnonce = _exractParam(authReq,"cnonce=\""); } MD5Builder md5; md5.begin(); md5.add(String(username)+":"+_realm+":"+String(password)); // md5 of the user:realm:user md5.calculate(); String _H1 = md5.toString(); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.println("Hash of user:realm:pass="******"GET:"+_uri); }else if(_currentMethod == HTTP_POST){ md5.add("POST:"+_uri); }else if(_currentMethod == HTTP_PUT){ md5.add("PUT:"+_uri); }else if(_currentMethod == HTTP_DELETE){ md5.add("DELETE:"+_uri); }else{ md5.add("GET:"+_uri); } md5.calculate(); String _H2 = md5.toString(); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.println("Hash of GET:uri=" + _H2); #endif md5.begin(); if(authReq.indexOf("qop=auth") != -1){ md5.add(_H1+":"+_nonce+":"+_nc+":"+_cnonce+":auth:"+_H2); }else{ md5.add(_H1+":"+_nonce+":"+_H2); } md5.calculate(); String _responsecheck = md5.toString(); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.println("The Proper response=" +_responsecheck); #endif if(_response==_responsecheck){ authReq = String(); return true; } } authReq = String(); } return false; }
double LocaleWin::parseDate(const Vector<DateFormatToken>& tokens, int baseYear, const String& input) { ensureShortMonthLabels(); ensureMonthLabels(); const double NaN = numeric_limits<double>::quiet_NaN(); unsigned inputIndex = 0; int day = -1, month = -1, year = -1; for (unsigned i = 0; i < tokens.size(); ++i) { switch (tokens[i].type) { case DateFormatToken::Literal: { String data = tokens[i].data; unsigned literalLength = data.length(); if (input.substring(inputIndex, literalLength) == data) inputIndex += literalLength; // Go ahead even if the input doesn't have this string. break; } case DateFormatToken::Day1: case DateFormatToken::Day2: day = parseNumber(input, inputIndex); if (day < 1 || day > 31) return NaN; break; case DateFormatToken::Month1: case DateFormatToken::Month2: case DateFormatToken::Month3: case DateFormatToken::Month4: month = parseNumberOrMonth(input, inputIndex); if (month < 0 || month > 11) return NaN; break; case DateFormatToken::Year1: { unsigned oldIndex = inputIndex; year = parseNumber(input, inputIndex); if (year <= 0) return NaN; if (inputIndex - oldIndex == 1) { int shortYear = baseYear % 10; int decade = baseYear - shortYear; if (shortYear >= 5) year += shortYear - 4 <= year ? decade : decade + 10; else year += shortYear + 5 >= year ? decade : decade - 10; } break; } case DateFormatToken::Year2: { unsigned oldIndex = inputIndex; year = parseNumber(input, inputIndex); if (year <= 0) return NaN; if (inputIndex - oldIndex == 2) { int shortYear = baseYear % 100; int century = baseYear - shortYear; if (shortYear >= 50) year += shortYear - 49 <= year ? century : century + 100; else year += shortYear + 50 >= year ? century : century - 100; } break; } case DateFormatToken::Year4: year = parseNumber(input, inputIndex); if (year <= 0) return NaN; break; } } if (year <= 0 || month < 0 || day <= 0) return NaN; return dateToDaysFrom1970(year, month, day) * msPerDay; }
bool SVGSMILElement::parseCondition(const String& value, BeginOrEnd beginOrEnd) { String parseString = value.stripWhiteSpace(); double sign = 1.; bool ok; size_t pos = parseString.find('+'); if (pos == kNotFound) { pos = parseString.find('-'); if (pos != kNotFound) sign = -1.; } String conditionString; SMILTime offset = 0; if (pos == kNotFound) conditionString = parseString; else { conditionString = parseString.left(pos).stripWhiteSpace(); String offsetString = parseString.substring(pos + 1).stripWhiteSpace(); offset = parseOffsetValue(offsetString); if (offset.isUnresolved()) return false; offset = offset * sign; } if (conditionString.isEmpty()) return false; pos = conditionString.find('.'); String baseID; String nameString; if (pos == kNotFound) nameString = conditionString; else { baseID = conditionString.left(pos); nameString = conditionString.substring(pos + 1); } if (nameString.isEmpty()) return false; Condition::Type type; int repeat = -1; if (nameString.startsWith("repeat(") && nameString.endsWith(')')) { repeat = nameString.substring(7, nameString.length() - 8).toUIntStrict(&ok); if (!ok) return false; nameString = "repeatn"; type = Condition::EventBase; } else if (nameString == "begin" || nameString == "end") { if (baseID.isEmpty()) return false; UseCounter::count(&document(), UseCounter::SVGSMILBeginOrEndSyncbaseValue); type = Condition::Syncbase; } else if (nameString.startsWith("accesskey(")) { // FIXME: accesskey() support. type = Condition::AccessKey; } else { UseCounter::count(&document(), UseCounter::SVGSMILBeginOrEndEventValue); type = Condition::EventBase; } m_conditions.append( Condition::create(type, beginOrEnd, baseID, nameString, offset, repeat)); if (type == Condition::EventBase && beginOrEnd == End) m_hasEndEventConditions = true; return true; }
String TextDiff::Change::appliedTo (const String& text) const noexcept { return text.substring (0, start) + (isDeletion() ? text.substring (start + length) : (insertedText + text.substring (start))); }
uint32_t ESP8266::recvPkg(uint8_t *buffer, uint32_t buffer_size, uint32_t *data_len, uint32_t timeout, uint8_t *coming_mux_id) { String data; char a; int32_t index_PIPDcomma = -1; int32_t index_colon = -1; /* : */ int32_t index_comma = -1; /* , */ int32_t len = -1; int8_t id = -1; bool has_data = false; uint32_t ret; unsigned long start; uint32_t i; if (buffer == NULL) { return 0; } start = millis(); while (millis() - start < timeout) { if(m_puart->available() > 0) { a = m_puart->read(); data += a; } index_PIPDcomma = data.indexOf("+IPD,"); if (index_PIPDcomma != -1) { index_colon = data.indexOf(':', index_PIPDcomma + 5); if (index_colon != -1) { index_comma = data.indexOf(',', index_PIPDcomma + 5); /* +IPD,id,len:data */ if (index_comma != -1 && index_comma < index_colon) { id = data.substring(index_PIPDcomma + 5, index_comma).toInt(); if (id < 0 || id > 4) { return 0; } len = data.substring(index_comma + 1, index_colon).toInt(); if (len <= 0) { return 0; } } else { /* +IPD,len:data */ len = data.substring(index_PIPDcomma + 5, index_colon).toInt(); if (len <= 0) { return 0; } } has_data = true; break; } } } if (has_data) { i = 0; ret = len > buffer_size ? buffer_size : len; start = millis(); while (millis() - start < 3000) { while(m_puart->available() > 0 && i < ret) { a = m_puart->read(); buffer[i++] = a; } if (i == ret) { rx_empty(); if (data_len) { *data_len = len; } if (index_comma != -1 && coming_mux_id) { *coming_mux_id = id; } return ret; } } } return 0; }
bool ESP8266WebServer::_parseRequest(WiFiClient& client) { // Read the first line of HTTP request String req = client.readStringUntil('\r'); client.readStringUntil('\n'); //reset header value for (int i = 0; i < _headerKeysCount; ++i) { _currentHeaders[i].value =String(); } // First line of HTTP request looks like "GET /path HTTP/1.1" // Retrieve the "/path" part by finding the spaces int addr_start = req.indexOf(' '); int addr_end = req.indexOf(' ', addr_start + 1); if (addr_start == -1 || addr_end == -1) { #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Invalid request: "); DEBUG_OUTPUT.println(req); #endif return false; } String methodStr = req.substring(0, addr_start); String url = req.substring(addr_start + 1, addr_end); String versionEnd = req.substring(addr_end + 8); _currentVersion = atoi(versionEnd.c_str()); String searchStr = ""; int hasSearch = url.indexOf('?'); if (hasSearch != -1){ searchStr = url.substring(hasSearch + 1); url = url.substring(0, hasSearch); } _currentUri = url; _chunked = false; HTTPMethod method = HTTP_GET; if (methodStr == "POST") { method = HTTP_POST; } else if (methodStr == "DELETE") { method = HTTP_DELETE; } else if (methodStr == "OPTIONS") { method = HTTP_OPTIONS; } else if (methodStr == "PUT") { method = HTTP_PUT; } else if (methodStr == "PATCH") { method = HTTP_PATCH; } _currentMethod = method; #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("method: "); DEBUG_OUTPUT.print(methodStr); DEBUG_OUTPUT.print(" url: "); DEBUG_OUTPUT.print(url); DEBUG_OUTPUT.print(" search: "); DEBUG_OUTPUT.println(searchStr); #endif //attach handler RequestHandler* handler; for (handler = _firstHandler; handler; handler = handler->next()) { if (handler->canHandle(_currentMethod, _currentUri)) break; } _currentHandler = handler; String formData; // below is needed only when POST type request if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){ String boundaryStr; String headerName; String headerValue; bool isForm = false; bool isEncoded = false; uint32_t contentLength = 0; //parse headers while(1){ req = client.readStringUntil('\r'); client.readStringUntil('\n'); if (req == "") break;//no moar headers int headerDiv = req.indexOf(':'); if (headerDiv == -1){ break; } headerName = req.substring(0, headerDiv); headerValue = req.substring(headerDiv + 1); headerValue.trim(); _collectHeader(headerName.c_str(),headerValue.c_str()); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("headerName: "); DEBUG_OUTPUT.println(headerName); DEBUG_OUTPUT.print("headerValue: "); DEBUG_OUTPUT.println(headerValue); #endif if (headerName.equalsIgnoreCase("Content-Type")){ if (headerValue.startsWith("text/plain")){ isForm = false; } else if (headerValue.startsWith("application/x-www-form-urlencoded")){ isForm = false; isEncoded = true; } else if (headerValue.startsWith("multipart/")){ boundaryStr = headerValue.substring(headerValue.indexOf('=')+1); isForm = true; } } else if (headerName.equalsIgnoreCase("Content-Length")){ contentLength = headerValue.toInt(); } else if (headerName.equalsIgnoreCase("Host")){ _hostHeader = headerValue; } } if (!isForm){ size_t plainLength; char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT); if (plainLength < contentLength) { free(plainBuf); return false; } if (contentLength > 0) { if (searchStr != "") searchStr += '&'; if(isEncoded){ //url encoded form String decoded = urlDecode(plainBuf); size_t decodedLen = decoded.length(); memcpy(plainBuf, decoded.c_str(), decodedLen); plainBuf[decodedLen] = 0; searchStr += plainBuf; } _parseArguments(searchStr); if(!isEncoded){ //plain post json or other data RequestArgument& arg = _currentArgs[_currentArgCount++]; arg.key = "plain"; arg.value = String(plainBuf); } #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Plain: "); DEBUG_OUTPUT.println(plainBuf); #endif free(plainBuf); } } if (isForm){ _parseArguments(searchStr); if (!_parseForm(client, boundaryStr, contentLength)) { return false; } } } else { String headerName; String headerValue; //parse headers while(1){ req = client.readStringUntil('\r'); client.readStringUntil('\n'); if (req == "") break;//no moar headers int headerDiv = req.indexOf(':'); if (headerDiv == -1){ break; } headerName = req.substring(0, headerDiv); headerValue = req.substring(headerDiv + 2); _collectHeader(headerName.c_str(),headerValue.c_str()); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("headerName: "); DEBUG_OUTPUT.println(headerName); DEBUG_OUTPUT.print("headerValue: "); DEBUG_OUTPUT.println(headerValue); #endif if (headerName.equalsIgnoreCase("Host")){ _hostHeader = headerValue; } } _parseArguments(searchStr); } client.flush(); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Request: "); DEBUG_OUTPUT.println(url); DEBUG_OUTPUT.print(" Arguments: "); DEBUG_OUTPUT.println(searchStr); #endif return true; }
bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){ (void) len; #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Parse Form: Boundary: "); DEBUG_OUTPUT.print(boundary); DEBUG_OUTPUT.print(" Length: "); DEBUG_OUTPUT.println(len); #endif String line; int retry = 0; do { line = client.readStringUntil('\r'); ++retry; } while (line.length() == 0 && retry < 3); client.readStringUntil('\n'); //start reading the form if (line == ("--"+boundary)){ RequestArgument* postArgs = new RequestArgument[32]; int postArgsLen = 0; while(1){ String argName; String argValue; String argType; String argFilename; bool argIsFile = false; line = client.readStringUntil('\r'); client.readStringUntil('\n'); if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase("Content-Disposition")){ int nameStart = line.indexOf('='); if (nameStart != -1){ argName = line.substring(nameStart+2); nameStart = argName.indexOf('='); if (nameStart == -1){ argName = argName.substring(0, argName.length() - 1); } else { argFilename = argName.substring(nameStart+2, argName.length() - 1); argName = argName.substring(0, argName.indexOf('"')); argIsFile = true; #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("PostArg FileName: "); DEBUG_OUTPUT.println(argFilename); #endif //use GET to set the filename if uploading using blob if (argFilename == "blob" && hasArg("filename")) argFilename = arg("filename"); } #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("PostArg Name: "); DEBUG_OUTPUT.println(argName); #endif argType = "text/plain"; line = client.readStringUntil('\r'); client.readStringUntil('\n'); if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase("Content-Type")){ argType = line.substring(line.indexOf(':')+2); //skip next line client.readStringUntil('\r'); client.readStringUntil('\n'); } #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("PostArg Type: "); DEBUG_OUTPUT.println(argType); #endif if (!argIsFile){ while(1){ line = client.readStringUntil('\r'); client.readStringUntil('\n'); if (line.startsWith("--"+boundary)) break; if (argValue.length() > 0) argValue += "\n"; argValue += line; } #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("PostArg Value: "); DEBUG_OUTPUT.println(argValue); DEBUG_OUTPUT.println(); #endif RequestArgument& arg = postArgs[postArgsLen++]; arg.key = argName; arg.value = argValue; if (line == ("--"+boundary+"--")){ #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.println("Done Parsing POST"); #endif break; } } else { _currentUpload.status = UPLOAD_FILE_START; _currentUpload.name = argName; _currentUpload.filename = argFilename; _currentUpload.type = argType; _currentUpload.totalSize = 0; _currentUpload.currentSize = 0; #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Start File: "); DEBUG_OUTPUT.print(_currentUpload.filename); DEBUG_OUTPUT.print(" Type: "); DEBUG_OUTPUT.println(_currentUpload.type); #endif if(_currentHandler && _currentHandler->canUpload(_currentUri)) _currentHandler->upload(*this, _currentUri, _currentUpload); _currentUpload.status = UPLOAD_FILE_WRITE; uint8_t argByte = _uploadReadByte(client); readfile: while(argByte != 0x0D){ if (!client.connected()) return _parseFormUploadAborted(); _uploadWriteByte(argByte); argByte = _uploadReadByte(client); } argByte = _uploadReadByte(client); if (!client.connected()) return _parseFormUploadAborted(); if (argByte == 0x0A){ argByte = _uploadReadByte(client); if (!client.connected()) return _parseFormUploadAborted(); if ((char)argByte != '-'){ //continue reading the file _uploadWriteByte(0x0D); _uploadWriteByte(0x0A); goto readfile; } else { argByte = _uploadReadByte(client); if (!client.connected()) return _parseFormUploadAborted(); if ((char)argByte != '-'){ //continue reading the file _uploadWriteByte(0x0D); _uploadWriteByte(0x0A); _uploadWriteByte((uint8_t)('-')); goto readfile; } } uint8_t endBuf[boundary.length()]; client.readBytes(endBuf, boundary.length()); if (strstr((const char*)endBuf, boundary.c_str()) != NULL){ if(_currentHandler && _currentHandler->canUpload(_currentUri)) _currentHandler->upload(*this, _currentUri, _currentUpload); _currentUpload.totalSize += _currentUpload.currentSize; _currentUpload.status = UPLOAD_FILE_END; if(_currentHandler && _currentHandler->canUpload(_currentUri)) _currentHandler->upload(*this, _currentUri, _currentUpload); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("End File: "); DEBUG_OUTPUT.print(_currentUpload.filename); DEBUG_OUTPUT.print(" Type: "); DEBUG_OUTPUT.print(_currentUpload.type); DEBUG_OUTPUT.print(" Size: "); DEBUG_OUTPUT.println(_currentUpload.totalSize); #endif line = client.readStringUntil(0x0D); client.readStringUntil(0x0A); if (line == "--"){ #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.println("Done Parsing POST"); #endif break; } continue; } else { _uploadWriteByte(0x0D); _uploadWriteByte(0x0A); _uploadWriteByte((uint8_t)('-')); _uploadWriteByte((uint8_t)('-')); uint32_t i = 0; while(i < boundary.length()){ _uploadWriteByte(endBuf[i++]); } argByte = _uploadReadByte(client); goto readfile; } } else { _uploadWriteByte(0x0D); goto readfile; } break; } } } } int iarg; int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount; for (iarg = 0; iarg < totalArgs; iarg++){ RequestArgument& arg = postArgs[postArgsLen++]; arg.key = _currentArgs[iarg].key; arg.value = _currentArgs[iarg].value; } if (_currentArgs) delete[] _currentArgs; _currentArgs = new RequestArgument[postArgsLen]; for (iarg = 0; iarg < postArgsLen; iarg++){ RequestArgument& arg = _currentArgs[iarg]; arg.key = postArgs[iarg].key; arg.value = postArgs[iarg].value; } _currentArgCount = iarg; if (postArgs) delete[] postArgs; return true; } #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Error: line: "); DEBUG_OUTPUT.println(line); #endif return false; }
void ESP8266WebServer::_parseArguments(String data) { #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("args: "); DEBUG_OUTPUT.println(data); #endif if (_currentArgs) delete[] _currentArgs; _currentArgs = 0; if (data.length() == 0) { _currentArgCount = 0; _currentArgs = new RequestArgument[1]; return; } _currentArgCount = 1; for (int i = 0; i < (int)data.length(); ) { i = data.indexOf('&', i); if (i == -1) break; ++i; ++_currentArgCount; } #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("args count: "); DEBUG_OUTPUT.println(_currentArgCount); #endif _currentArgs = new RequestArgument[_currentArgCount+1]; int pos = 0; int iarg; for (iarg = 0; iarg < _currentArgCount;) { int equal_sign_index = data.indexOf('=', pos); int next_arg_index = data.indexOf('&', pos); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("pos "); DEBUG_OUTPUT.print(pos); DEBUG_OUTPUT.print("=@ "); DEBUG_OUTPUT.print(equal_sign_index); DEBUG_OUTPUT.print(" &@ "); DEBUG_OUTPUT.println(next_arg_index); #endif if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) { #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("arg missing value: "); DEBUG_OUTPUT.println(iarg); #endif if (next_arg_index == -1) break; pos = next_arg_index + 1; continue; } RequestArgument& arg = _currentArgs[iarg]; arg.key = data.substring(pos, equal_sign_index); arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index)); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("arg "); DEBUG_OUTPUT.print(iarg); DEBUG_OUTPUT.print(" key: "); DEBUG_OUTPUT.print(arg.key); DEBUG_OUTPUT.print(" value: "); DEBUG_OUTPUT.println(arg.value); #endif ++iarg; if (next_arg_index == -1) break; pos = next_arg_index + 1; } _currentArgCount = iarg; #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("args count: "); DEBUG_OUTPUT.println(_currentArgCount); #endif }
String XmlDocument::expandExternalEntity (const String& entity) { if (needToLoadDTD) { if (dtdText.isNotEmpty()) { dtdText = dtdText.trimCharactersAtEnd (">"); tokenisedDTD.addTokens (dtdText, true); if (tokenisedDTD [tokenisedDTD.size() - 2].equalsIgnoreCase ("system") && tokenisedDTD [tokenisedDTD.size() - 1].isQuotedString()) { const String fn (tokenisedDTD [tokenisedDTD.size() - 1]); tokenisedDTD.clear(); tokenisedDTD.addTokens (getFileContents (fn), true); } else { tokenisedDTD.clear(); const int openBracket = dtdText.indexOfChar ('['); if (openBracket > 0) { const int closeBracket = dtdText.lastIndexOfChar (']'); if (closeBracket > openBracket) tokenisedDTD.addTokens (dtdText.substring (openBracket + 1, closeBracket), true); } } for (int i = tokenisedDTD.size(); --i >= 0;) { if (tokenisedDTD[i].startsWithChar ('%') && tokenisedDTD[i].endsWithChar (';')) { const String parsed (getParameterEntity (tokenisedDTD[i].substring (1, tokenisedDTD[i].length() - 1))); StringArray newToks; newToks.addTokens (parsed, true); tokenisedDTD.remove (i); for (int j = newToks.size(); --j >= 0;) tokenisedDTD.insert (i, newToks[j]); } } } needToLoadDTD = false; } for (int i = 0; i < tokenisedDTD.size(); ++i) { if (tokenisedDTD[i] == entity) { if (tokenisedDTD[i - 1].equalsIgnoreCase ("<!entity")) { String ent (tokenisedDTD [i + 1].trimCharactersAtEnd (">").trim().unquoted()); // check for sub-entities.. int ampersand = ent.indexOfChar ('&'); while (ampersand >= 0) { const int semiColon = ent.indexOf (i + 1, ";"); if (semiColon < 0) { setLastError ("entity without terminating semi-colon", false); break; } const String resolved (expandEntity (ent.substring (i + 1, semiColon))); ent = ent.substring (0, ampersand) + resolved + ent.substring (semiColon + 1); ampersand = ent.indexOfChar (semiColon + 1, '&'); } return ent; } } } setLastError ("unknown entity", true); return entity; }
void LocaleWin::initializeLocalizerData() { if (m_didInitializeNumberData) return; Vector<String, DecimalSymbolsSize> symbols; enum DigitSubstitution { DigitSubstitutionContext = 0, DigitSubstitution0to9 = 1, DigitSubstitutionNative = 2, }; DWORD digitSubstitution = DigitSubstitution0to9; getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, digitSubstitution); if (digitSubstitution == DigitSubstitution0to9) { symbols.append("0"); symbols.append("1"); symbols.append("2"); symbols.append("3"); symbols.append("4"); symbols.append("5"); symbols.append("6"); symbols.append("7"); symbols.append("8"); symbols.append("9"); } else { String digits = getLocaleInfoString(LOCALE_SNATIVEDIGITS); ASSERT(digits.length() >= 10); for (unsigned i = 0; i < 10; ++i) symbols.append(digits.substring(i, 1)); } ASSERT(symbols.size() == DecimalSeparatorIndex); symbols.append(getLocaleInfoString(LOCALE_SDECIMAL)); ASSERT(symbols.size() == GroupSeparatorIndex); symbols.append(getLocaleInfoString(LOCALE_STHOUSAND)); ASSERT(symbols.size() == DecimalSymbolsSize); String negativeSign = getLocaleInfoString(LOCALE_SNEGATIVESIGN); enum NegativeFormat { NegativeFormatParenthesis = 0, NegativeFormatSignPrefix = 1, NegativeFormatSignSpacePrefix = 2, NegativeFormatSignSuffix = 3, NegativeFormatSpaceSignSuffix = 4, }; DWORD negativeFormat = NegativeFormatSignPrefix; getLocaleInfo(LOCALE_INEGNUMBER, negativeFormat); String negativePrefix = emptyString(); String negativeSuffix = emptyString(); switch (negativeFormat) { case NegativeFormatParenthesis: negativePrefix = "("; negativeSuffix = ")"; break; case NegativeFormatSignSpacePrefix: negativePrefix = negativeSign + " "; break; case NegativeFormatSignSuffix: negativeSuffix = negativeSign; break; case NegativeFormatSpaceSignSuffix: negativeSuffix = " " + negativeSign; break; case NegativeFormatSignPrefix: // Fall through. default: negativePrefix = negativeSign; break; } m_didInitializeNumberData = true; setLocalizerData(symbols, emptyString(), emptyString(), negativePrefix, negativeSuffix); }
/** * Set the root path to be used as the working directory * * @param rootPath const wchar_t* * @return bool */ bool AlfrescoInterface::setRootPath( const wchar_t* rootPath) { // Close the existing folder, if valid if ( m_handle != INVALID_HANDLE_VALUE) CloseHandle(m_handle); // Clear the root path m_rootPath = ""; // Check if the path is to a mapped drive String path = rootPath; String alfPath = rootPath; if ( alfPath.length() >= 2 && alfPath.charAt(1) == ':') { // Try and convert the local path to a UNC path m_mappedDrive = alfPath.substring(0, 2); wchar_t remPath[MAX_PATH]; DWORD remPathLen = MAX_PATH; DWORD sts = WNetGetConnection(( LPWSTR) m_mappedDrive.data(), remPath, &remPathLen); if ( sts != NO_ERROR) return false; // Build the UNC path to the folder alfPath = remPath; if ( alfPath.endsWith( PathSeperator) == false) alfPath.append( PathSeperator); m_rootPath = alfPath; // Build the full UNC path to the target if ( path.length() > 3) alfPath.append( path.substring( 3)); } // Save the UNC path m_uncPath = alfPath; // Check if the UNC path is valid if ( m_uncPath.startsWith(UNCPathPrefix)) { // Strip any trailing separator from the path if ( m_uncPath.endsWith(PathSeperator)) m_uncPath = m_uncPath.substring(0, m_uncPath.length() - 1); // Make sure the path is to a folder DWORD attr = GetFileAttributes(m_uncPath); if ( attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY)) { // Open the path m_handle = CreateFile(m_uncPath, FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if ( m_handle == INVALID_HANDLE_VALUE) { // DEBUG if ( HAS_DEBUG) DBGOUT_TS << "%% Error opening folder " << m_uncPath << ", error " << GetLastError() << endl; // Error, failed to open folder on Alfresco CIFS share return false; } } // Set the root path if ( m_rootPath.length() == 0) { int pos = m_uncPath.indexOf( PathSeperator, 2); if ( pos != -1) { pos = m_uncPath.indexOf( PathSeperator, pos + 1); if ( pos == -1) m_rootPath = m_uncPath; else m_rootPath = m_uncPath.substring(0, pos); } } } // Return the folder status return isAlfrescoFolder(); }
CIMFeatures HTTPClient::getFeatures() { m_statusCode = -1; String methodOrig = m_requestMethod; m_requestMethod = "OPTIONS"; prepareHeaders(); String reasonPhrase; Resp_t rt = RETRY; do { checkConnection(); handleAuth(); sendHeaders(m_requestMethod, "HTTP/1.1"); m_ostr.flush(); m_requestHeadersNew.clear(); m_responseHeaders.clear(); m_statusLine.erase(); reasonPhrase = checkResponse(rt); } while (rt == RETRY); m_requestMethod = methodOrig; if (rt == FATAL) { OW_THROW_ERR(HTTPException, Format("Unable to process request: %1", reasonPhrase).c_str(), m_statusCode); } if (getHeaderValue("allow").indexOf("M-POST") == String::npos) { m_requestMethod = "POST"; } if (getHeaderValue("Accept-Encoding").indexOf("deflate") != String::npos) { m_doDeflateOut = true; } String extURL = getHeaderValue("Opt"); size_t idx = extURL.indexOf(';'); if (idx < 1 || idx == String::npos) { OW_THROW_ERR(HTTPException, "No \"Opt\" header in OPTIONS response", m_statusCode); } CIMFeatures rval; rval.extURL = extURL.substring(0, idx); rval.extURL.trim(); String hp = extURL.substring(idx + 1); idx = hp.indexOf("="); hp = hp.substring(idx + 1); hp.trim(); if (hp.length() != 2) { OW_THROW_ERR(HTTPException, "HTTP Ext header prefix is not a two digit " "number", m_statusCode); } hp += "-"; rval.protocolVersion = getHeaderValue(hp + "CIMProtocolVersion"); String supportedGroups; rval.supportsBatch = false; if (headerHasKey(hp + "CIMSupportedFunctionalGroups")) { rval.cimProduct = CIMFeatures::SERVER; supportedGroups = getHeaderValue(hp + "CIMSupportedFunctionalGroups"); if (headerHasKey(hp + "CIMSupportsMultipleOperations")) { rval.supportsBatch = true; } } else if (headerHasKey(hp + "CIMSupportedExportGroups")) { rval.cimProduct = CIMFeatures::LISTENER; supportedGroups = getHeaderValue(hp + "CIMSupportedExportGroups"); if (headerHasKey(hp + "CIMSupportsMultipleExports")) { rval.supportsBatch = true; } } else { OW_THROW_ERR(HTTPException, "No CIMSupportedFunctionalGroups or " "CIMSupportedExportGroups header", m_statusCode); } rval.supportedGroups = supportedGroups.tokenize(","); for (size_t i = 0; i < rval.supportedGroups.size(); i++) { rval.supportedGroups[i].trim(); } rval.supportedQueryLanguages = getHeaderValue(hp + "CIMSupportedQueryLanguages").tokenize(","); for (size_t i = 0; i < rval.supportedQueryLanguages.size(); i++) { rval.supportedQueryLanguages[i].trim(); } rval.cimom = getHeaderValue(hp + "CIMOM"); rval.validation = getHeaderValue(hp + "CIMValidation"); return rval; }
String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, bool& outIsSpelling, int& outFirstFoundOffset, GrammarDetail& outGrammarDetail) { if (!unifiedTextCheckerEnabled()) return ""; String firstFoundItem; String misspelledWord; String badGrammarPhrase; // Initialize out parameters; these will be updated if we find something to return. outIsSpelling = true; outFirstFoundOffset = 0; outGrammarDetail.location = -1; outGrammarDetail.length = 0; outGrammarDetail.guesses.clear(); outGrammarDetail.userDescription = ""; // Expand the search range to encompass entire paragraphs, since text checking needs that much context. // Determine the character offset from the start of the paragraph to the start of the original search range, // since we will want to ignore results in this area. RefPtr<Range> paragraphRange = m_range->cloneRange(IGNORE_EXCEPTION); setStart(paragraphRange.get(), startOfParagraph(m_range->startPosition())); int totalRangeLength = TextIterator::rangeLength(paragraphRange.get()); setEnd(paragraphRange.get(), endOfParagraph(m_range->startPosition())); RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer()->document(), paragraphRange->startPosition(), m_range->startPosition()); int rangeStartOffset = TextIterator::rangeLength(offsetAsRange.get()); int totalLengthProcessed = 0; bool firstIteration = true; bool lastIteration = false; while (totalLengthProcessed < totalRangeLength) { // Iterate through the search range by paragraphs, checking each one for spelling and grammar. int currentLength = TextIterator::rangeLength(paragraphRange.get()); int currentStartOffset = firstIteration ? rangeStartOffset : 0; int currentEndOffset = currentLength; if (inSameParagraph(paragraphRange->startPosition(), m_range->endPosition())) { // Determine the character offset from the end of the original search range to the end of the paragraph, // since we will want to ignore results in this area. RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), paragraphRange->startPosition(), m_range->endPosition()); currentEndOffset = TextIterator::rangeLength(endOffsetAsRange.get()); lastIteration = true; } if (currentStartOffset < currentEndOffset) { String paragraphString = plainText(paragraphRange.get()); if (paragraphString.length() > 0) { bool foundGrammar = false; int spellingLocation = 0; int grammarPhraseLocation = 0; int grammarDetailLocation = 0; unsigned grammarDetailIndex = 0; Vector<TextCheckingResult> results; TextCheckingTypeMask checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling; checkTextOfParagraph(*m_client->textChecker(), paragraphString, checkingTypes, results); for (unsigned i = 0; i < results.size(); i++) { const TextCheckingResult* result = &results[i]; if (result->type == TextCheckingTypeSpelling && result->location >= currentStartOffset && result->location + result->length <= currentEndOffset) { ASSERT(result->length > 0); ASSERT(result->location >= 0); spellingLocation = result->location; misspelledWord = paragraphString.substring(result->location, result->length); ASSERT(misspelledWord.length()); break; } if (checkGrammar && result->type == TextCheckingTypeGrammar && result->location < currentEndOffset && result->location + result->length > currentStartOffset) { ASSERT(result->length > 0); ASSERT(result->location >= 0); // We can't stop after the first grammar result, since there might still be a spelling result after // it begins but before the first detail in it, but we can stop if we find a second grammar result. if (foundGrammar) break; for (unsigned j = 0; j < result->details.size(); j++) { const GrammarDetail* detail = &result->details[j]; ASSERT(detail->length > 0); ASSERT(detail->location >= 0); if (result->location + detail->location >= currentStartOffset && result->location + detail->location + detail->length <= currentEndOffset && (!foundGrammar || result->location + detail->location < grammarDetailLocation)) { grammarDetailIndex = j; grammarDetailLocation = result->location + detail->location; foundGrammar = true; } } if (foundGrammar) { grammarPhraseLocation = result->location; outGrammarDetail = result->details[grammarDetailIndex]; badGrammarPhrase = paragraphString.substring(result->location, result->length); ASSERT(badGrammarPhrase.length()); } } } if (!misspelledWord.isEmpty() && (!checkGrammar || badGrammarPhrase.isEmpty() || spellingLocation <= grammarDetailLocation)) { int spellingOffset = spellingLocation - currentStartOffset; if (!firstIteration) { RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), m_range->startPosition(), paragraphRange->startPosition()); spellingOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get()); } outIsSpelling = true; outFirstFoundOffset = spellingOffset; firstFoundItem = misspelledWord; break; } if (checkGrammar && !badGrammarPhrase.isEmpty()) { int grammarPhraseOffset = grammarPhraseLocation - currentStartOffset; if (!firstIteration) { RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), m_range->startPosition(), paragraphRange->startPosition()); grammarPhraseOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get()); } outIsSpelling = false; outFirstFoundOffset = grammarPhraseOffset; firstFoundItem = badGrammarPhrase; break; } } } if (lastIteration || totalLengthProcessed + currentLength >= totalRangeLength) break; VisiblePosition newParagraphStart = startOfNextParagraph(paragraphRange->endPosition()); setStart(paragraphRange.get(), newParagraphStart); setEnd(paragraphRange.get(), endOfParagraph(newParagraphStart)); firstIteration = false; totalLengthProcessed += currentLength; } return firstFoundItem; }
static URL processAndCreateYouTubeURL(const URL& url, bool& isYouTubeShortenedURL) { if (!url.protocolIs("http") && !url.protocolIs("https")) return URL(); // Bail out early if we aren't even on www.youtube.com or youtube.com. if (!isYouTubeURL(url)) return URL(); const String& hostName = url.host().lower(); bool isYouTubeMobileWebAppURL = hostName == "m.youtube.com"; isYouTubeShortenedURL = hostName == "youtu.be"; // Short URL of the form: http://youtu.be/v1d301D if (isYouTubeShortenedURL) { const String& videoID = url.lastPathComponent(); if (videoID.isEmpty() || videoID == "/") return URL(); return createYouTubeURL(videoID, emptyString()); } String path = url.path(); String query = url.query(); String fragment = url.fragmentIdentifier(); // On the YouTube mobile web app, the path and query string are put into the // fragment so that one web page is only ever loaded (see <rdar://problem/9550639>). if (isYouTubeMobileWebAppURL) { size_t location = fragment.find('?'); if (location == notFound) { path = fragment; query = emptyString(); } else { path = fragment.substring(0, location); query = fragment.substring(location + 1); } fragment = emptyString(); } if (path.lower() == "/watch") { if (!query.isEmpty()) { const auto& queryDictionary = queryKeysAndValues(query); String videoID = valueForKey(queryDictionary, "v"); if (!videoID.isEmpty()) { const auto& fragmentDictionary = queryKeysAndValues(url.fragmentIdentifier()); String timeID = valueForKey(fragmentDictionary, "t"); return createYouTubeURL(videoID, timeID); } } // May be a new-style link (see <rdar://problem/7733692>). if (fragment.startsWith('!')) { query = fragment.substring(1); if (!query.isEmpty()) { const auto& queryDictionary = queryKeysAndValues(query); String videoID = valueForKey(queryDictionary, "v"); if (!videoID.isEmpty()) { String timeID = valueForKey(queryDictionary, "t"); return createYouTubeURL(videoID, timeID); } } } } else if (hasCaseInsensitivePrefix(path, "/v/") || hasCaseInsensitivePrefix(path, "/e/")) { String videoID = url.lastPathComponent(); // These URLs are funny - they don't have a ? for the first query parameter. // Strip all characters after and including '&' to remove extraneous parameters after the video ID. size_t ampersand = videoID.find('&'); if (ampersand != notFound) videoID = videoID.substring(0, ampersand); if (!videoID.isEmpty()) return createYouTubeURL(videoID, emptyString()); } return URL(); }