bool AbstractParser::parseField(StringView words) { #define PARSE_FIELD(X) \ for (const auto x : DEFINED_ROOM_##X##_TYPES) { \ if (getParserCommandName(x).matches(firstWord)) { \ setRoomFieldCommand(x, RoomField::X##_TYPE); \ return true; \ } \ } if (words.isEmpty()) return false; // REVISIT: support "set room field XXX" ? const auto firstWord = words.takeFirstWord(); if (!words.isEmpty()) return false; PARSE_FIELD(LIGHT); PARSE_FIELD(SUNDEATH); PARSE_FIELD(PORTABLE); PARSE_FIELD(RIDABLE); PARSE_FIELD(ALIGN); return false; #undef PARSE }
void AbstractParser::parseName(StringView view) { if (!view.isEmpty()) { auto dir = tryGetDir(view); if (!view.isEmpty()) { auto name = view.takeFirstWord(); nameDoorCommand(name.toQString(), dir); return; } } showSyntax("name <dir> <name>"); }
IDBKeyPathLexer::TokenType IDBKeyPathLexer::lexIdentifier(String& element) { StringView start = m_remainingText; if (!m_remainingText.isEmpty() && isIdentifierStartCharacter(m_remainingText[0])) m_remainingText = m_remainingText.substring(1); else return TokenError; while (!m_remainingText.isEmpty() && isIdentifierCharacter(m_remainingText[0])) m_remainingText = m_remainingText.substring(1); element = start.substring(0, start.length() - m_remainingText.length()).toString(); return TokenIdentifier; }
void Settings::remove(const StringView& _name) const { ini_t* ini = INI_T(m_ini); FilePath uri(_name); const StringView path = strTrim(uri.getPath(), "/"); const StringView& fileName = uri.getFileName(); int32_t section = INI_GLOBAL_SECTION; if (!path.isEmpty() ) { section = ini_find_section(ini, path.getPtr(), path.getLength() ); if (INI_NOT_FOUND == section) { section = INI_GLOBAL_SECTION; } } int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() ); if (INI_NOT_FOUND == property) { return; } ini_property_remove(ini, section, property); if (INI_GLOBAL_SECTION != section && 0 == ini_property_count(ini, section) ) { ini_section_remove(ini, section); } }
const char* Settings::get(const StringView& _name) const { ini_t* ini = INI_T(m_ini); FilePath uri(_name); const StringView path(strTrim(uri.getPath(), "/") ); const StringView& fileName(uri.getFileName() ); int32_t section = INI_GLOBAL_SECTION; if (!path.isEmpty() ) { section = ini_find_section(ini, path.getPtr(), path.getLength() ); if (INI_NOT_FOUND == section) { section = INI_GLOBAL_SECTION; } } int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() ); if (INI_NOT_FOUND == property) { return NULL; } return ini_property_value(ini, section, property); }
TextEncoding HTMLMetaCharsetParser::encodingFromMetaAttributes(const AttributeList& attributes) { bool gotPragma = false; enum { None, Charset, Pragma } mode = None; StringView charset; for (auto& attribute : attributes) { const String& attributeName = attribute.first; const String& attributeValue = attribute.second; if (attributeName == http_equivAttr) { if (equalIgnoringCase(attributeValue, "content-type")) gotPragma = true; } else if (charset.isEmpty()) { if (attributeName == charsetAttr) { charset = attributeValue; mode = Charset; } else if (attributeName == contentAttr) { charset = extractCharset(attributeValue); if (charset.length()) mode = Pragma; } } } if (mode == Charset || (mode == Pragma && gotPragma)) return TextEncoding(stripLeadingAndTrailingHTMLSpaces(charset.toStringWithoutCopying())); return TextEncoding(); }
static void setWindowFeature(WindowFeatures& features, StringView key, StringView value) { // Listing a key with no value is shorthand for key=yes int numericValue; if (value.isEmpty() || equalLettersIgnoringASCIICase(value, "yes")) numericValue = 1; else numericValue = value.toInt(); // We treat key of "resizable" here as an additional feature rather than setting resizeable to true. // This is consistent with Firefox, but could also be handled at another level. if (equalLettersIgnoringASCIICase(key, "left") || equalLettersIgnoringASCIICase(key, "screenx")) features.x = numericValue; else if (equalLettersIgnoringASCIICase(key, "top") || equalLettersIgnoringASCIICase(key, "screeny")) features.y = numericValue; else if (equalLettersIgnoringASCIICase(key, "width") || equalLettersIgnoringASCIICase(key, "innerwidth")) features.width = numericValue; else if (equalLettersIgnoringASCIICase(key, "height") || equalLettersIgnoringASCIICase(key, "innerheight")) features.height = numericValue; else if (equalLettersIgnoringASCIICase(key, "menubar")) features.menuBarVisible = numericValue; else if (equalLettersIgnoringASCIICase(key, "toolbar")) features.toolBarVisible = numericValue; else if (equalLettersIgnoringASCIICase(key, "location")) features.locationBarVisible = numericValue; else if (equalLettersIgnoringASCIICase(key, "status")) features.statusBarVisible = numericValue; else if (equalLettersIgnoringASCIICase(key, "fullscreen")) features.fullscreen = numericValue; else if (equalLettersIgnoringASCIICase(key, "scrollbars")) features.scrollbarsVisible = numericValue; else if (numericValue == 1) features.additionalFeatures.append(key.toString()); }
void AbstractParser::parseDirections(StringView view) { if (view.isEmpty()) showSyntax("dirs [-(name|desc|dyncdesc|note|exits|all)] pattern"); else doGetDirectionsCommand(view); }
// https://wicg.github.io/entries-api/#resolve-a-relative-path static String resolveRelativeVirtualPath(StringView baseVirtualPath, StringView relativeVirtualPath) { ASSERT(baseVirtualPath[0] == '/'); if (!relativeVirtualPath.isEmpty() && relativeVirtualPath[0] == '/') return relativeVirtualPath.length() == 1 ? relativeVirtualPath.toString() : resolveRelativeVirtualPath("/", relativeVirtualPath.substring(1)); Vector<StringView> virtualPathSegments; for (auto segment : baseVirtualPath.split('/')) virtualPathSegments.append(segment); for (auto segment : relativeVirtualPath.split('/')) { ASSERT(!segment.isEmpty()); if (segment == ".") continue; if (segment == "..") { if (!virtualPathSegments.isEmpty()) virtualPathSegments.removeLast(); continue; } virtualPathSegments.append(segment); } if (virtualPathSegments.isEmpty()) return "/"_s; StringBuilder builder; for (auto& segment : virtualPathSegments) { builder.append('/'); builder.append(segment); } return builder.toString(); }
void AbstractParser::parseSearch(StringView view) { if (view.isEmpty()) showSyntax("search [-(name|desc|dyncdesc|note|exits|all)] pattern"); else doSearchCommand(view); }
WindowFeatures parseWindowFeatures(StringView featuresString) { // The IE rule is: all features except for channelmode and fullscreen default to YES, but // if the user specifies a feature string, all features default to NO. (There is no public // standard that applies to this method.) // // <http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/open_0.asp> // We always allow a window to be resized, which is consistent with Firefox. WindowFeatures features; if (featuresString.isEmpty()) return features; features.menuBarVisible = false; features.statusBarVisible = false; features.toolBarVisible = false; features.locationBarVisible = false; features.scrollbarsVisible = false; processFeaturesString(featuresString, [&features](StringView key, StringView value) { setWindowFeature(features, key, value); }); return features; }
bool AbstractParser::parseExitFlag(const ExitFlag flag, StringView words) { const auto dir = tryGetDir(words); if (!words.isEmpty()) return false; toggleExitFlagCommand(flag, dir); return true; }
bool AbstractParser::parseLoadFlags(StringView words) { if (words.isEmpty()) return false; const auto firstWord = words.takeFirstWord(); if (!words.isEmpty()) return false; for (const RoomLoadFlag loadFlag : ALL_LOAD_FLAGS) { if (getParserCommandName(loadFlag).matches(firstWord)) { toggleRoomFlagCommand(loadFlag, RoomField::LOAD_FLAGS); return true; } } return false; }
bool AbstractParser::parseDoorAction(const DoorActionType dat, StringView words) { const auto dir = tryGetDir(words); if (!words.isEmpty()) return false; performDoorCommand(dir, dat); return true; }
void AbstractParser::parseGroupTell(const StringView &view) { if (view.isEmpty()) sendToUser("What do you want to tell the group?\r\n"); else { emit sendGroupTellEvent(view.toQByteArray()); sendToUser("OK.\r\n"); } }
// https://wicg.github.io/entries-api/#valid-path static bool isValidVirtualPath(StringView virtualPath) { if (virtualPath.isEmpty()) return true; if (virtualPath[0] == '/') { // An absolute path is a string consisting of '/' (U+002F SOLIDUS) followed by one or more path segments joined by '/' (U+002F SOLIDUS). return isZeroOrMorePathSegmentsSeparatedBySlashes(virtualPath.substring(1)); } return isValidRelativeVirtualPath(virtualPath); }
// https://wicg.github.io/entries-api/#relative-path static bool isValidRelativeVirtualPath(StringView virtualPath) { if (virtualPath.isEmpty()) return false; if (virtualPath[0] == '/') return false; return isZeroOrMorePathSegmentsSeparatedBySlashes(virtualPath); }
void AbstractParser::parseSpecialCommand(StringView wholeCommand) { if (wholeCommand.isEmpty()) throw std::runtime_error("input is empty"); if (evalSpecialCommandMap(wholeCommand)) return; const auto word = wholeCommand.takeFirstWord(); sendToUser(QString("Unrecognized command: %1\r\n").arg(word.toQString())); }
// https://wicg.github.io/entries-api/#path-segment static bool isValidPathSegment(StringView segment) { if (segment.isEmpty() || segment == "." || segment == "..") return true; for (unsigned i = 0; i < segment.length(); ++i) { if (!isValidPathNameCharacter(segment[i])) return false; } return true; }
void AbstractParser::parseGroupKick(const StringView &view) { if (view.isEmpty()) sendToUser("Who do you want to kick from the group?\r\n"); else { // REVISIT: We should change GroupManager to be a "FrontEnd" in this // thread and call it directly emit sendGroupKickEvent(view.toQByteArray().simplified()); sendToUser("OK.\r\n"); } }
IDBKeyPathLexer::TokenType IDBKeyPathLexer::lex(String& element) { if (m_remainingText.isEmpty()) return TokenEnd; if (m_remainingText[0] == '.') { m_remainingText = m_remainingText.substring(1); return TokenDot; } return lexIdentifier(element); }
bool AbstractParser::parseExitFlags(StringView words) { if (words.isEmpty()) return false; const auto firstWord = words.takeFirstWord(); for (const ExitFlag flag : ALL_EXIT_FLAGS) { if (getParserCommandName(flag).matches(firstWord)) { return parseExitFlag(flag, words); } } return false; }
bool AbstractParser::parseDoorAction(StringView words) { if (words.isEmpty()) return false; const auto firstWord = words.takeFirstWord(); for (const DoorActionType dat : ALL_DOOR_ACTION_TYPES) { if (getParserCommandName(dat).matches(firstWord)) { return parseDoorAction(dat, words); } } return false; }
void AbstractParser::parseSetCommand(StringView view) { if (view.isEmpty()) { sendToUser(QString("Syntax: %1set prefix [punct-char]\r\n").arg(prefixChar)); return; } auto first = view.takeFirstWord(); if (Abbrev{"prefix", 3}.matches(first)) { if (view.isEmpty()) { showCommandPrefix(); return; } auto next = view.takeFirstWord(); if (next.size() == 3) { auto quote = next.takeFirstLetter(); const bool validQuote = quote == '\'' || quote == '"'; const auto prefix = next.takeFirstLetter().toLatin1(); if (validQuote && isValidPrefix(prefix) && quote == next.takeFirstLetter() && quote != prefix && setCommandPrefix(prefix)) { return; } } else if (next.size() == 1) { const auto prefix = next.takeFirstLetter().toLatin1(); if (setCommandPrefix(prefix)) { return; } } sendToUser("Invalid prefix.\r\n"); return; } sendToUser("That variable is not supported."); }
void AbstractParser::parseHelp(StringView words) { if (words.isEmpty()) { showHelp(); return; } auto next = words.takeFirstWord(); if (Abbrev{"abbreviations", 2}.matches(next)) { showHelpCommands(true); return; } else if (Abbrev{"commands", 1}.matches(next)) { showHelpCommands(false); return; } auto &map = m_specialCommandMap; auto name = next.toQString().toStdString(); auto it = map.find(name); if (it != map.end()) { it->second.help(name); return; } if (Abbrev{"map", 1}.matches(next)) showMapHelp(); else if (Abbrev{"door", 1}.matches(next)) showDoorCommandHelp(); else if (Abbrev{"group", 1}.matches(next)) showGroupHelp(); else if (Abbrev{"exits", 2}.matches(next)) showExitHelp(); else if (Abbrev{"flags", 1}.matches(next)) showRoomSimpleFlagsHelp(); else if (Abbrev{"mobiles", 2}.matches(next)) showRoomMobFlagsHelp(); else if (Abbrev{"load", 2}.matches(next)) showRoomLoadFlagsHelp(); else if (Abbrev{"miscellaneous", 2}.matches(next)) showMiscHelp(); else { showHelp(); } }
bool MultipartHandle::parseHeadersIfPossible() { size_t contentLength = m_buffer.size(); if (!contentLength) return false; const char* content = m_buffer.data(); // Check if we have the header closing strings. if (!strnstr(content, "\r\n\r\n", contentLength)) { // Some servers closes the headers with only \n-s. if (!strnstr(content, "\n\n", contentLength)) { // Don't have the header closing string. Wait for more data. return false; } } // Parse the HTTP headers. String value; StringView name; char* p = const_cast<char*>(content); const char* end = content + contentLength; size_t totalConsumedLength = 0; for (; p < end; ++p) { String failureReason; size_t consumedLength = parseHTTPHeader(p, end - p, failureReason, name, value, false); if (!consumedLength) break; // No more header to parse. p += consumedLength; totalConsumedLength += consumedLength; // The name should not be empty, but the value could be empty. if (name.isEmpty()) break; m_headers.add(name.toString(), value); } m_buffer.remove(0, totalConsumedLength + 1); return true; }
void Settings::set(const StringView& _name, const StringView& _value) { ini_t* ini = INI_T(m_ini); FilePath uri(_name); const StringView path(strTrim(uri.getPath(), "/") ); const StringView& fileName(uri.getFileName() ); int32_t section = INI_GLOBAL_SECTION; if (!path.isEmpty() ) { section = ini_find_section(ini, path.getPtr(), path.getLength() ); if (INI_NOT_FOUND == section) { section = ini_section_add(ini, path.getPtr(), path.getLength() ); } } int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() ); if (INI_NOT_FOUND == property) { ini_property_add( ini , section , fileName.getPtr() , fileName.getLength() , _value.getPtr() , _value.getLength() ); } else { ini_property_value_set( ini , section , property , _value.getPtr() , _value.getLength() ); } }
CString TextEncoding::encode(StringView text, UnencodableHandling handling) const { if (!m_name) return CString(); if (text.isEmpty()) return ""; // FIXME: What's the right place to do normalization? // It's a little strange to do it inside the encode function. // Perhaps normalization should be an explicit step done before calling encode. auto upconvertedCharacters = text.upconvertedCharacters(); const UChar* source = upconvertedCharacters; size_t sourceLength = text.length(); Vector<UChar> normalizedCharacters; UErrorCode err = U_ZERO_ERROR; if (unorm_quickCheck(source, sourceLength, UNORM_NFC, &err) != UNORM_YES) { // First try using the length of the original string, since normalization to NFC rarely increases length. normalizedCharacters.grow(sourceLength); int32_t normalizedLength = unorm_normalize(source, sourceLength, UNORM_NFC, 0, normalizedCharacters.data(), sourceLength, &err); if (err == U_BUFFER_OVERFLOW_ERROR) { err = U_ZERO_ERROR; normalizedCharacters.resize(normalizedLength); normalizedLength = unorm_normalize(source, sourceLength, UNORM_NFC, 0, normalizedCharacters.data(), normalizedLength, &err); } ASSERT(U_SUCCESS(err)); source = normalizedCharacters.data(); sourceLength = normalizedLength; } return newTextCodec(*this)->encode(source, sourceLength, handling); }
bool AbstractParser::parsePrint(StringView &input) { const auto syntax = [this]() { sendToUser("Print what? [dynamic | static | note]\r\n"); }; if (input.isEmpty()) { syntax(); return true; } const auto next = input.takeFirstWord(); if (Abbrev{"dynamic", 1}.matches(next)) { printRoomInfo(dynamicRoomFields); return true; } else if (Abbrev{"static", 1}.matches(next)) { printRoomInfo(staticRoomFields); return true; } else if (Abbrev{"note", 1}.matches(next)) { showNote(); return true; } else { syntax(); return true; } }
const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end) { StringView name; String value; bool sawSecWebSocketExtensionsHeaderField = false; bool sawSecWebSocketAcceptHeaderField = false; bool sawSecWebSocketProtocolHeaderField = false; const char* p = start; for (; p < end; p++) { size_t consumedLength = parseHTTPHeader(p, end - p, m_failureReason, name, value); if (!consumedLength) return nullptr; p += consumedLength; // Stop once we consumed an empty line. if (name.isEmpty()) break; HTTPHeaderName headerName; if (!findHTTPHeaderName(name, headerName)) { // Evidence in the wild shows that services make use of custom headers in the handshake m_serverHandshakeResponse.addHTTPHeaderField(name.toString(), value); continue; } // https://tools.ietf.org/html/rfc7230#section-3.2.4 // "Newly defined header fields SHOULD limit their field values to US-ASCII octets." if ((headerName == HTTPHeaderName::SecWebSocketExtensions || headerName == HTTPHeaderName::SecWebSocketAccept || headerName == HTTPHeaderName::SecWebSocketProtocol) && !value.containsOnlyASCII()) { m_failureReason = makeString(name, " header value should only contain ASCII characters"); return nullptr; } if (headerName == HTTPHeaderName::SecWebSocketExtensions) { if (sawSecWebSocketExtensionsHeaderField) { m_failureReason = ASCIILiteral("The Sec-WebSocket-Extensions header must not appear more than once in an HTTP response"); return nullptr; } if (!m_extensionDispatcher.processHeaderValue(value)) { m_failureReason = m_extensionDispatcher.failureReason(); return nullptr; } sawSecWebSocketExtensionsHeaderField = true; } else { if (headerName == HTTPHeaderName::SecWebSocketAccept) { if (sawSecWebSocketAcceptHeaderField) { m_failureReason = ASCIILiteral("The Sec-WebSocket-Accept header must not appear more than once in an HTTP response"); return nullptr; } sawSecWebSocketAcceptHeaderField = true; } else if (headerName == HTTPHeaderName::SecWebSocketProtocol) { if (sawSecWebSocketProtocolHeaderField) { m_failureReason = ASCIILiteral("The Sec-WebSocket-Protocol header must not appear more than once in an HTTP response"); return nullptr; } sawSecWebSocketProtocolHeaderField = true; } m_serverHandshakeResponse.addHTTPHeaderField(headerName, value); } } return p; }