This file is indexed.

/usr/share/pyshared/quodlibet/plugins/songsmenu.py is in exfalso 2.4-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# Copyright 2006 Joe Wreschnig
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation

import gtk

from quodlibet import qltk
from quodlibet.util import print_exc
from quodlibet.plugins import ListWrapper, Manager

class SongsMenuPlugin(gtk.ImageMenuItem):
    """Plugins of this type are subclasses of gtk.ImageMenuItem.
    They will be added, in alphabetical order, to the "Plugins" menu
    that appears when songs or lists of songs are right-clicked.
    They provide one or more of the following instance methods:
    
        self.plugin_single_song(song)
        self.plugin_song(song)
        self.plugin_songs(songs)
        self.plugin_single_album(album)
        self.plugin_album(album)
        self.plugin_albums(albums)

    All matching provided callables on a single object are called in the
    above order if they match until one returns a true value. They are
    not called with real AudioFile objects, but rather wrappers that
    automatically detect metadata or disk changes, and save or reload
    the files as appropriate.

    The single_ variant is only called if a single song/album is selected.

    The singular tense is called once for each selected song/album, but the
    plural tense is called with a list of songs/albums.

    An album is a list of songs all with the same album, labelid,
    and/or musicbrainz_albumid tags (like in the Album List).

    To make your plugin insensitive if unsupported songs are selected,
    a method that takes a list of songs and returns True or False to set
    the sensitivity of the menu entry:
        self.plugin_handles(songs)

    When these functions are called, the self.plugin_window will be
    available. This is the gtk.Window the plugin was invoked from. This
    provides access to two important widgets, self.plugin_window.browser
    and self.plugin_window.songlist.

    All of this is managed by the constructor for SongsMenuPlugin, so
    make sure it gets called if you override it (you shouldn't have to).
    """

    plugin_single_song = None
    plugin_song = None
    plugin_songs = None
    plugin_single_album = None
    plugin_album = None
    plugin_albums = None

    __initialized = False
    def __init__(self, songs):
        super(SongsMenuPlugin, self).__init__(self.PLUGIN_NAME)
        self.__initialized = True
        try: i = gtk.image_new_from_stock(self.PLUGIN_ICON, gtk.ICON_SIZE_MENU)
        except AttributeError: pass
        else: self.set_image(i)
        self.set_sensitive(bool(self.plugin_handles(songs)))

    @property
    def initialized(self):
        # If the GObject __init__ method is bypassed, it can cause segfaults.
        # This explicitly prevents a bad plugin from taking down the app.
        return self.__initialized

    def plugin_handles(self, songs):
        return True

class SongsMenuPlugins(Manager):
    Kinds = [SongsMenuPlugin]

    def Menu(self, library, parent, songs):
        songs = ListWrapper(songs)
        parent = qltk.get_top_parent(parent)

        attrs = ['plugin_song', 'plugin_songs',
                 'plugin_album', 'plugin_albums']

        if len(songs) == 1: attrs.append('plugin_single_song')

        last = (songs and songs[-1]) or None
        for song in songs:
            if song.album_key != last.album_key:
                break
            last = song
        else:
            attrs.append('plugin_single_album')

        items = []
        kinds = self.find_subclasses(SongsMenuPlugin)
        kinds.sort(key=lambda plugin: plugin.PLUGIN_ID)
        for Kind in kinds:
            usable = max([callable(getattr(Kind, s)) for s in attrs])
            if usable:
                try: items.append(Kind(songs))
                except: print_exc()
        items = filter(lambda i: i.initialized, items)

        if items:
            menu = gtk.Menu()
            for item in items:
                try:
                    menu.append(item)
                    args = (library, parent, songs)
                    if item.get_submenu():
                        for subitem in item.get_submenu().get_children():
                            subitem.connect_object(
                                'activate', self.__handle, item, *args)
                    else:
                        item.connect('activate', self.__handle, *args)
                except:
                    print_exc()
                    item.destroy()

        else: menu = None
        return menu

    def __get_albums(self, songs):
        albums = {}
        for song in songs:
            key = song.album_key
            if key not in albums:
                albums[key] = []
            albums[key].append(song)

        albums = albums.values()
        map(list.sort, albums)
        return albums

    def __handle(self, plugin, library, parent, songs):
        if len(songs) == 0: return

        plugin.plugin_window = parent
        try:
            if len(songs) == 1 and callable(plugin.plugin_single_song):
                try: ret = plugin.plugin_single_song(songs[0])
                except Exception: print_exc()
                else:
                    if ret: return
            if callable(plugin.plugin_song):
                try: ret = map(plugin.plugin_song, songs)
                except Exception: print_exc()
                else:
                    if max(ret): return
            if callable(plugin.plugin_songs):
                try: ret = plugin.plugin_songs(songs)
                except Exception: print_exc()
                else:
                    if ret: return

            if max(map(callable,(plugin.plugin_single_album,
                plugin.plugin_album, plugin.plugin_albums))):
                albums = self.__get_albums(songs)

            if callable(plugin.plugin_single_album) and len(albums) == 1:
                try: ret = plugin.plugin_single_album(albums[0])
                except Exception: print_exc()
                else:
                    if ret: return
            if callable(plugin.plugin_album):
                try: ret = map(plugin.plugin_album, albums)
                except Exception: print_exc()
                else:
                    if max(ret): return
            if callable(plugin.plugin_albums):
                try: ret = plugin.plugin_albums(albums)
                except Exception: print_exc()
                else:
                    if ret: return

        finally:
            del(plugin.plugin_window)
            self._check_change(library, parent, filter(None, songs))