/*! * Обработка запроса пользователя на создание нового фида. * * Эта функция вызывается \b post запросом содержащим только имя фида. */ FeedReply NodeFeeds::add() { FeedReply reply(Notice::OK); const QString &name = m_packet->text(); if (name.contains(LC('*')) || name.contains(LC('/')) || name.contains(LC(' '))) return Notice::BadRequest; FeedPtr feed = m_channel->feed(name, false); if (!feed) { feed = m_channel->feed(name, true, false); if (!feed) return Notice::InternalError; feed->head().acl().add(m_user->id()); reply.status = FeedStorage::save(feed); } else FeedStorage::clone(feed); reply.status = FeedStorage::save(feed); if (reply.status == Notice::OK) reply.date = feed->head().date(); return reply; }
/*! * Определение доступных прав пользователя, для определения какие действия над сообщением возможны. */ int HistoryChatView::permissions(const MessageRecord &record) const { if (!record.id) return NoPermissions; FeedPtr messages = ChatClient::server()->feed(FEED_NAME_MESSAGES, false); if (!messages) return NoPermissions; const QVariantMap data = messages->data(); const int flags = data.value(MESSAGES_FEED_EDITABLE_KEY, DefaultEditFlags).toInt(); const bool timeout = (qAbs(ChatClient::date() - record.date) / 1000) > data.value(MESSAGES_FEED_TIMEOUT_KEY, DefaultTimeOut).toInt(); if (record.senderId == ChatClient::id() && (flags & SelfEdit) && !timeout) return Remove | Modify; if (ChatId(record.destId).type() != ChatId::ChannelId) return NoPermissions; const int acl = ClientFeeds::match(ChatClient::channels()->get(record.destId), ChatClient::channel()); if ((acl & Acl::SpecialWrite) || (acl & Acl::Edit)) { int out = 0; if (flags & ModeratorRemove) out |= Remove; if (flags & ModeratorEdit) out |= Modify; return out; } return NoPermissions; }
bool ProfileField::apply(const QVariant &value) { FeedPtr feed = ChatClient::channel()->feed(FEED_NAME_PROFILE, false); if (!feed) return false; if (feed->data().value(m_field) == value) return false; return ClientFeeds::post(ChatClient::id(), FEED_NAME_PROFILE + LC('/') + m_field, value, Feed::Echo | Feed::Share | Feed::Broadcast); }
/*! * Переопределение проверки прав доступа. * * Этот фид использует права доступа фида FEED_ACL. */ bool NodeInfoFeed::can(Channel *channel, int acl) const { if (!channel && acl != Acl::Read) return false; FeedPtr feed = m_header.channel()->feed(FEED_NAME_ACL, false, false); if (feed) return feed->can(channel, acl); return Feed::can(channel, acl); }
/*! * Служебная функция получения фида. * * На фид установляется маска прав доступа \p mask и если фид не существует добавляется владелец фида. */ FeedPtr Hosts::feed(const QString &name, int mask) const { FeedPtr feed = m_channel->feed(name, false); if (!feed) { feed = m_channel->feed(name, true, false); feed->head().acl().add(m_channel->id()); } feed->head().acl().setMask(mask); return feed; }
void ProfileField::reload() { FeedPtr feed = ChatClient::channel()->feed(FEED_NAME_PROFILE, false); if (!feed) return; QVariant data = feed->data().value(m_field); if (data.isNull()) return; setData(data); }
/*! * Чтение нового входящего сообщения. */ bool NodeMessages::read(PacketReader *reader) { if (ChatId(reader->sender()).type() != ChatId::UserId) return cleanup(); m_sender = Ch::channel(reader->sender(), ChatId::UserId); if (!m_sender) return cleanup(); MessageNotice packet(m_type, reader); m_packet = &packet; const qint64 date = m_packet->date(); FeedEvent *event = createEvent(); if (event->status != Notice::OK) { reject(event->status); FeedEvents::start(event); return cleanup(); } if (packet.direction() == Notice::Internal) { if (m_packet->command() == LS("ping")) pong(date); Core::i()->route(m_dest); delete event; return cleanup(); } FeedPtr feed = m_dest->feed(FEED_NAME_MESSAGES, true, false); event->diffTo = event->date; event->date = m_packet->date(); if (m_dest->type() == ChatId::UserId && m_dest->status().value() == Status::Offline) { event->status = Notice::ChannelOffline; reject(event->status); NodeMessagesDB::add(packet, event->status); Ch::gc(m_dest); } else { if (feed->get(MESSAGES_FEED_LOGGING_KEY).status == Notice::OK) NodeMessagesDB::add(packet); Core::i()->route(m_dest); } FeedStorage::save(feed, m_packet->date()); FeedEvents::start(event); return cleanup(); }
/*! * Фиксация события обновления фида \b hosts и отправка уведомления об этом. * * \param host Информация о подключении. * \param method \b post если пользователь подключился или \b put если отключился. * \param socket Номер сокета подключения. */ void Hosts::updateHostsFeed(HostInfo host, const QString &method, quint64 socket) { m_date = DateTime::utc(); host->date = m_date; FeedPtr hosts = feed(); FeedEvent *event = new FeedEvent(m_channel->id(), m_channel->id(), method); event->name = FEED_NAME_HOSTS; event->diffTo = hosts->head().date(); event->date = m_date; event->status = Notice::OK; event->path = SimpleID::encode(host->hostId); event->socket = socket; DataBase::add(host); FeedStorage::save(feed(), m_date); FeedEvents::start(event); }
/*! * Универсальный метод для отдачи тела фида. */ bool FeedHandler::serveFeed(ChatChannel channel, const QString &feedName) { if (!ifMethodAllowed()) return true; if (!channel || !channel->feeds().all().contains(feedName)) { setNoStore(); m_response->writeHead(Tufao::HttpServerResponse::NOT_FOUND); m_response->end(); return true; } FeedPtr feed = channel->feed(feedName); qint64 date = feed->head().date(); RestReplyCache &cache = m_cache[channel->id() + feedName.toUtf8()]; if (cache.date != date) { cache.date = date; cache.etag = etag(date, m_path.toUtf8()); cache.body = JSON::generate(feed->feed()); } setLastModified(date); setETag(cache.etag); setNoCache(); if (!ifModified(cache.etag)) { m_response->writeHead(Tufao::HttpServerResponse::NOT_MODIFIED); m_response->end(); return true; } m_response->writeHead(Tufao::HttpServerResponse::OK); if (m_request->method() != "HEAD") { setContentLength(cache.body.size()); m_response->end(cache.body); } else m_response->end(); return true; }
ChannelIndexData::ChannelIndexData(ChatChannel channel) : count(0) , options(NoOptions) , visibility(0) , name(channel->name()) { FeedPtr feed = channel->feed(FEED_NAME_INFO, false); if (!feed) return; visibility = feed->data().value(INFO_FEED_VISIBILITY_KEY, 0).toInt(); if (visibility < 0) return; if (channel->permanent()) options |= Permanent; id = channel->id(); count = channel->channels().size(); title = feed->data().value(INFO_FEED_TITLE_KEY).toMap().value(INFO_FEED_TEXT_KEY).toString(); if (feed->data().value(INFO_FEED_PINNED_KEY, false).toBool()) options |= Pinned; FeedPtr acl = channel->feed(FEED_NAME_ACL, false); if (acl && AclValue::match(acl.data(), 0) == 0) options |= Private; }
/*! * Фильтрация сообщения. * * \return Notice::OK если всё в порядке или другое значение, если сообщение должно быть отклонено. */ int NodeMessages::filter() { if (!m_dest) return Notice::NotFound; if (m_dest->type() == ChatId::ServerId) return Notice::BadRequest; if (m_dest->type() == ChatId::ChannelId && !m_dest->channels().contains(m_sender->id())) return Notice::BadRequest; if (!m_dest->canWrite(m_sender)) return Notice::Forbidden; if (m_dest->type() == ChatId::ChannelId) { const FeedPtr feed = m_dest->feed(FEED_NAME_INFO, true, false); if (m_packet->command() == LS("image") && !feed->data().value(INFO_FEED_IMAGES_KEY, true).toBool()) return Notice::Forbidden; } return Notice::OK; }
/*! * Фиксация события обновления фида \b user и отправка уведомления об этом. */ void Hosts::updateUserFeed(HostInfo host, const QString &method, quint64 socket) { FeedPtr user = this->user(); FeedEvent *event = new FeedEvent(m_channel->id(), m_channel->id(), method); if (!user->head().f().isEmpty()) { event->broadcast = Sockets::all(Ch::channel(m_channel->id()), true); if (method == FEED_METHOD_POST) event->broadcast.removeAll(socket); } event->name = FEED_NAME_USER; event->diffTo = user->head().date(); event->date = m_date; event->status = Notice::OK; event->path = SimpleID::encode(host->hostId); event->socket = socket; user->data()[LS("last")] = event->path; FeedStorage::save(user, m_date); FeedEvents::start(event); }
inline bool FeedMergeGroup::addFeed(const FeedPtr& feed) { if (feed->miniMergeType() == FeedMergeReplace) { _feedList.push_front(feed); while (_feedList.front()->feed != _feedList.back()->feed) { _feedList.pop_back(); } return updateWeight(); } // for append -- _feedList.push_front(feed); for (FeedList::iterator it = ++_feedList.begin(); it != _feedList.end();) { if ((*it)->isValid()) { ++it; } else { it = _feedList.erase(it); } } if (_feedList.size() > 5) { _feedList.pop_back(); } return updateWeight(); }
void UserFeedSet::_addFeed(const FeedPtr& feed, const FeedConfigPtr& config) { // MCE_DEBUG("UserFeedSet::_addFeed "<<"feed="<<feed->feed<<" merge="<<feed->merge); StypeMergeIndex& index = _mergeGroupSet.get<0> (); StypeMergeIndex::iterator it = index.find(boost::make_tuple( feed->smallType(), feed->merge)); if (it != index.end()) { MCE_DEBUG("UserFeedSet::addFeed --> find in map"); FeedMergeGroupPtr g = (*it); if (config && config->updateTime == 0) { feed->time = (*it)->timestamp(); } g->addFeed(feed); index.replace(it, g); } else { MCE_DEBUG("UserFeedSet::addFeed --> not in map"); FeedMergeGroupPtr g = new FeedMergeGroup(feed); _mergeGroupSet.insert(g); } _miniTime = _miniTime < feed->time ? _miniTime : feed->time; }
/*! * Базовая функция совершения операции над фидом. * * \param channel Канал владелец фида. * \param method Метод \sa Methods. * \param name Имя фида с опциональным путём запроса. * \param sender Канал создавший запрос. * \param json Данные запроса. */ FeedReply FeedsCore::request(ServerChannel *channel, const QString &method, const QString &name, ServerChannel *sender, const QVariantMap &json) { if (!channel || !sender) return Notice::InternalError; const QPair<QString, QString> split = FeedNotice::split(name); FeedPtr feed = channel->feed(split.first, false); if (!feed) return Notice::NotFound; FeedReply reply(Notice::InternalError); const int cmd = methodToInt(method); FeedEvent *event = new FeedEvent(channel->id(), sender->id(), method); event->request = json; event->name = split.first; event->path = split.second; event->date = feed->head().date(); if (!feed->can(sender, Acl::Read)) return done(event, Notice::Forbidden); if (cmd != Get && !feed->can(sender, Acl::Write)) return done(event, Notice::Forbidden); switch (cmd) { case Get: reply = feed->get(event->path, json, sender); break; case Post: reply = feed->post(event->path, json, sender); break; case Put: reply = feed->put(event->path, json, sender); break; case Delete: reply = feed->del(event->path, sender); break; } if (cmd != Get && reply.status == Notice::OK) { const int options = json.value(FEED_KEY_OPTIONS).toInt(); event->diffTo = event->date; event->date = reply.date; if (reply.date) FeedStorage::save(feed, reply.date); if (options & Feed::Broadcast) { if (channel->type() == SimpleID::ServerId) { const QList<QByteArray> channels = m_self->m_subscription.value(event->name); QList<quint64> sockets; foreach (const QByteArray &id, channels) { ChatChannel user = Ch::channel(id, SimpleID::UserId, false); if (user) Sockets::merge(sockets, user->sockets()); } event->broadcast = sockets; } else event->broadcast = Sockets::channel(channel); }