Tizen 2.1 base
[platform/upstream/hplip.git] / base / pkit.py
1 # -*- coding: utf-8 -*-
2 #
3 # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 #
19 # Author: Stan Dolson , Goutam Kodu
20 #
21
22 # Std Lib
23 import os
24 import os.path
25 import sys
26 import re
27 import time
28 import cStringIO
29 import ConfigParser
30 import shutil
31 import stat
32
33 # Local
34 from base.logger import *
35 from base.g import *
36 from base.codes import *
37 from base import utils, device
38
39 # DBus
40 import dbus
41 import dbus.service
42 import gobject
43
44 import warnings
45 # Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
46 # (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
47 warnings.simplefilter("ignore", DeprecationWarning)
48
49
50 class AccessDeniedException(dbus.DBusException):
51     _dbus_error_name = 'com.hp.hplip.AccessDeniedException'
52
53 class UnsupportedException(dbus.DBusException):
54     _dbus_error_name = 'com.hp.hplip.UnsupportedException'
55
56 class UsageError(dbus.DBusException):
57     _dbus_error_name = 'com.hp.hplip.UsageError'
58
59
60 POLICY_KIT_ACTION = "com.hp.hplip"
61 INSTALL_PLUGIN_ACTION = "com.hp.hplip.installplugin"
62
63
64 def get_service_bus():
65     return dbus.SystemBus()
66
67
68 def get_service(bus=None):
69     if not bus:
70         bus = get_service_bus()
71
72     service = bus.get_object(BackendService.SERVICE_NAME, '/')
73     service = dbus.Interface(service, BackendService.INTERFACE_NAME)
74     return service
75
76
77 class PolicyKitAuthentication(object):
78     def __init__(self):
79         super(PolicyKitAuthentication, self).__init__()
80         self.pkit = None
81         self.auth = None
82
83
84     def is_authorized(self, action_id, pid=None):
85         if pid == None:
86             pid = os.getpid()
87
88         pid = dbus.UInt32(pid)
89
90         authorized = self.policy_kit.IsProcessAuthorized(action_id, pid, False)
91         log.debug("is_authorized(%s) = %r" % (action_id, authorized))
92
93         return (authorized == 'yes')
94
95
96     def obtain_authorization(self, action_id, widget=None):
97         if self.is_authorized(action_id):
98             return True
99
100         xid = (widget and widget.get_toplevel().window.xid or 0)
101         xid, pid = dbus.UInt32(xid), dbus.UInt32(os.getpid())
102
103         granted = self.auth_agent.ObtainAuthorization(action_id, xid, pid)
104         log.debug("obtain_authorization(%s) = %r" % (action_id, granted))
105
106         return bool(granted)
107
108
109     def get_policy_kit(self):
110         if self.pkit:
111             return self.pkit
112
113         service = dbus.SystemBus().get_object('org.freedesktop.PolicyKit', '/')
114         self.pkit = dbus.Interface(service, 'org.freedesktop.PolicyKit')
115         return self.pkit
116
117     policy_kit = property(get_policy_kit)
118
119
120     def get_auth_agent(self):
121         if self.auth:
122             return self.auth
123
124         self.auth = dbus.SessionBus().get_object(
125             'org.freedesktop.PolicyKit.AuthenticationAgent', '/')
126         return self.auth
127
128     auth_agent = property(get_auth_agent)
129
130
131
132 class PolicyKitService(dbus.service.Object):
133     def check_permission_v0(self, sender, action=POLICY_KIT_ACTION):
134         if not sender:
135             log.error("Session not authorized by PolicyKit")
136             raise AccessDeniedException('Session not authorized by PolicyKit')
137
138         try:
139             policy_auth = PolicyKitAuthentication()
140             bus = dbus.SystemBus()
141
142             dbus_object = bus.get_object('org.freedesktop.DBus', '/')
143             dbus_object = dbus.Interface(dbus_object, 'org.freedesktop.DBus')
144
145             pid = dbus.UInt32(dbus_object.GetConnectionUnixProcessID(sender))
146
147             granted = policy_auth.is_authorized(action, pid)
148             if not granted:
149                 log.error("Process not authorized by PolicyKit")
150                 raise AccessDeniedException('Process not authorized by PolicyKit')
151
152             granted = policy_auth.policy_kit.IsSystemBusNameAuthorized(action,
153                                                                        sender,
154                                                                        False)
155             if granted != 'yes':
156                 log.error("Session not authorized by PolicyKit version 0")
157                 raise AccessDeniedException('Session not authorized by PolicyKit')
158
159         except AccessDeniedException:
160             log.warning("AccessDeniedException")
161             raise
162
163         except dbus.DBusException, ex:
164             log.warning("AccessDeniedException %r", ex)
165             raise AccessDeniedException(ex.message)
166
167
168     def check_permission_v1(self, sender, connection, action=POLICY_KIT_ACTION):
169         if not sender or not connection:
170             log.error("Session not authorized by PolicyKit")
171             raise AccessDeniedException('Session not authorized by PolicyKit')
172
173         system_bus = dbus.SystemBus()
174         obj = system_bus.get_object("org.freedesktop.PolicyKit1",
175                                     "/org/freedesktop/PolicyKit1/Authority",
176                                     "org.freedesktop.PolicyKit1.Authority")
177         policy_kit = dbus.Interface(obj, "org.freedesktop.PolicyKit1.Authority")
178         info = dbus.Interface(connection.get_object("org.freedesktop.DBus",
179                                                     "/org/freedesktop/DBus/Bus",
180                                                     False),
181                               "org.freedesktop.DBus")
182         pid = info.GetConnectionUnixProcessID(sender)
183         
184         subject = (
185             'unix-process',
186             { 'pid' : dbus.UInt32(pid, variant_level = 1) }
187         )
188         details = { '' : '' }
189         flags = dbus.UInt32(1)         # AllowUserInteraction = 0x00000001
190         cancel_id = ''
191
192         (ok, notused, details) = \
193             policy_kit.CheckAuthorization(subject,
194                                           action,
195                                           details,
196                                           flags,
197                                           cancel_id)
198         if not ok:
199             log.error("Session not authorized by PolicyKit version 1")
200
201         return ok
202
203
204 if utils.to_bool(sys_conf.get('configure', 'policy-kit')):
205     class BackendService(PolicyKitService):
206         INTERFACE_NAME = 'com.hp.hplip'
207         SERVICE_NAME   = 'com.hp.hplip'
208         LOGFILE_NAME   = '/tmp/hp-pkservice.log'
209
210         def __init__(self, connection=None, path='/', logfile=LOGFILE_NAME):
211             if connection is None:
212                 connection = get_service_bus()
213
214             super(BackendService, self).__init__(connection, path)
215
216             self.name = dbus.service.BusName(self.SERVICE_NAME, connection)
217             self.loop = gobject.MainLoop()
218             self.version = 0
219
220             log.set_logfile("%s.%d" % (logfile, os.getpid()))
221             log.set_level("debug")
222
223         def run(self, version=None):
224             if version is None:
225                 version = policykit_version()
226                 if version is None:
227                     log.error("Unable to determine installed PolicyKit version")
228                     return
229
230             self.version = version
231             log.set_where(Logger.LOG_TO_CONSOLE_AND_FILE)
232             log.debug("Starting back-end service loop (version %d)" % version)
233
234             self.loop.run()
235
236
237         @dbus.service.method(dbus_interface=INTERFACE_NAME,
238                                 in_signature='s', out_signature='b',
239                                 sender_keyword='sender',
240                                 connection_keyword='connection')
241         def installPlugin(self, src_dir, sender=None, connection=None):
242             if self.version == 0:
243                 try:
244                     self.check_permission_v0(sender, INSTALL_PLUGIN_ACTION)
245                 except AccessDeniedException, e:
246                     return False
247
248             elif self.version == 1:
249                 if not self.check_permission_v1(sender,
250                                                 connection,
251                                                 INSTALL_PLUGIN_ACTION):
252                     return False
253
254             else:
255                 log.error("installPlugin: invalid PolicyKit version %d" % self.version)
256                 return False
257
258             log.debug("installPlugin: installing from '%s'" % src_dir)
259
260             if not copyPluginFiles(src_dir):
261                 log.error("Plugin installation failed")
262                 return False
263
264             return True
265
266
267         @dbus.service.method(dbus_interface=INTERFACE_NAME,
268                                 in_signature='s', out_signature='b',
269                                 sender_keyword='sender',
270                                 connection_keyword='connection')
271         def shutdown(self, arg, sender=None, connection=None):
272             log.debug("Stopping backend service")
273             self.loop.quit()
274
275             return True
276
277
278
279 class PolicyKit(object):
280     def __init__(self, version=None):
281         if version is None:
282             version = policykit_version()
283             if version is None:
284                 log.debug("Unable to determine installed PolicyKit version")
285                 return
286
287         self.bus = dbus.SystemBus()
288         self.obj = self.bus.get_object(POLICY_KIT_ACTION, "/")
289         self.iface = dbus.Interface(self.obj, dbus_interface=POLICY_KIT_ACTION)
290         self.version = version
291
292     def installPlugin(self, src_dir):
293         if self.version == 0:
294             auth = PolicyKitAuthentication()
295             if not auth.is_authorized(INSTALL_PLUGIN_ACTION):
296                 if not auth.obtain_authorization(INSTALL_PLUGIN_ACTION):
297                     return None
298
299         try:
300             ok = self.iface.installPlugin(src_dir)
301             return ok
302         except dbus.DBusException, e:
303             log.debug("installPlugin: %s" % str(e))
304             return False
305
306
307     def shutdown(self):
308         if self.version == 0:
309             auth = PolicyKitAuthentication()
310             if not auth.is_authorized(INSTALL_PLUGIN_ACTION):
311                 if not auth.obtain_authorization(INSTALL_PLUGIN_ACTION):
312                     return None
313
314         try:
315             ok = self.iface.shutdown("")
316             return ok
317         except dbus.DBusException, e:
318             log.debug("shutdown: %s" % str(e))
319             return False
320
321
322
323 def copyPluginFiles(src_dir):
324     os.chdir(src_dir)
325
326     plugin_spec = ConfigBase("plugin.spec")
327     products = plugin_spec.keys("products")
328
329     BITNESS = utils.getBitness()
330     ENDIAN = utils.getEndian()
331     PPDDIR = sys_conf.get('dirs', 'ppd')
332     DRVDIR = sys_conf.get('dirs', 'drv')
333     HOMEDIR = sys_conf.get('dirs', 'home')
334     DOCDIR = sys_conf.get('dirs', 'doc')
335     CUPSBACKENDDIR = sys_conf.get('dirs', 'cupsbackend')
336     CUPSFILTERDIR = sys_conf.get('dirs', 'cupsfilter')
337     RULESDIR = '/etc/udev/rules.d'
338
339     processor = utils.getProcessor()
340     if processor == 'power_machintosh':
341         ARCH = 'ppc'
342     else:
343         ARCH = 'x86_%d' % BITNESS
344
345     if BITNESS == 64:
346         SANELIBDIR = '/usr/lib64/sane'
347         LIBDIR = '/usr/lib64'
348     else:
349         SANELIBDIR = '/usr/lib/sane'
350         LIBDIR = '/usr/lib'
351
352     copies = []
353
354     for PRODUCT in products:
355         MODEL = PRODUCT.replace('hp-', '').replace('hp_', '')
356         for s in plugin_spec.get("products", PRODUCT).split(','):
357
358             if not plugin_spec.has_section(s):
359                 log.error("Missing section [%s]" % s)
360                 return False
361
362             src = plugin_spec.get(s, 'src', '')
363             trg = plugin_spec.get(s, 'trg', '')
364             link = plugin_spec.get(s, 'link', '')
365
366             if not src:
367                 log.error("Missing 'src=' value in section [%s]" % s)
368                 return False
369
370             if not trg:
371                 log.error("Missing 'trg=' value in section [%s]" % s)
372                 return False
373
374             src = os.path.basename(utils.cat(src))
375             trg = utils.cat(trg)
376
377             if link:
378                 link = utils.cat(link)
379
380             copies.append((src, trg, link))
381
382     copies = utils.uniqueList(copies)
383     copies.sort()
384
385     os.umask(0)
386
387     for src, trg, link in copies:
388
389         if not os.path.exists(src):
390             log.debug("Source file %s does not exist. Skipping." % src)
391             continue
392
393         if os.path.exists(trg):
394             log.debug("Target file %s already exists. Replacing." % trg)
395             os.remove(trg)
396
397         trg_dir = os.path.dirname(trg)
398
399         if not os.path.exists(trg_dir):
400             log.debug("Target directory %s does not exist. Creating." % trg_dir)
401             os.makedirs(trg_dir, 0755)
402
403         if not os.path.isdir(trg_dir):
404             log.error("Target directory %s exists but is not a directory. Skipping." % trg_dir)
405             continue
406
407         try:
408             shutil.copyfile(src, trg)
409         except (IOError, OSError), e:
410             log.error("File copy failed: %s" % e.strerror)
411             continue
412
413         else:
414             if not os.path.exists(trg):
415                 log.error("Target file %s does not exist. File copy failed." % trg)
416                 continue
417             else:
418                 os.chmod(trg, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH)
419
420             if link:
421                 if os.path.exists(link):
422                     log.debug("Symlink already exists. Replacing.")
423                     os.remove(link)
424
425                 log.debug("Creating symlink %s (link) to file %s (target)..." %
426                     (link, trg))
427
428                 try:
429                     os.symlink(trg, link)
430                 except (OSError, IOError), e:
431                     log.debug("Unable to create symlink: %s" % e.strerror)
432                     pass
433
434     log.debug("Updating hplip.conf - installed = 1")
435     sys_state.set('plugin', "installed", '1')
436     log.debug("Updating hplip.conf - eula = 1")
437     sys_state.set('plugin', "eula", '1')
438     plugin_version = sys_conf.get('hplip', 'version', '0.0.0')
439     sys_state.set('plugin','version', plugin_version)
440     return True
441
442
443 def run_plugin_command(required=True, plugin_reason=PLUGIN_REASON_NONE):
444     su_sudo = None
445     need_sudo = True
446     name = None
447     version = None
448
449     if utils.to_bool(sys_conf.get('configure', 'policy-kit')):
450         try:
451             obj = PolicyKit()
452             su_sudo = "%s"
453             need_sudo = False
454             log.debug("Using PolicyKit for authentication")
455         except dbus.DBusException, ex:
456             log.error("PolicyKit NOT installed when configured for use")
457
458     if os.geteuid() == 0:
459         su_sudo = "%s"
460         need_sudo = False
461
462     password_f = None
463     if need_sudo:
464         su_sudo = utils.su_sudo()
465     if su_sudo is "su":
466         name,version,is_su = utils.os_release()
467         log.debug("name = %s version = %s is_su = %s" %(name,version,is_su))
468         if ( name == 'Fedora' and version >= '14' and is_su == True):
469            #using su opening GUI apps fail in Fedora 14. 
470            #To run GUI apps as root, you need a root login shell (su -) in Fedora 14   
471            su_sudo = 'su - -c "%s"'
472         else:
473            su_sudo = 'su -c "%s"'
474         password_f = "get_password_ui"    
475     if su_sudo is None:
476         log.error("Unable to find a suitable sudo command to run 'hp-plugin'")
477         return (False, False)
478
479     req = '--required'
480     if not required:
481         req = '--optional'
482
483
484     if utils.which("hp-plugin"):
485         p_path="hp-plugin"
486     else:
487         p_path="python ./plugin.py"
488
489     if 'gksu' in su_sudo:
490         cmd = su_sudo % ("%s -u %s --reason %s" % (p_path, req, plugin_reason))
491         cmd +=" -m" 
492         cmd += (" \"hp-plugin:- HP Device requires to install HP proprietary plugin. Please enter user (sudo) password to continue\"")
493     else:
494         cmd = su_sudo % ("%s -u %s --reason %s To_install_plugin_for_HP_Device" % (p_path, req, plugin_reason))
495
496     log.debug("%s" % cmd)
497     if password_f is not None:
498         status, output = utils.run(cmd, log_output=True, password_func=password_f, timeout=1)
499     else:
500         status, output = utils.run(cmd, log_output=True, password_func=None, timeout=1)
501     
502     return (status == 0, True)
503
504
505 def policykit_version():
506     if os.path.isdir("/usr/share/polkit-1"):
507         return 1
508     elif os.path.isdir("/usr/share/PolicyKit"):
509         return 0
510     else:
511         return None