diff options
Diffstat (limited to 'indra/llui')
-rw-r--r-- | indra/llui/llnotificationslistener.cpp | 269 | ||||
-rw-r--r-- | indra/llui/llnotificationslistener.h | 20 | ||||
-rw-r--r-- | indra/llui/llurlentry.cpp | 4 | ||||
-rw-r--r-- | indra/llui/llurlregistry.cpp | 20 | ||||
-rw-r--r-- | indra/llui/llurlregistry.h | 4 |
5 files changed, 312 insertions, 5 deletions
diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp index fe4fbe7510..629964c322 100644 --- a/indra/llui/llnotificationslistener.cpp +++ b/indra/llui/llnotificationslistener.cpp @@ -10,10 +10,10 @@ */ #include "linden_common.h" - #include "llnotificationslistener.h" - #include "llnotifications.h" +#include "llsd.h" +#include "llui.h" LLNotificationsListener::LLNotificationsListener(LLNotifications & notifications) : LLEventAPI("LLNotifications", @@ -24,6 +24,47 @@ LLNotificationsListener::LLNotificationsListener(LLNotifications & notifications "Add a notification with specified [\"name\"], [\"substitutions\"] and [\"payload\"].\n" "If optional [\"reply\"] specified, arrange to send user response on that LLEventPump.", &LLNotificationsListener::requestAdd); + add("listChannels", + "Post to [\"reply\"] a map of info on existing channels", + &LLNotificationsListener::listChannels, + LLSD().with("reply", LLSD())); + add("listChannelNotifications", + "Post to [\"reply\"] an array of info on notifications in channel [\"channel\"]", + &LLNotificationsListener::listChannelNotifications, + LLSD().with("reply", LLSD()).with("channel", LLSD())); + add("respond", + "Respond to notification [\"uuid\"] with data in [\"response\"]", + &LLNotificationsListener::respond, + LLSD().with("uuid", LLSD())); + add("cancel", + "Cancel notification [\"uuid\"]", + &LLNotificationsListener::cancel, + LLSD().with("uuid", LLSD())); + add("ignore", + "Ignore future notification [\"name\"]\n" + "(from <notification name= > in notifications.xml)\n" + "according to boolean [\"ignore\"].\n" + "If [\"name\"] is omitted or undefined, [un]ignore all future notifications.\n" + "Note that ignored notifications are not forwarded unless intercepted before\n" + "the \"Ignore\" channel.", + &LLNotificationsListener::ignore); + add("forward", + "Forward to [\"pump\"] future notifications on channel [\"channel\"]\n" + "according to boolean [\"forward\"]. When enabled, only types matching\n" + "[\"types\"] are forwarded, as follows:\n" + "omitted or undefined: forward all notifications\n" + "string: forward only the specific named [sig]type\n" + "array of string: forward any notification matching any named [sig]type.\n" + "When boolean [\"respond\"] is true, we auto-respond to each forwarded\n" + "notification.", + &LLNotificationsListener::forward, + LLSD().with("channel", LLSD())); +} + +// This is here in the .cpp file so we don't need the definition of class +// Forwarder in the header file. +LLNotificationsListener::~LLNotificationsListener() +{ } void LLNotificationsListener::requestAdd(const LLSD& event_data) const @@ -57,3 +98,227 @@ void LLNotificationsListener::NotificationResponder(const std::string& reply_pum reponse_event["response"] = response; LLEventPumps::getInstance()->obtain(reply_pump).post(reponse_event); } + +void LLNotificationsListener::listChannels(const LLSD& params) const +{ + LLReqID reqID(params); + LLSD response(reqID.makeResponse()); + for (LLNotifications::ChannelMap::const_iterator cmi(mNotifications.mChannels.begin()), + cmend(mNotifications.mChannels.end()); + cmi != cmend; ++cmi) + { + LLSD channelInfo; + channelInfo["parent"] = cmi->second->getParentChannelName(); + response[cmi->first] = channelInfo; + } + LLEventPumps::instance().obtain(params["reply"]).post(response); +} + +void LLNotificationsListener::listChannelNotifications(const LLSD& params) const +{ + LLReqID reqID(params); + LLSD response(reqID.makeResponse()); + LLNotificationChannelPtr channel(mNotifications.getChannel(params["channel"])); + if (channel) + { + LLSD notifications(LLSD::emptyArray()); + for (LLNotificationChannel::Iterator ni(channel->begin()), nend(channel->end()); + ni != nend; ++ni) + { + notifications.append(asLLSD(*ni)); + } + response["notifications"] = notifications; + } + LLEventPumps::instance().obtain(params["reply"]).post(response); +} + +void LLNotificationsListener::respond(const LLSD& params) const +{ + LLNotificationPtr notification(mNotifications.find(params["uuid"])); + if (notification) + { + notification->respond(params["response"]); + } +} + +void LLNotificationsListener::cancel(const LLSD& params) const +{ + LLNotificationPtr notification(mNotifications.find(params["uuid"])); + if (notification) + { + mNotifications.cancel(notification); + } +} + +void LLNotificationsListener::ignore(const LLSD& params) const +{ + // Calling a method named "ignore", but omitting its "ignore" Boolean + // argument, should by default cause something to be ignored. Explicitly + // pass ["ignore"] = false to cancel ignore. + bool ignore = true; + if (params.has("ignore")) + { + ignore = params["ignore"].asBoolean(); + } + // This method can be used to affect either a single notification name or + // all future notifications. The two use substantially different mechanisms. + if (params["name"].isDefined()) + { + // ["name"] was passed: ignore just that notification + LLUI::sSettingGroups["ignores"]->setBOOL(params["name"], ignore); + } + else + { + // no ["name"]: ignore all future notifications + mNotifications.setIgnoreAllNotifications(ignore); + } +} + +class LLNotificationsListener::Forwarder: public LLEventTrackable +{ + LOG_CLASS(LLNotificationsListener::Forwarder); +public: + Forwarder(LLNotifications& llnotifications, const std::string& channel): + mNotifications(llnotifications), + mRespond(false) + { + // Connect to the specified channel on construction. Because + // LLEventTrackable is a base, we should automatically disconnect when + // destroyed. + LLNotificationChannelPtr channelptr(llnotifications.getChannel(channel)); + if (channelptr) + { + // Try connecting at the front of the 'changed' signal. That way + // we shouldn't get starved by preceding listeners. + channelptr->connectAtFrontChanged(boost::bind(&Forwarder::handle, this, _1)); + } + } + + void setPumpName(const std::string& name) { mPumpName = name; } + void setTypes(const LLSD& types) { mTypes = types; } + void setRespond(bool respond) { mRespond = respond; } + +private: + bool handle(const LLSD& notification) const; + bool matchType(const LLSD& filter, const std::string& type) const; + + LLNotifications& mNotifications; + std::string mPumpName; + LLSD mTypes; + bool mRespond; +}; + +void LLNotificationsListener::forward(const LLSD& params) +{ + std::string channel(params["channel"]); + // First decide whether we're supposed to start forwarding or stop it. + // Default to true. + bool forward = true; + if (params.has("forward")) + { + forward = params["forward"].asBoolean(); + } + if (! forward) + { + // This is a request to stop forwarding notifications on the specified + // channel. The rest of the params don't matter. + // Because mForwarders contains scoped_ptrs, erasing the map entry + // DOES delete the heap Forwarder object. Because Forwarder derives + // from LLEventTrackable, destroying it disconnects it from the + // channel. + mForwarders.erase(channel); + return; + } + // From here on, we know we're being asked to start (or modify) forwarding + // on the specified channel. Find or create an appropriate Forwarder. + ForwarderMap::iterator + entry(mForwarders.insert(ForwarderMap::value_type(channel, ForwarderMap::mapped_type())).first); + if (! entry->second) + { + entry->second.reset(new Forwarder(mNotifications, channel)); + } + // Now, whether this Forwarder is brand-new or not, update it with the new + // request info. + Forwarder& fwd(*entry->second); + fwd.setPumpName(params["pump"]); + fwd.setTypes(params["types"]); + fwd.setRespond(params["respond"]); +} + +bool LLNotificationsListener::Forwarder::handle(const LLSD& notification) const +{ + LL_INFOS("LLNotificationsListener") << "handle(" << notification << ")" << LL_ENDL; + if (notification["sigtype"].asString() == "delete") + { + LL_INFOS("LLNotificationsListener") << "ignoring delete" << LL_ENDL; + return false; + } + LLNotificationPtr note(mNotifications.find(notification["id"])); + if (! note) + { + LL_INFOS("LLNotificationsListener") << notification["id"] << " not found" << LL_ENDL; + return false; + } + if (! matchType(mTypes, note->getType())) + { + LL_INFOS("LLNotificationsListener") << "didn't match types " << mTypes << LL_ENDL; + return false; + } + LL_INFOS("LLNotificationsListener") << "sending via '" << mPumpName << "'" << LL_ENDL; + // This is a notification we care about. Forward it through specified + // LLEventPump. + LLEventPumps::instance().obtain(mPumpName).post(asLLSD(note)); + // Are we also being asked to auto-respond? + if (mRespond) + { + LL_INFOS("LLNotificationsListener") << "should respond" << LL_ENDL; + note->respond(LLSD::emptyMap()); + // Did that succeed in removing the notification? Only cancel() if + // it's still around -- otherwise we get an LL_ERRS crash! + note = mNotifications.find(notification["id"]); + if (note) + { + LL_INFOS("LLNotificationsListener") << "respond() didn't clear, canceling" << LL_ENDL; + mNotifications.cancel(note); + } + } + return false; // let other listeners get same notification +} + +bool LLNotificationsListener::Forwarder::matchType(const LLSD& filter, const std::string& type) const +{ + // Decide whether this notification matches filter: + // undefined: forward all notifications + if (filter.isUndefined()) + { + return true; + } + // array of string: forward any notification matching any named type + if (filter.isArray()) + { + for (LLSD::array_const_iterator ti(filter.beginArray()), tend(filter.endArray()); + ti != tend; ++ti) + { + if (ti->asString() == type) + { + return true; + } + } + // Didn't match any entry in the array + return false; + } + // string: forward only the specific named type + return (filter.asString() == type); +} + +LLSD LLNotificationsListener::asLLSD(LLNotificationPtr note) +{ + LLSD notificationInfo(note->asLLSD()); + // For some reason the following aren't included in asLLSD(). + notificationInfo["summary"] = note->summarize(); + notificationInfo["id"] = note->id(); + notificationInfo["type"] = note->getType(); + notificationInfo["message"] = note->getMessage(); + notificationInfo["label"] = note->getLabel(); + return notificationInfo; +} diff --git a/indra/llui/llnotificationslistener.h b/indra/llui/llnotificationslistener.h index 9b405d7b4b..de208b57f0 100644 --- a/indra/llui/llnotificationslistener.h +++ b/indra/llui/llnotificationslistener.h @@ -13,6 +13,10 @@ #define LL_LLNOTIFICATIONSLISTENER_H #include "lleventapi.h" +#include "llnotificationptr.h" +#include <boost/shared_ptr.hpp> +#include <map> +#include <string> class LLNotifications; class LLSD; @@ -21,13 +25,27 @@ class LLNotificationsListener : public LLEventAPI { public: LLNotificationsListener(LLNotifications & notifications); + ~LLNotificationsListener(); +private: void requestAdd(LLSD const & event_data) const; -private: void NotificationResponder(const std::string& replypump, const LLSD& notification, const LLSD& response) const; + + void listChannels(const LLSD& params) const; + void listChannelNotifications(const LLSD& params) const; + void respond(const LLSD& params) const; + void cancel(const LLSD& params) const; + void ignore(const LLSD& params) const; + void forward(const LLSD& params); + + static LLSD asLLSD(LLNotificationPtr); + + class Forwarder; + typedef std::map<std::string, boost::shared_ptr<Forwarder> > ForwarderMap; + ForwarderMap mForwarders; LLNotifications & mNotifications; }; diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 7350457274..983f0a2d49 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -80,7 +80,7 @@ std::string LLUrlEntryBase::escapeUrl(const std::string &url) const "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" - "-._~!$?&()*+,@:;=/%"; + "-._~!$?&()*+,@:;=/%#"; std::sort(no_escape_chars.begin(), no_escape_chars.end()); initialized = true; @@ -201,7 +201,7 @@ std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) // LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol() { - mPattern = boost::regex("(\\bwww\\.\\S+\\.\\S+|\\S+.com\\S*|\\S+.net\\S*|\\S+.edu\\S*|\\S+.org\\S*)", + mPattern = boost::regex("(\\bwww\\.\\S+\\.\\S+|\\b[^ \t\n\r\f\v:/]+.com\\S*|\\b[^ \t\n\r\f\v:/]+.net\\S*|\\b[^ \t\n\r\f\v:/]+.edu\\S*|\\b[^ \t\n\r\f\v:/]+.org\\S*)", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_http.xml"; mTooltip = LLTrans::getString("TooltipHttpUrl"); diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index afcff0d409..ad5c0911f8 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -223,3 +223,23 @@ bool LLUrlRegistry::hasUrl(const LLWString &text) LLUrlMatch match; return findUrl(text, match); } + +bool LLUrlRegistry::isUrl(const std::string &text) +{ + LLUrlMatch match; + if (findUrl(text, match)) + { + return (match.getStart() == 0 && match.getEnd() >= text.size()-1); + } + return false; +} + +bool LLUrlRegistry::isUrl(const LLWString &text) +{ + LLUrlMatch match; + if (findUrl(text, match)) + { + return (match.getStart() == 0 && match.getEnd() >= text.size()-1); + } + return false; +} diff --git a/indra/llui/llurlregistry.h b/indra/llui/llurlregistry.h index d7800d8cfc..399ee0a988 100644 --- a/indra/llui/llurlregistry.h +++ b/indra/llui/llurlregistry.h @@ -85,6 +85,10 @@ public: bool hasUrl(const std::string &text); bool hasUrl(const LLWString &text); + // return true if the given string is a URL that findUrl would match + bool isUrl(const std::string &text); + bool isUrl(const LLWString &text); + private: LLUrlRegistry(); friend class LLSingleton<LLUrlRegistry>; |