diff options
author | Armin Weatherwax | 2011-03-13 15:19:55 +0100 |
---|---|---|
committer | Armin Weatherwax | 2011-03-13 15:19:55 +0100 |
commit | b543f291fcf945330fd5a533cc7235c20d7cb4c5 (patch) | |
tree | 46e725a9a11f6d54535f869d0a2494efaeb6985c /linden/indra/newview/llviewerparcelmedia.cpp | |
parent | fix voice doesn't kick in. (diff) | |
download | meta-impy-b543f291fcf945330fd5a533cc7235c20d7cb4c5.zip meta-impy-b543f291fcf945330fd5a533cc7235c20d7cb4c5.tar.gz meta-impy-b543f291fcf945330fd5a533cc7235c20d7cb4c5.tar.bz2 meta-impy-b543f291fcf945330fd5a533cc7235c20d7cb4c5.tar.xz |
Henri Beauchamps version of Sione Lomus media filter patch.
from http://sldev.free.fr/ : "MediaFilter_v3: based on code by Sione Lomu with a couple of bugfixes by Tonya Souther, this improved patch brings media and streaming audio URLs filtering (to prevent IP ripping by so-called security systems which violate the SL TOS by catching your IP and associating your various avatars with it, thus violating your anonimity). Beside empowering your viewer with allow/deny/blacklist/whitelist functions per domain, this improved patch makes the difference between external servers (domains names filtering) and in-world servers (scripted objects with built-in HTTP servers). I also fixed various bugs, security holes and shortcomings, refactored the code and improved it, and added a whitelist/blacklist erasing function."
Imprudence changes: added "MediaFilter" debug to be able to inspect the full url and media texture uuid
Diffstat (limited to 'linden/indra/newview/llviewerparcelmedia.cpp')
-rw-r--r-- | linden/indra/newview/llviewerparcelmedia.cpp | 397 |
1 files changed, 393 insertions, 4 deletions
diff --git a/linden/indra/newview/llviewerparcelmedia.cpp b/linden/indra/newview/llviewerparcelmedia.cpp index d4ebbd9..a5d97f2 100644 --- a/linden/indra/newview/llviewerparcelmedia.cpp +++ b/linden/indra/newview/llviewerparcelmedia.cpp | |||
@@ -33,6 +33,7 @@ | |||
33 | #include "llviewerprecompiledheaders.h" | 33 | #include "llviewerprecompiledheaders.h" |
34 | #include "llviewerparcelmedia.h" | 34 | #include "llviewerparcelmedia.h" |
35 | 35 | ||
36 | #include "kokuastreamingaudio.h" | ||
36 | #include "llagent.h" | 37 | #include "llagent.h" |
37 | #include "llviewercontrol.h" | 38 | #include "llviewercontrol.h" |
38 | #include "llviewermedia.h" | 39 | #include "llviewermedia.h" |
@@ -46,17 +47,27 @@ | |||
46 | #include "llviewerwindow.h" | 47 | #include "llviewerwindow.h" |
47 | #include "llfirstuse.h" | 48 | #include "llfirstuse.h" |
48 | #include "llpluginclassmedia.h" | 49 | #include "llpluginclassmedia.h" |
50 | #include "llnotify.h" | ||
51 | #include "llsdserialize.h" | ||
49 | 52 | ||
53 | #include "lloverlaybar.h" | ||
54 | #include "slfloatermediafilter.h" | ||
55 | #include "llinventorymodel.h" | ||
50 | // Static Variables | 56 | // Static Variables |
51 | 57 | ||
52 | S32 LLViewerParcelMedia::sMediaParcelLocalID = 0; | 58 | S32 LLViewerParcelMedia::sMediaParcelLocalID = 0; |
53 | LLUUID LLViewerParcelMedia::sMediaRegionID; | 59 | LLUUID LLViewerParcelMedia::sMediaRegionID; |
54 | viewer_media_t LLViewerParcelMedia::sMediaImpl; | 60 | viewer_media_t LLViewerParcelMedia::sMediaImpl; |
55 | 61 | bool LLViewerParcelMedia::sIsUserAction = false; | |
62 | bool LLViewerParcelMedia::sMediaFilterListLoaded = false; | ||
63 | LLSD LLViewerParcelMedia::sMediaFilterList; | ||
64 | std::set<std::string> LLViewerParcelMedia::sMediaQueries; | ||
65 | std::set<std::string> LLViewerParcelMedia::sAllowedMedia; | ||
66 | std::set<std::string> LLViewerParcelMedia::sDeniedMedia; | ||
56 | 67 | ||
57 | // Local functions | 68 | // Local functions |
58 | bool callback_play_media(const LLSD& notification, const LLSD& response, LLParcel* parcel); | 69 | bool callback_play_media(const LLSD& notification, const LLSD& response, LLParcel* parcel); |
59 | 70 | void callback_media_alert(const LLSD& notification, const LLSD& response, LLParcel* parcel, U32 type, std::string domain); | |
60 | 71 | ||
61 | // static | 72 | // static |
62 | void LLViewerParcelMedia::initClass() | 73 | void LLViewerParcelMedia::initClass() |
@@ -175,7 +186,7 @@ void LLViewerParcelMedia::update(LLParcel* parcel) | |||
175 | } | 186 | } |
176 | 187 | ||
177 | // static | 188 | // static |
178 | void LLViewerParcelMedia::play(LLParcel* parcel) | 189 | void LLViewerParcelMedia::play(LLParcel* parcel, bool filter) |
179 | { | 190 | { |
180 | lldebugs << "LLViewerParcelMedia::play" << llendl; | 191 | lldebugs << "LLViewerParcelMedia::play" << llendl; |
181 | 192 | ||
@@ -185,7 +196,17 @@ void LLViewerParcelMedia::play(LLParcel* parcel) | |||
185 | return; | 196 | return; |
186 | 197 | ||
187 | std::string media_url = parcel->getMediaURL(); | 198 | std::string media_url = parcel->getMediaURL(); |
188 | std::string media_current_url = parcel->getMediaCurrentURL(); | 199 | LLStringUtil::trim(media_url); |
200 | |||
201 | if (!media_url.empty() && gSavedSettings.getBOOL("MediaEnableFilter") && (filter || !allowedMedia(media_url))) | ||
202 | { | ||
203 | // If filtering is needed or in case media_url just changed | ||
204 | // to something we did not yet approve. | ||
205 | LLViewerParcelMediaAutoPlay::playStarted(); | ||
206 | filterMedia(parcel, 0); | ||
207 | return; | ||
208 | } | ||
209 | |||
189 | std::string mime_type = parcel->getMediaType(); | 210 | std::string mime_type = parcel->getMediaType(); |
190 | LLUUID placeholder_texture_id = parcel->getMediaID(); | 211 | LLUUID placeholder_texture_id = parcel->getMediaID(); |
191 | U8 media_auto_scale = parcel->getMediaAutoScale(); | 212 | U8 media_auto_scale = parcel->getMediaAutoScale(); |
@@ -403,6 +424,8 @@ void LLViewerParcelMedia::processParcelMediaUpdate( LLMessageSystem *msg, void * | |||
403 | media_url = media_url_buffer; | 424 | media_url = media_url_buffer; |
404 | msg->getU8("DataBlock", "MediaAutoScale", media_auto_scale); | 425 | msg->getU8("DataBlock", "MediaAutoScale", media_auto_scale); |
405 | 426 | ||
427 | LL_DEBUGS("MediaFilter") << "New media texture id: " << media_id << LL_ENDL; | ||
428 | |||
406 | if (msg->has("DataBlockExtended")) // do we have the extended data? | 429 | if (msg->has("DataBlockExtended")) // do we have the extended data? |
407 | { | 430 | { |
408 | char media_type_buffer[257]; | 431 | char media_type_buffer[257]; |
@@ -438,6 +461,7 @@ void LLViewerParcelMedia::processParcelMediaUpdate( LLMessageSystem *msg, void * | |||
438 | 461 | ||
439 | play(parcel); | 462 | play(parcel); |
440 | } | 463 | } |
464 | |||
441 | } | 465 | } |
442 | } | 466 | } |
443 | // Static | 467 | // Static |
@@ -586,3 +610,368 @@ void LLViewerParcelMediaNavigationObserver::onNavigateComplete( const EventType& | |||
586 | 610 | ||
587 | } | 611 | } |
588 | */ | 612 | */ |
613 | |||
614 | void LLViewerParcelMedia::playStreamingMusic(LLParcel* parcel, bool filter) | ||
615 | { | ||
616 | std::string music_url = parcel->getMusicURL(); | ||
617 | LLStringUtil::trim(music_url); | ||
618 | if (!music_url.empty() && gSavedSettings.getBOOL("MediaEnableFilter") && (filter || !allowedMedia(music_url))) | ||
619 | { | ||
620 | // If filtering is needed or in case music_url just changed | ||
621 | // to something we did not yet approve. | ||
622 | filterMedia(parcel, 1); | ||
623 | } | ||
624 | else if (gAudioStream) | ||
625 | { | ||
626 | LLStringUtil::trim(music_url); | ||
627 | gAudioStream->startInternetStream(music_url); | ||
628 | if (music_url.empty()) | ||
629 | { | ||
630 | LLOverlayBar::audioFilterStop(); | ||
631 | } | ||
632 | else | ||
633 | { | ||
634 | LLOverlayBar::audioFilterPlay(); | ||
635 | } | ||
636 | } | ||
637 | } | ||
638 | |||
639 | void LLViewerParcelMedia::stopStreamingMusic() | ||
640 | { | ||
641 | if (gAudioStream) | ||
642 | { | ||
643 | gAudioStream->stopInternetStream(); | ||
644 | LLOverlayBar::audioFilterStop(); | ||
645 | } | ||
646 | } | ||
647 | |||
648 | bool LLViewerParcelMedia::allowedMedia(std::string media_url) | ||
649 | { | ||
650 | LLStringUtil::trim(media_url); | ||
651 | std::string domain = extractDomain(media_url); | ||
652 | if (sAllowedMedia.count(domain)) | ||
653 | { | ||
654 | return true; | ||
655 | } | ||
656 | for (S32 i = 0; i < (S32)sMediaFilterList.size(); i++) | ||
657 | { | ||
658 | if (sMediaFilterList[i]["domain"].asString() == domain) | ||
659 | { | ||
660 | if (sMediaFilterList[i]["action"].asString() == "allow") | ||
661 | { | ||
662 | return true; | ||
663 | } | ||
664 | else | ||
665 | { | ||
666 | return false; | ||
667 | } | ||
668 | } | ||
669 | } | ||
670 | return false; | ||
671 | } | ||
672 | |||
673 | void LLViewerParcelMedia::filterMedia(LLParcel* parcel, U32 type) | ||
674 | { | ||
675 | std::string media_action; | ||
676 | std::string media_url; | ||
677 | std::string domain; | ||
678 | |||
679 | if (parcel != LLViewerParcelMgr::getInstance()->getAgentParcel()) | ||
680 | { | ||
681 | // The parcel just changed (may occur right out after a TP) | ||
682 | sIsUserAction = false; | ||
683 | return; | ||
684 | } | ||
685 | |||
686 | if (type == 0) | ||
687 | { | ||
688 | media_url = parcel->getMediaURL(); | ||
689 | } | ||
690 | else | ||
691 | { | ||
692 | media_url = parcel->getMusicURL(); | ||
693 | } | ||
694 | LLStringUtil::trim(media_url); | ||
695 | |||
696 | LL_DEBUGS("MediaFilter") << "Requested " << (type == 0 ? "media" : "music") << "-URL: " << media_url << LL_ENDL; | ||
697 | |||
698 | domain = extractDomain(media_url); | ||
699 | |||
700 | if (sMediaQueries.count(domain) > 0) | ||
701 | { | ||
702 | sIsUserAction = false; | ||
703 | return; | ||
704 | } | ||
705 | |||
706 | if (sIsUserAction) | ||
707 | { | ||
708 | // This was a user manual request to play this media, so give | ||
709 | // it another chance... | ||
710 | sIsUserAction = false; | ||
711 | if (sDeniedMedia.count(domain)) | ||
712 | { | ||
713 | sDeniedMedia.erase(domain); | ||
714 | SLFloaterMediaFilter::setDirty(); | ||
715 | } | ||
716 | } | ||
717 | |||
718 | if (!sMediaFilterListLoaded || sDeniedMedia.count(domain)) | ||
719 | { | ||
720 | media_action = "ignore"; | ||
721 | } | ||
722 | else if (sAllowedMedia.count(domain)) | ||
723 | { | ||
724 | media_action = "allow"; | ||
725 | } | ||
726 | else | ||
727 | { | ||
728 | for (S32 i = 0; i < (S32)sMediaFilterList.size(); i++) | ||
729 | { | ||
730 | if (sMediaFilterList[i]["domain"].asString() == domain) | ||
731 | { | ||
732 | media_action = sMediaFilterList[i]["action"].asString(); | ||
733 | break; | ||
734 | } | ||
735 | } | ||
736 | } | ||
737 | |||
738 | if (media_action == "allow" || media_url.empty()) | ||
739 | { | ||
740 | if (type == 0) | ||
741 | { | ||
742 | play(parcel, false); | ||
743 | } | ||
744 | else | ||
745 | { | ||
746 | playStreamingMusic(parcel, false); | ||
747 | } | ||
748 | } | ||
749 | else if (media_action == "deny") | ||
750 | { | ||
751 | LLSD args; | ||
752 | args["DOMAIN"] = domain; | ||
753 | LLNotifications::instance().add("MediaBlocked", args); | ||
754 | if (type == 1) | ||
755 | { | ||
756 | LLViewerParcelMedia::stopStreamingMusic(); | ||
757 | } | ||
758 | // So to avoid other "blocked" messages later in the session | ||
759 | // for this url should it be requested again by a script. | ||
760 | sDeniedMedia.insert(domain); | ||
761 | } | ||
762 | else if (media_action == "ignore") | ||
763 | { | ||
764 | if (type == 1) | ||
765 | { | ||
766 | LLViewerParcelMedia::stopStreamingMusic(); | ||
767 | } | ||
768 | } | ||
769 | else | ||
770 | { | ||
771 | sMediaQueries.insert(domain); | ||
772 | LLSD args; | ||
773 | args["DOMAIN"] = domain; | ||
774 | if (media_url.find('?') != std::string::npos) | ||
775 | { | ||
776 | args["WARNING"] = " (WARNING: this URL also contains parameter(s) that could potentially be used to correlate your avatar name with your IP)"; | ||
777 | } | ||
778 | else | ||
779 | { | ||
780 | args["WARNING"] = ""; | ||
781 | } | ||
782 | if (type == 0) | ||
783 | { | ||
784 | args["TYPE"] = "a media"; | ||
785 | } | ||
786 | else | ||
787 | { | ||
788 | args["TYPE"] = "an audio"; | ||
789 | } | ||
790 | LLNotifications::instance().add("MediaAlert", args, LLSD(), boost::bind(callback_media_alert, _1, _2, parcel, type, domain)); | ||
791 | } | ||
792 | } | ||
793 | |||
794 | void callback_media_alert(const LLSD ¬ification, const LLSD &response, LLParcel* parcel, U32 type, std::string domain) | ||
795 | { | ||
796 | S32 option = LLNotification::getSelectedOption(notification, response); | ||
797 | |||
798 | LLSD args; | ||
799 | args["DOMAIN"] = domain; | ||
800 | |||
801 | if (option == 0 || option == 3) // Allow or Whitelist | ||
802 | { | ||
803 | LLViewerParcelMedia::sAllowedMedia.insert(domain); | ||
804 | if (option == 3) // Whitelist | ||
805 | { | ||
806 | LLSD newmedia; | ||
807 | newmedia["domain"] = domain; | ||
808 | newmedia["action"] = "allow"; | ||
809 | LLViewerParcelMedia::sMediaFilterList.append(newmedia); | ||
810 | LLViewerParcelMedia::saveDomainFilterList(); | ||
811 | args["LISTED"] = "whitelisted"; | ||
812 | LLNotifications::instance().add("MediaListed", args); | ||
813 | } | ||
814 | if (type == 0) | ||
815 | { | ||
816 | LLViewerParcelMedia::play(parcel, false); | ||
817 | } | ||
818 | else | ||
819 | { | ||
820 | LLViewerParcelMedia::playStreamingMusic(parcel, false); | ||
821 | } | ||
822 | } | ||
823 | else if (option == 1 || option == 2) // Deny or Blacklist | ||
824 | { | ||
825 | LLViewerParcelMedia::sDeniedMedia.insert(domain); | ||
826 | if (type == 1) | ||
827 | { | ||
828 | LLViewerParcelMedia::stopStreamingMusic(); | ||
829 | } | ||
830 | if (option == 1) // Deny | ||
831 | { | ||
832 | LLNotifications::instance().add("MediaBlocked", args); | ||
833 | } | ||
834 | else // Blacklist | ||
835 | { | ||
836 | LLSD newmedia; | ||
837 | newmedia["domain"] = domain; | ||
838 | newmedia["action"] = "deny"; | ||
839 | LLViewerParcelMedia::sMediaFilterList.append(newmedia); | ||
840 | LLViewerParcelMedia::saveDomainFilterList(); | ||
841 | args["LISTED"] = "blacklisted"; | ||
842 | LLNotifications::instance().add("MediaListed", args); | ||
843 | } | ||
844 | } | ||
845 | |||
846 | LLViewerParcelMedia::sMediaQueries.erase(domain); | ||
847 | SLFloaterMediaFilter::setDirty(); | ||
848 | } | ||
849 | |||
850 | void LLViewerParcelMedia::saveDomainFilterList() | ||
851 | { | ||
852 | std::string medialist_filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "media_filter.xml"); | ||
853 | |||
854 | llofstream medialistFile(medialist_filename); | ||
855 | LLSDSerialize::toPrettyXML(sMediaFilterList, medialistFile); | ||
856 | medialistFile.close(); | ||
857 | } | ||
858 | |||
859 | bool LLViewerParcelMedia::loadDomainFilterList() | ||
860 | { | ||
861 | sMediaFilterListLoaded = true; | ||
862 | |||
863 | std::string medialist_filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "media_filter.xml"); | ||
864 | |||
865 | if (!LLFile::isfile(medialist_filename)) | ||
866 | { | ||
867 | LLSD emptyllsd; | ||
868 | llofstream medialistFile(medialist_filename); | ||
869 | LLSDSerialize::toPrettyXML(emptyllsd, medialistFile); | ||
870 | medialistFile.close(); | ||
871 | } | ||
872 | |||
873 | if (LLFile::isfile(medialist_filename)) | ||
874 | { | ||
875 | llifstream medialistFile(medialist_filename); | ||
876 | LLSDSerialize::fromXML(sMediaFilterList, medialistFile); | ||
877 | medialistFile.close(); | ||
878 | SLFloaterMediaFilter::setDirty(); | ||
879 | return true; | ||
880 | } | ||
881 | else | ||
882 | { | ||
883 | return false; | ||
884 | } | ||
885 | } | ||
886 | |||
887 | void LLViewerParcelMedia::clearDomainFilterList() | ||
888 | { | ||
889 | sMediaFilterList.clear(); | ||
890 | sAllowedMedia.clear(); | ||
891 | sDeniedMedia.clear(); | ||
892 | saveDomainFilterList(); | ||
893 | LLNotifications::instance().add("MediaFiltersCleared"); | ||
894 | SLFloaterMediaFilter::setDirty(); | ||
895 | } | ||
896 | |||
897 | std::string LLViewerParcelMedia::extractDomain(std::string url) | ||
898 | { | ||
899 | static std::string last_region = "@"; | ||
900 | |||
901 | if (url.empty()) | ||
902 | { | ||
903 | return url; | ||
904 | } | ||
905 | |||
906 | LLStringUtil::toLower(url); | ||
907 | |||
908 | size_t pos = url.find("//"); | ||
909 | |||
910 | if (pos != std::string::npos) | ||
911 | { | ||
912 | size_t count = url.size() - pos + 2; | ||
913 | url = url.substr(pos + 2, count); | ||
914 | } | ||
915 | |||
916 | // Check that there is at least one slash in the URL and add a trailing | ||
917 | // one if not (for media/audio URLs such as http://mydomain.net) | ||
918 | if (url.find('/') == std::string::npos) | ||
919 | { | ||
920 | url += '/'; | ||
921 | } | ||
922 | |||
923 | // If there's a user:password@ part, remove it | ||
924 | pos = url.find('@'); | ||
925 | if (pos != std::string::npos && pos < url.find('/')) // if '@' is not before the first '/', then it's not a user:password | ||
926 | { | ||
927 | size_t count = url.size() - pos + 1; | ||
928 | url = url.substr(pos + 1, count); | ||
929 | } | ||
930 | |||
931 | if (url.find(gAgent.getRegion()->getHost().getHostName()) == 0 || url.find(last_region) == 0) | ||
932 | { | ||
933 | // This must be a scripted object rezzed in the region: | ||
934 | // extend the concept of "domain" to encompass the | ||
935 | // scripted object server id and avoid blocking all other | ||
936 | // objects at once in this region... | ||
937 | |||
938 | // Get rid of any port number | ||
939 | pos = url.find('/'); // We earlier made sure that there's one | ||
940 | url = gAgent.getRegion()->getHost().getHostName() + url.substr(pos); | ||
941 | |||
942 | pos = url.find('?'); | ||
943 | if (pos != std::string::npos) | ||
944 | { | ||
945 | // Get rid of any parameter | ||
946 | url = url.substr(0, pos); | ||
947 | } | ||
948 | |||
949 | pos = url.rfind('/'); | ||
950 | if (pos != std::string::npos) | ||
951 | { | ||
952 | // Get rid of the filename, if any, keeping only the server + path | ||
953 | url = url.substr(0, pos); | ||
954 | } | ||
955 | } | ||
956 | else | ||
957 | { | ||
958 | pos = url.find(':'); | ||
959 | if (pos != std::string::npos && pos < url.find('/')) | ||
960 | { | ||
961 | // Keep anything before the port number and strip the rest off | ||
962 | url = url.substr(0, pos); | ||
963 | } | ||
964 | else | ||
965 | { | ||
966 | pos = url.find('/'); // We earlier made sure that there's one | ||
967 | url = url.substr(0, pos); | ||
968 | } | ||
969 | } | ||
970 | |||
971 | |||
972 | // Remember this region, so to cope with requests occuring just after a | ||
973 | // TP out of it. | ||
974 | last_region = gAgent.getRegion()->getHost().getHostName(); | ||
975 | |||
976 | return url; | ||
977 | } | ||