This file is indexed.

/usr/lib/python2.7/dist-packages/Mailnag/common/plugins.py is in mailnag 1.0.0-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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#
# plugins.py
#
# Copyright 2013, 2014 Patrick Ulbrich <zulu99@gmx.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#

import os
import imp
import inspect
import logging

from common.config import cfg_folder
from common.dist_cfg import LIB_DIR

PLUGIN_LIB_PATH = os.path.join(LIB_DIR, 'plugins')
PLUGIN_USER_PATH = os.path.join(cfg_folder, 'plugins')
PLUGIN_PATHS = [ PLUGIN_LIB_PATH, PLUGIN_USER_PATH ]

#
# All known hook types.
#
# TODO : make this class an enum 
# when Mailnag is ported to python3
class HookTypes:
	# func signature: 
	# IN:	None
	# OUT:	None
	MAIL_CHECK = 'mail-check'
	# func signature: 
	# IN:	new mails, all mails
	# OUT:	None
	MAILS_ADDED = 'mails-added'
	# func signature:
	# IN:	remaining mails
	# OUT:	None
	MAILS_REMOVED = 'mails-removed'
	# func signature:
	# IN:	all mails
	# OUT:	filtered mails
	FILTER_MAILS = 'filter-mails'


#
# Registry class for plugin hooks.
#
# Registered hook functions must not block the mailnag daemon.
# Hook functions with an execution time > 1s should be 
# implemented non-blocking (i. e. asynchronously).
class HookRegistry:
	def __init__(self):
		self._hooks = {
			HookTypes.MAIL_CHECK	: [],
			HookTypes.MAILS_ADDED	: [],
			HookTypes.MAILS_REMOVED	: [],
			HookTypes.FILTER_MAILS 	: []
		}
	
	
	def register_hook_func(self, hooktype, func):
		self._hooks[hooktype].append(func)
	
	
	def unregister_hook_func(self, hooktype, func):
		self._hooks[hooktype].remove(func)
	
	
	def get_hook_funcs(self, hooktype):
		return self._hooks[hooktype]


# Abstract base class for a MailnagController instance 
# passed to plugins.
class MailnagController:
	# Returns a HookRegistry object.
	def get_hooks(self):					pass
	# Shuts down the Mailnag process.
	# May throw an InvalidOperationException.
	def shutdown(self):						pass
	# Enforces a manual mail check.
	# May throw an InvalidOperationException.
	def check_for_mails(self):				pass
	# Marks the mail with specified mail_id as read.
	# May throw an InvalidOperationException.
	def mark_mail_as_read(self, mail_id):	pass


#
# Mailnag Plugin base class
#
class Plugin:
	def __init__(self):
		# Plugins shouldn't do anything in the constructor. 
		# They are expected to start living if they are actually 
		# enabled (i.e. in the enable() method).
		# Plugin data isn't enabled yet and call to methods like
		# get_mailnag_controller() or get_config().
		pass
	
	#
	# Abstract methods, 
	# to be overriden by derived plugin types.
	#
	def enable(self):
		# Plugins are expected to
		# register all hooks here.
		raise NotImplementedError
	
	
	def disable(self):
		# Plugins are expected to
		# unregister all hooks here, 
		# free all allocated resources, 
		# and terminate threads (if any).
		raise NotImplementedError
	
	
	def get_manifest(self):
		# Plugins are expected to
		# return a tuple of the following form:
		# (name, description, version, author, mandatory).
		raise NotImplementedError
	
	
	def get_default_config(self):
		# Plugins are expected to return a
		# dictionary with default values.
		raise NotImplementedError
	
	
	def has_config_ui(self):
		# Plugins are expected to return True if
		# they provide a configuration widget,
		# otherwise they must return False.
		raise NotImplementedError
	
	
	def get_config_ui(self):
		# Plugins are expected to
		# return a GTK widget here.
		# Return None if the plugin 
		# does not need a config widget.
		raise NotImplementedError

	
	def load_ui_from_config(self, config_ui):
		# Plugins are expected to
		# load their config values (get_config()) 
		# in the widget returned by get_config_ui().
		raise NotImplementedError
	
	
	def save_ui_to_config(self, config_ui):
		# Plugins are expected to
		# save the config values of the widget
		# returned by get_config_ui() to their config
		# (get_config()).
		raise NotImplementedError
	
	
	#
	# Public methods
	#
	def init(self, modname, cfg, mailnag_controller):
		config = {}
		
		# try to load plugin config
		if cfg.has_section(modname):
			for name, value in cfg.items(modname):
				config[name] = value
		
		# sync with default config
		default_config = self.get_default_config()
		for k, v in default_config.iteritems():
			if not config.has_key(k):
				config[k] = v
		
		self._modname = modname
		self._config = config
		self._mailnag_controller = mailnag_controller
	
	
	def get_name(self):
		name = self.get_manifest()[0]
		return name
	
	
	def get_modname(self):
		return self._modname
	
	
	def get_config(self):
		return self._config
	
	
	#
	# Protected methods
	#
	def get_mailnag_controller(self):
		return self._mailnag_controller
	
	
	#
	# Static methods
	#
	
	# Note : Plugin instances do not own 
	# a reference to MailnagController object 
	# when instantiated in *config mode*.
	@staticmethod
	def load_plugins(cfg, mailnag_controller = None, filter_names = None):
		plugins = []
		plugin_types = Plugin._load_plugin_types()
		
		for modname, t in plugin_types:			
			try:
				if (filter_names == None) or (modname in filter_names):
					p = t()
					p.init(modname, cfg, mailnag_controller)
					plugins.append(p)
			except:
				logging.exception("Failed to instantiate plugin '%s'" % modname)
		
		return plugins
	
	
	@staticmethod
	def _load_plugin_types():
		plugin_types = []
		
		for path in PLUGIN_PATHS:
			if not os.path.exists(path):
				continue
			
			for f in os.listdir(path):
				mod = None
				modname, ext = os.path.splitext(f)
				
				try:
					if ext.lower() == '.py':
						if not os.path.exists(os.path.join(path, modname + '.pyc')):
							mod = imp.load_source(modname, os.path.join(path, f))
					elif ext.lower() == '.pyc':
						mod = imp.load_compiled(modname, os.path.join(path, f))
				
					if mod != None:
						for t in dir(mod):
							t = getattr(mod, t)
							if inspect.isclass(t) and \
								(inspect.getmodule(t) == mod) and \
								issubclass(t, Plugin):
								plugin_types.append((modname, t))
						
				except:
					logging.exception("Error while opening plugin file '%s'" % f)
		
		return plugin_types