bool SessionManager::checkAccessControl( const char* username, InternetAddress* ip) { bool rval; // check the address against 127.x.x.x for localhost status, any user // has access control from localhost rval = (strncmp(ip->getAddress(), "127.", 4) == 0); if(!rval) { // not localhost, will have to check session database: // get bitmunk user ID Url url; url.format("/api/3.0/users?username=%s", username); DynamicObject user; if(mNode->getMessenger()->getFromBitmunk(&url, user)) { rval = mSessionDatabase.checkAccessControl( BM_USER_ID(user["id"]), ip->getAddress()); } } if(!rval) { ExceptionRef e = new Exception( "Access denied for user.", "bitmunk.webui.SessionManager.AccessDenied"); e->getDetails()["username"] = username; e->getDetails()["ip"] = ip->getAddress(); Exception::push(e); } return rval; }
static PatternRef _compileDomainRegex(const char* domain) { PatternRef rval(NULL); string regex = "^"; regex.append(domain); regex.append("$"); // escape all periods StringTools::replaceAll(regex, ".", "\\."); // replace all wildcards with (.*) StringTools::replaceAll(regex, "*", ".*"); // try to compile the pattern (match case, no-sub matches allowed) rval = Pattern::compile(regex.c_str(), true, false); if(rval.isNull()) { ExceptionRef e = new Exception( "Could not add proxy domain. Invalid domain format.", "bitmunk.node.ProxyResourceHandler.InvalidDomainFormat"); e->getDetails()["domain"] = domain; e->getDetails()["regex"] = regex.c_str(); Exception::push(e); } return rval; }
bool ControlPoint::performAction( const char* actionName, DynamicObject& params, Service& service, ActionResult& result) { bool rval = false; // ensure action exists in the service if(!service->hasMember("actions") || !service["actions"]->hasMember(actionName)) { ExceptionRef e = new Exception( "Service has no such action.", "monarch.upnp.NoSuchAction"); e->getDetails()["actionName"] = actionName; e->getDetails()["serviceType"] = service["serviceType"]->getString(); e->getDetails()["serviceId"] = service["serviceId"]->getString(); Exception::set(e); } else { // create a soap message SoapMessage msg; msg["name"] = actionName; msg["namespace"] = service["serviceType"]->getString(); msg["params"] = params; // do soap transfer rval = doSoap(service, msg, result); } return rval; }
int FileInputStream::read(char* b, int length) { int rval = -1; if(ensureOpen()) { rval = 0; // do read int count = fread(b, 1, length, mHandle); if(count != length) { // check for an error if(ferror(mHandle) != 0) { ExceptionRef e = new Exception( "Could not read file.", "monarch.io.File.ReadError"); e->getDetails()["path"] = mFile->getAbsolutePath(); e->getDetails()["error"] = strerror(errno); Exception::set(e); rval = -1; } } if(rval != -1) { // return number of bytes read rval = count; } } return rval; }
int AbstractSocket::receive(char* b, int length) { int rval = -1; if(!isBound()) { ExceptionRef e = new Exception( "Cannot read from unbound socket.", SOCKET_EXCEPTION_TYPE ".NotBound"); Exception::set(e); } else { // try to receive some data, don't block int flags = 0; #ifdef MSG_DONTWAIT flags |= MSG_DONTWAIT; #endif rval = SOCKET_MACRO_recv(mFileDescriptor, b, length, flags); if(rval < 0) { // see if error is other than no data is available (EAGAIN) if(errno != EAGAIN) { ExceptionRef e = new Exception( "Could not read from socket.", SOCKET_EXCEPTION_TYPE); e->getDetails()["error"] = strerror(errno); Exception::set(e); } // FIXME: this will probably work differently in the future // non-blocking socket, set exception else if(isReceiveNonBlocking()) { // using asynchronous IO ExceptionRef e = new Exception( "Socket would block during receive.", SOCKET_EXCEPTION_TYPE ".WouldBlock"); e->getDetails()["wouldBlock"] = true; Exception::set(e); } // wait for data to become available else if(waitUntilReady(true, getReceiveTimeout())) { // receive data rval = SOCKET_MACRO_recv(mFileDescriptor, b, length, flags); if(rval < 0) { ExceptionRef e = new Exception( "Could not read from socket.", SOCKET_EXCEPTION_TYPE); e->getDetails()["error"] = strerror(errno); Exception::set(e); } } } } return rval; }
bool DirectiveService::createDirective( BtpAction* action, DynamicObject& in, DynamicObject& out) { bool rval = false; UserId userId; if(mNode->checkLogin(action, &userId)) { // turn user ID into a key char key[22]; snprintf(key, 22, "%" PRIu64, userId); // lock to insert directive mCacheLock.lock(); { // initialize user's directive cache as needed if(!mDirectives->hasMember(key)) { mDirectives[key]->setType(Map); } DynamicObject& cache = mDirectives[key]; if(cache->length() < MAX_DIRECTIVES) { // insert directive into cache insertDirective(cache, in); // return directive ID out["directiveId"] = in["id"]->getString(); rval = true; // fire created event Event e; e["type"] = "bitmunk.system.Directive.created"; e["details"]["userId"] = userId; e["details"]["directiveId"] = in["id"]; e["details"]["directive"] = in; mNode->getEventController()->schedule(e); } else { // too many directives ExceptionRef e = new Exception( "Could not add directive. Maximum number of " "directives reached.", "bitmunk.system.DirectiveService.TooManyDirectives"); e->getDetails()["userId"] = userId; e->getDetails()["max"] = MAX_DIRECTIVES; Exception::set(e); } } mCacheLock.unlock(); } return rval; }
int FileInputStream::readLine(string& line, char delimiter) { int rval = -1; if(ensureOpen()) { rval = 0; // feof returns non-zero when EOF if(feof(mHandle) == 0) { // get line char* data = NULL; size_t size = 0; ssize_t length = getdelim(&data, &size, delimiter, mHandle); if(length == -1) { if(feof(mHandle) != 0) { // end of file } else { ExceptionRef e = new Exception( "Could not read file.", "monarch.io.File.ReadError"); e->getDetails()["path"] = mFile->getAbsolutePath(); e->getDetails()["error"] = strerror(errno); Exception::set(e); rval = -1; } } else { // line was read rval = 1; if(data[length - 1] == delimiter) { // do not include delimiter line.assign(data, length - 1); } else { line.assign(data, length); } free(data); } } } return rval; }
bool Messenger::exchange( UserId peerId, Url* url, BtpMessage* out, BtpMessage* in, UserId userId, UserId agentId, uint32_t timeout) { bool rval = true; if(userId != 0) { // get user profile ProfileRef p; if(!mNode->getLoginData(agentId == 0 ? userId : agentId, NULL, &p)) { ExceptionRef e = new Exception( "Could not do BTP exchange. Not logged in.", "bitmunk.node.Messenger.NotLoggedIn"); BM_ID_SET(e->getDetails()["userId"], userId); BM_ID_SET(e->getDetails()["agentId"], agentId); Exception::set(e); rval = false; } else if(!p.isNull()) { // set user ID and profile for outgoing secure message out->setUserId(userId); out->setAgentProfile(p); // set public key source for incoming secure message in->setPublicKeySource(mNode->getPublicKeyCache()); } } // do btp exchange if(rval) { // if nodeuser is set, override peer ID with it DynamicObject vars; url->getQueryVariables(vars); if(vars->hasMember("nodeuser")) { peerId = BM_USER_ID(vars["nodeuser"]); } rval = mClient.exchange(peerId, url, out, in, timeout); } return rval; }
bool DirectiveService::processDirective( BtpAction* action, DynamicObject& in, DynamicObject& out) { bool rval = false; UserId userId; if(mNode->checkLogin(action, &userId)) { // get directive by its ID DynamicObject params; action->getResourceParams(params); const char* id = params[0]->getString(); DynamicObject directive(NULL); // remove directive from cache removeDirective(userId, id, &directive); // ensure directive is not invalid if(directive.isNull()) { // directive not found ExceptionRef e = new Exception( "Could not process directive. Directive does not exist.", "bitmunk.system.DirectiveService.InvalidDirectiveId", 404); e->getDetails()["userId"] = userId; e->getDetails()["directiveId"] = id; Exception::set(e); } // process directive else { // include user ID in directive directive["userId"] = userId; // include directive in output out["userId"] = userId; out["directive"] = directive; // run directive processor fiber DirectiveProcessor* dp = new DirectiveProcessor(mNode); dp->setDirective(directive); mNode->getFiberScheduler()->addFiber(dp); rval = true; } } return rval; }
bool AbstractSocket::listen(int backlog) { if(!isBound()) { ExceptionRef e = new Exception( "Cannot listen on unbound socket.", SOCKET_EXCEPTION_TYPE ".NotBound"); Exception::set(e); } else { // set backlog mBacklog = backlog; // listen int error = SOCKET_MACRO_listen(mFileDescriptor, backlog); if(error < 0) { ExceptionRef e = new Exception( "Could not listen on socket.", SOCKET_EXCEPTION_TYPE); e->getDetails()["error"] = strerror(errno); Exception::set(e); } else { // now listening mListening = true; // set socket to non-blocking so accept() calls can be interrupted SOCKET_MACRO_fcntl(mFileDescriptor, F_SETFL, O_NONBLOCK); } } return mListening; }
bool AbstractConnection::connect(const char* url) { bool rval = false; // clean up old url mUrl.setNull(); // ensure URL isn't malformed Exception::clear(); mUrl = new Url(url); if(Exception::isSet()) { ExceptionRef e = new Exception( "Invalid database url.", "monarch.sql.Connection.InvalidUrl"); e->getDetails()["url"] = url; Exception::push(e); } else { // call implementation-specific code rval = connect(&(*mUrl)); } return rval; }
bool AbstractConnection::rollback() { bool rval = false; // save the reason for the rollback ExceptionRef reason = Exception::get(); // attempt to do the rollback Statement* s = prepare("ROLLBACK"); rval = (s != NULL) && s->execute() && s->reset(); if(!rval) { ExceptionRef e = new Exception( "Could not rollback transaction.", "monarch.sql.Connection.TransactionRollbackError"); if(!reason.isNull()) { e->getDetails()["rollbackReason"] = Exception::convertToDynamicObject(reason); } Exception::push(e); } return rval; }
bool NodeConfigManager::getBitmunkHomePath(string& path) { bool rval; Config c = getConfigManager()->getConfig(MAIN_ID); // get initial value const char* bitmunkHomePath = c["monarch.app.Core"]["home"]->getString(); // make sure bitmunkHomePath is user-expanded rval = File::expandUser(bitmunkHomePath, path); // make sure path is absolute if(!rval || !File::isPathAbsolute(path.c_str())) { ExceptionRef e = new Exception( "Could not get absolute bitmunk home path.", "bitmunk.node.NodeConfigManager.ConfigError"); e->getDetails()["bitmunkHomePath"] = bitmunkHomePath; Exception::push(e); rval = false; } #ifdef WIN32 if(rval) { // swap backslashes to standard if on windows StringTools::replaceAll(path, "\\", "/"); } #endif return rval; }
bool SessionManager::getSessionFromAction( BtpAction* action, string& session, InternetAddress* ip) { bool rval = true; // get client cookies CookieJar jar; jar.readCookies(action->getRequest()->getHeader(), CookieJar::Client); // check for bitmunk-session cookie Cookie cookie = jar.getCookie("bitmunk-session"); if(cookie.isNull()) { ExceptionRef e = new Exception( "No 'bitmunk-session' cookie.", "bitmunk.webui.SessionManager.MissingCookie"); e->getDetails()["missingCookie"] = "bitmunk-session"; Exception::set(e); rval = false; } else { // get session ID session = cookie["value"]->getString(); } if(rval) { // get IP rval = action->getClientInternetAddress(ip); } return rval; }
DynamicObject ValidatorContext::addError( const char* type, DynamicObject* object) { DynamicObject errorDetail; // setup error detail errorDetail["type"] = type; // FIXME: localize message -- lehn // FIXME: really? do we need to mention this, because we'd have to // do this for every string in the system.. -- manu errorDetail["message"] = "The given value does not meet all of the data " "validation requirements. Please examine the error details for more " "information about the specific requirements."; if(object != NULL && (mMaskType & ValidatorContext::MaskInvalidValues) == 0) { errorDetail["invalidValue"] = *object; } // add error detail to results errors std::string fullpath = getPath(); mResults["errors"][fullpath.c_str()] = errorDetail; // Skip setting exceptions if requested. Return errorDetail regardless. if(mSetExceptions) { ExceptionRef e; if(!Exception::isSet()) { e = new Exception( "The given object does not meet all of the data validation " "requirements. Please examine the error details for more " "information about the specific requirements.", "monarch.validation.ValidationError"); Exception::set(e); } else { e = Exception::get(); // Check if we are adding to a ValidationError if(!e->isType("monarch.validation.ValidationError")) { // FIXME: this is a bit bogus. If validation checking keeps causing // other exceptions then a long cause chain could be generated // switching between ValidationError and other types. e = new Exception( "The given object does not meet all of the data validation " "requirements. Please examine the error details for more " "information about the specific requirements.", "monarch.validation.ValidationError"); Exception::push(e); } } // add detail to "errors" section of exception details e->getDetails()["errors"][fullpath.c_str()] = errorDetail; } return errorDetail; }
Config NodeConfigManager::getModuleConfig(const char* moduleName, bool raw) { Config rval(NULL); if(raw) { rval = getConfigManager()->getConfig(moduleName, raw); } else { Config tmp = getConfigManager()->getConfig(MAIN_ID, raw); if(!tmp.isNull() && tmp->hasMember(moduleName)) { rval = tmp[moduleName]; } else { ExceptionRef e = new Exception( "Could not get module config. Invalid module name.", "bitmunk.node.NodeConfigManager.InvalidModuleName"); e->getDetails()["moduleName"] = moduleName; Exception::push(e); } } return rval; }
int64_t MySqlRow::getColumnIndex(const char* name) { // use 64-bit signed int to cover all values + error (negative 1) int64_t rval = -1; for(unsigned int i = 0; i < mFieldCount; ++i) { if(strcmp(name, mFields[i].name) == 0) { rval = i; break; } } if(rval == -1) { // set exception ExceptionRef e = new Exception( "Could not get column value. Invalid column name.", "monarch.sql.mysql.MySql"); e->getDetails()["name"] = name; Exception::set(e); } return rval; }
bool PortService::start() { if(!mOperation.isNull()) { // stop service stop(); } // initialize service mOperation = initialize(); if(!mOperation.isNull()) { // run service mServer->getOperationRunner()->runOperation(mOperation); } else { // set exception ExceptionRef e = new Exception( "Port service failed to start.", "monarch.net.PortService.StartFailed"); e->getDetails()["name"] = mName; Exception::push(e); // clean up service cleanup(); } return !mOperation.isNull(); }
bool NodeConfigManager::getUserDataPath(UserId userId, string& path) { bool rval; Config c = getConfigManager()->getConfig(MAIN_ID); // get initial value const char* usersPath = c["node"]["usersPath"]->getString(); rval = expandBitmunkHomePath(usersPath, path); if(rval) { // append user id path char userIdStr[ID_MAX]; snprintf(userIdStr, ID_MAX, ID_FMT, userId); path.assign(File::join(path.c_str(), userIdStr)); #ifdef WIN32 // swap backslashes to standard if on windows StringTools::replaceAll(path, "\\", "/"); #endif } if(!rval) { ExceptionRef e = new Exception( "Could not get absolute user data path.", "bitmunk.node.NodeConfigManager.ConfigError"); e->getDetails()["usersPath"] = usersPath; Exception::push(e); } return rval; }
bool FileInputStream::ensureOpen() { bool rval = true; // try to open the file (mFile is null when using stdin) if(mHandle == NULL && !mFile.isNull()) { if(!mFile->exists()) { ExceptionRef e = new Exception( "Could not open file.", "monarch.io.File.NotFound"); e->getDetails()["path"] = mFile->getAbsolutePath(); Exception::set(e); rval = false; } else if(!mFile->isReadable()) { ExceptionRef e = new Exception( "Could not open file.", "monarch.io.File.AccessDenied"); e->getDetails()["path"] = mFile->getAbsolutePath(); Exception::set(e); rval = false; } else { mHandle = fopen(mFile->getAbsolutePath(), "rb"); if(mHandle == NULL) { ExceptionRef e = new Exception( "Could not open file stream.", "monarch.io.File.OpenFailed"); e->getDetails()["path"] = mFile->getAbsolutePath(); e->getDetails()["error"] = strerror(errno); Exception::set(e); rval = false; } } } return rval; }
bool UdpSocket::joinGroup(SocketAddress* group, SocketAddress* localAddress) { int error = 0; if(group->getCommunicationDomain() == SocketAddress::IPv6) { // create IPv6 multicast request struct ipv6_mreq request; // set multicast address inet_pton(AF_INET6, group->getAddress(), &request.ipv6mr_multiaddr); // use any address for local interface request.ipv6mr_interface = 0; // join group error = setsockopt( mFileDescriptor, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char*)&request, sizeof(request)); } else { // create IPv4 multicast request struct ip_mreq request; // set multicast address inet_pton(AF_INET, group->getAddress(), &request.imr_multiaddr); // set local interface if(localAddress == NULL) { // use any address for local interface request.imr_interface.s_addr = INADDR_ANY; } else { inet_pton(AF_INET, localAddress->getAddress(), &request.imr_interface); } // join group error = setsockopt( mFileDescriptor, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&request, sizeof(request)); } if(error < 0) { ExceptionRef e = new Exception( "Could not join multicast group.", SOCKET_EXCEPTION_TYPE); e->getDetails()["error"] = strerror(errno); Exception::set(e); } return error == 0; }
int64_t FileInputStream::skip(int64_t count) { int64_t rval = -1; if(ensureOpen()) { bool success; // store current position and the end position mo_fseek_off_t curr = mo_ftell(mHandle); success = (mo_fseek(mHandle, 0, SEEK_END) == 0); mo_fseek_off_t end = mo_ftell(mHandle); success = success && (mo_fseek(mHandle, curr, SEEK_SET) == 0); rval = count; if(rval > 0 && curr < end) { // do not skip past EOF if(rval > (end - curr)) { rval = end - curr; } // skip from current offset success = success && (mo_fseek(mHandle, rval, SEEK_CUR) == 0); } if(!success) { ExceptionRef e = new Exception( "Could not read file.", "monarch.io.File.ReadError"); e->getDetails()["path"] = mFile->getAbsolutePath(); e->getDetails()["error"] = strerror(errno); Exception::set(e); rval = -1; } } return rval; }
bool AbstractSocket::bind(SocketAddress* address) { // acquire file descriptor if(acquireFileDescriptor(address->getCommunicationDomain())) { // populate address structure unsigned int size = 130; char addr[size]; address->toSockAddr((sockaddr*)&addr, size); // bind int error = SOCKET_MACRO_bind(mFileDescriptor, (sockaddr*)&addr, size); if(error < 0) { ExceptionRef e = new Exception( "Could not bind socket.", SOCKET_EXCEPTION_TYPE); e->getDetails()["error"] = strerror(errno); e->getDetails()["address"] = address->getAddress(); e->getDetails()["port"] = address->getPort(); Exception::set(e); // close socket close(); } else { // initialize input and output initializeInput(); initializeOutput(); // now bound mBound = true; // update address getLocalAddress(address); } } return mBound; }
bool NodeConfigManager::translateUrlToUserFilePath( UserId userId, const char* url, string& filePath) { bool rval = false; Url u(url); const string& path = u.getSchemeSpecificPart(); // ensure url scheme is sqlite3 if((strcmp(u.getScheme().c_str(), "sqlite3") != 0) && (strcmp(u.getScheme().c_str(), "file") != 0)) { ExceptionRef e = new Exception( "Could not translate URL, the scheme is not recognized.", BITMUNK_CM_EXCEPTION ".BadUrlScheme"); e->getDetails()["url"] = url; e->getDetails()["supportedSchemes"]->append() = "sqlite3"; e->getDetails()["supportedSchemes"]->append() = "file"; Exception::set(e); } // check url format else if(path.length() <= 2 || path[0] != '/' || path[1] != '/') { ExceptionRef e = new Exception( "Could not translate URL, the path format is not recognized.", BITMUNK_CM_EXCEPTION ".BadUrlFormat"); e->getDetails()["url"] = url; Exception::set(e); } else { // skip "//" const char* urlPath = path.c_str() + 2; // expand url path for user as necessary rval = expandUserDataPath(urlPath, userId, filePath); } return rval; }
bool DatabaseClient::checkForSchema(const char* table) { bool rval = true; // ensure the schema exists if(!mSchemas->hasMember(table)) { ExceptionRef e = new Exception( "No schema defined for table.", DBC_EXCEPTION ".MissingSchema"); e->getDetails()["table"] = table; Exception::set(e); rval = false; } return rval; }
bool UdpSocket::setBroadcastEnabled(bool enable) { // set broadcast flag int broadcast = (enable) ? 1 : 0; int error = setsockopt( mFileDescriptor, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast)); if(error < 0) { ExceptionRef e = new Exception( "Could not set broadcast flag.", SOCKET_EXCEPTION_TYPE); e->getDetails()["error"] = strerror(errno); Exception::set(e); } return error == 0; }
bool UdpSocket::setMulticastTimeToLive(unsigned char ttl) { int error = 0; // set multicast ttl flag error = setsockopt( mFileDescriptor, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl)); if(error < 0) { ExceptionRef e = new Exception( "Could not set multicast TTL.", SOCKET_EXCEPTION_TYPE); e->getDetails()["error"] = strerror(errno); Exception::set(e); } return error == 0; }
bool AbstractSocket::getRemoteAddress(SocketAddress* address) { bool rval = false; if(!mConnected) { ExceptionRef e = new Exception( "Cannot get local address for an unconnected socket.", SOCKET_EXCEPTION_TYPE ".Closed"); Exception::set(e); } else { // get address structure socklen_t size = 130; char addr[size]; // get remote information int error = SOCKET_MACRO_getpeername( mFileDescriptor, (sockaddr*)&addr, &size); if(error < 0) { // invalidate file descriptor if bad if(errno == EBADF) { mFileDescriptor = -1; close(); } ExceptionRef e = new Exception( "Could not get socket remote address.", SOCKET_EXCEPTION_TYPE); e->getDetails()["error"] = strerror(errno); Exception::set(e); } else { // convert socket address address->fromSockAddr((sockaddr*)&addr, size); rval = true; } } return rval; }
bool UdpSocket::setMulticastHops(unsigned char hops) { int error = 0; // set multicast hops flag error = setsockopt( mFileDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char*)&hops, sizeof(hops)); if(error < 0) { ExceptionRef e = new Exception( "Could not set multicast hops.", SOCKET_EXCEPTION_TYPE); e->getDetails()["error"] = strerror(errno); Exception::set(e); } return error == 0; }
DynamicObject Exception::convertToDynamicObject(ExceptionRef& e) { DynamicObject dyno; dyno["message"] = e->getMessage(); dyno["type"] = e->getType(); if(!e->getCause().isNull()) { dyno["cause"] = convertToDynamicObject(e->getCause()); } if(!(*e).mDetails->isNull()) { dyno["details"] = e->getDetails(); } return dyno; }