merge with master
[platform/upstream/hplip.git] / plugin.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 #
20 # Author: Don Welch
21 #
22
23 __version__ = '2.1'
24 __mod__ = 'hp-plugin'
25 __title__ = 'Plugin Download and Install Utility'
26 __doc__ = ""
27
28 # Std Lib
29 import sys
30 import getopt
31 import time
32 import os.path
33 import re
34 import os
35 import gzip
36
37 # Local
38 from base.g import *
39 from base import device, utils, tui, module
40 from prnt import cups
41
42 pm = None
43
44 def plugin_download_callback(c, s, t):
45     pm.update(int(100*c*s/t),
46              utils.format_bytes(c*s))
47
48
49 def plugin_install_callback(s):
50     print s
51
52 def clean_exit(code=0):
53     mod.unlockInstance()
54     sys.exit(code)
55
56 USAGE = [ (__doc__, "", "name", True),
57           ("Usage: %s [MODE] [OPTIONS]" % __mod__, "", "summary", True),
58           utils.USAGE_MODE,
59           utils.USAGE_GUI_MODE,
60           utils.USAGE_INTERACTIVE_MODE,
61           ("Installation for required printer mode:", "--required (Qt4 only)", "option", False),
62           ("Installation for optional printer mode:", "--optional (Qt4 only)", "option", False),
63           #("Installation generic mode:", "--generic (default)", "option", False),
64           utils.USAGE_LANGUAGE,
65           utils.USAGE_OPTIONS,
66           ("Specify the path to the plugin file:", "-p <path> or --path=<path> or --plugin=<path>", "option", False),
67           utils.USAGE_LOGGING1, utils.USAGE_LOGGING2, utils.USAGE_LOGGING3,
68           utils.USAGE_HELP,
69           utils.USAGE_SPACE,
70           utils.USAGE_SEEALSO,
71           ("hp-setup", "", "seealso", False),
72           ("hp-firmware", "", "seealso", False),
73         ]
74
75
76 mod = module.Module(__mod__, __title__, __version__, __doc__, USAGE,
77                     (INTERACTIVE_MODE, GUI_MODE),
78                     (UI_TOOLKIT_QT3, UI_TOOLKIT_QT4), True)
79
80 opts, device_uri, printer_name, mode, ui_toolkit, loc = \
81     mod.parseStdOpts('p:', ['path=', 'plugin=', 'plug-in=', 'reason=',
82                             'generic', 'optional', 'required'],
83                      handle_device_printer=False)
84
85 plugin_path = None
86 install_mode = PLUGIN_NONE # reuse plugin types for mode (PLUGIN_NONE = generic)
87 plugin_reason = PLUGIN_REASON_NONE
88
89 for o, a in opts:
90     if o in ('-p', '--path', '--plugin', '--plug-in'):
91         plugin_path = os.path.normpath(os.path.abspath(os.path.expanduser(a)))
92
93     elif o == '--required':
94         install_mode = PLUGIN_REQUIRED
95         if ui_toolkit == 'qt3':
96             log.warn("--required switch ignored.")
97
98     elif o == '--optional':
99         install_mode = PLUGIN_OPTIONAL
100         if ui_toolkit == 'qt3':
101             log.warn("--optional switch ignored.")
102
103     elif o == '--reason':
104         plugin_reason = int(a)
105
106
107 version = prop.installed_version
108 plugin_filename = 'hplip-%s-plugin.run' % version
109
110 ok= mod.lockInstance()
111 if ok is False:
112     log.error("Plug-in lock acquire failed. check if hp-plugin is already running")
113     sys.exit(1)
114
115 if plugin_path is not None:
116     if not os.path.exists(plugin_path):
117         log.error("Plug-in path '%s' not found." % plugin_path)
118         clean_exit(1)
119
120     if os.path.isdir(plugin_path):
121         plugin_path = os.path.join(plugin_path, 'hplip-%s-plugin.run' % version)
122
123         if not os.path.exists(plugin_path):
124             log.error("Plug-in path '%s' not found." % plugin_path)
125             clean_exit(1)
126
127     if os.path.basename(plugin_path) != plugin_filename:
128         log.error("Plug-in filename must be '%s'." % plugin_filename)
129         clean_exit(1)
130
131
132     size, checksum, timestamp = os.stat(plugin_path)[6], '', 0.0
133     plugin_path = 'file://' + plugin_path
134     log.debug("Plugin path=%s (%d)" % (plugin_path, size))
135
136
137 if mode == GUI_MODE:
138     if ui_toolkit == 'qt3':
139         if not utils.canEnterGUIMode():
140             log.error("%s requires GUI support (try running with --qt4). Try using interactive (-i) mode." % __mod__)
141             clean_exit(1)
142     else:
143         if not utils.canEnterGUIMode4():
144             log.error("%s requires GUI support (try running with --qt3). Try using interactive (-i) mode." % __mod__)
145             clean_exit(1)
146
147
148 PKIT = utils.to_bool(sys_conf.get('configure', 'policy-kit'))
149 if PKIT:
150     try:
151         from base.pkit import *
152         try:
153             pkit = PolicyKit()
154             pkit_installed = True
155         except dbus.DBusException, ex:
156             log.error("PolicyKit support requires DBUS or PolicyKit support files missing")
157             pkit_installed = False
158     except:
159         log.error("Unable to load pkit...is HPLIP installed?")
160         pkit_installed = False
161 else:
162     pkit_installed = False
163
164
165 if mode == GUI_MODE:
166     if ui_toolkit == 'qt3':
167         try:
168             from qt import *
169             from ui import pluginform2
170         except ImportError:
171             log.error("Unable to load Qt3 support. Is it installed?")
172             clean_exit(1)
173
174         app = QApplication(sys.argv)
175         QObject.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()"))
176
177         if loc is None:
178             loc = user_conf.get('ui', 'loc', 'system')
179             if loc.lower() == 'system':
180                 loc = str(QTextCodec.locale())
181                 log.debug("Using system locale: %s" % loc)
182
183         if loc.lower() != 'c':
184             e = 'utf8'
185             try:
186                 l, x = loc.split('.')
187                 loc = '.'.join([l, e])
188             except ValueError:
189                 l = loc
190                 loc = '.'.join([loc, e])
191
192             log.debug("Trying to load .qm file for %s locale." % loc)
193             trans = QTranslator(None)
194
195             qm_file = 'hplip_%s.qm' % l
196             log.debug("Name of .qm file: %s" % qm_file)
197             loaded = trans.load(qm_file, prop.localization_dir)
198
199             if loaded:
200                 app.installTranslator(trans)
201             else:
202                 loc = 'c'
203
204         if loc == 'c':
205             log.debug("Using default 'C' locale")
206         else:
207             log.debug("Using locale: %s" % loc)
208             QLocale.setDefault(QLocale(loc))
209             prop.locale = loc
210             try:
211                 locale.setlocale(locale.LC_ALL, locale.normalize(loc))
212             except locale.Error:
213                 pass
214
215         if not pkit_installed and not os.geteuid() == 0:
216             log.error("You must be root to run this utility.")
217
218             QMessageBox.critical(None,
219                                  "HP Device Manager - Plug-in Installer",
220                                  "You must be root to run hp-plugin.",
221                                   QMessageBox.Ok,
222                                   QMessageBox.NoButton,
223                                   QMessageBox.NoButton)
224
225             clean_exit(1)
226
227         w = pluginform2.PluginForm2()
228         app.setMainWidget(w)
229         w.show()
230
231         app.exec_loop()
232
233     else: # qt4
234         try:
235             from PyQt4.QtGui import QApplication, QMessageBox
236             from ui4.plugindialog import PluginDialog
237         except ImportError:
238             log.error("Unable to load Qt4 support. Is it installed?")
239             clean_exit(1)
240
241         app = QApplication(sys.argv)
242
243         if not pkit_installed and not os.geteuid() == 0:
244             log.error("You must be root to run this utility.")
245
246             QMessageBox.critical(None,
247                                  "HP Device Manager - Plug-in Installer",
248                                  "You must be root to run hp-plugin.",
249                                   QMessageBox.Ok,
250                                   QMessageBox.NoButton,
251                                   QMessageBox.NoButton)
252
253             clean_exit(1)
254
255
256         dialog = PluginDialog(None, install_mode, plugin_reason)
257         dialog.show()
258         try:
259             log.debug("Starting GUI loop...")
260             app.exec_()
261         except KeyboardInterrupt:
262             log.error("User exit")
263             clean_exit(0)
264
265
266 else: # INTERACTIVE_MODE
267     try:
268         if not os.geteuid() == 0:
269             log.error("You must be root to run this utility.")
270             clean_exit(1)
271
272         log.info("(Note: Defaults for each question are maked with a '*'. Press <enter> to accept the default.)")
273         log.info("")
274
275         from installer import core_install
276         core = core_install.CoreInstall()
277
278         core.set_plugin_version()
279
280         tui.header("PLUG-IN INSTALLATION FOR HPLIP %s" % version)
281
282         if core.check_for_plugin() == PLUGIN_INSTALLED and plugin_path is None:
283             log.info("The driver plugin for HPLIP %s appears to already be installed." % version)
284
285             cont, ans = tui.enter_yes_no("Do you wish to download and re-install the plug-in?")
286
287             if not cont or not ans:
288                 clean_exit(0)
289
290
291         if plugin_path is None:
292             table = tui.Formatter(header=('Option', 'Description'), min_widths=(10, 50))
293             table.add(('d', 'Download plug-in from HP (recomended)'))
294             table.add(('p', 'Specify a path to the plug-in (advanced)'))
295             table.add(('q', 'Quit hp-plugin (skip installation)'))
296
297             table.output()
298
299             cont, ans = tui.enter_choice("\nEnter option (d=download*, p=specify path, q=quit) ? ",
300                 ['d', 'p'], 'd')
301
302             if not cont: # q
303                 clean_exit(0)
304
305
306             if ans == 'd': # d - download
307                 # read plugin.conf (local or on sf.net) to get plugin_path (http://)
308                 plugin_conf_url = core.get_plugin_conf_url()
309
310                 if plugin_conf_url.startswith('file://'):
311                     tui.header("COPY CONFIGURATION")
312                 else:
313                     tui.header("DOWNLOAD CONFIGURATION")
314
315                     log.info("Checking for network connection...")
316                     ok = core.check_network_connection()
317
318                     if not ok:
319                         log.error("Network connection not detected.")
320                         clean_exit(1)
321
322
323                 log.info("Downloading configuration file from: %s" % plugin_conf_url)
324                 pm = tui.ProgressMeter("Downloading configuration:")
325
326                 plugin_path, size, checksum, timestamp, ok = core.get_plugin_info(plugin_conf_url,
327                     plugin_download_callback)
328
329                 print
330
331                 if not plugin_path.startswith('http://') and not plugin_path.startswith('file://'):
332                     plugin_path = 'file://' + plugin_path
333
334             else: # p - specify plugin path
335
336                 while True:
337                     plugin_path = raw_input(log.bold("Enter the path to the 'hplip-%s-plugin.run' file (q=quit) : " %
338                         version)).strip()
339
340                     if plugin_path.strip().lower() == 'q':
341                         clean_exit(1)
342
343                     if not plugin_path.startswith('http://'):
344                         plugin_path = os.path.normpath(os.path.abspath(os.path.expanduser(plugin_path)))
345
346                         if not os.path.exists(plugin_path):
347                             log.error("Plug-in path '%s' not found." % plugin_path)
348                             continue
349
350                         if os.path.isdir(plugin_path):
351                             plugin_path = os.path.join(plugin_path, plugin_filename)
352
353                             if not os.path.exists(plugin_path):
354                                 log.error("Plug-in path '%s' not found." % plugin_path)
355                                 continue
356
357                         if os.path.basename(plugin_path) != plugin_filename:
358                             log.error("Plug-in filename must be '%s'." % plugin_filename)
359                             continue
360
361                         size, checksum, timestamp = os.stat(plugin_path)[6], '', 0.0
362                         plugin_path = 'file://' + plugin_path
363
364                     break
365
366
367         if plugin_path.startswith('file://'):
368             tui.header("COPY PLUGIN")
369         else:
370             tui.header("DOWNLOAD PLUGIN")
371
372             log.info("Checking for network connection...")
373             ok = core.check_network_connection()
374
375             if not ok:
376                 log.error("Network connection not detected.")
377                 clean_exit(1)
378
379         log.info("Downloading plug-in from: %s" % plugin_path)
380         pm = tui.ProgressMeter("Downloading plug-in:")
381
382         status, ret = core.download_plugin(plugin_path, size, checksum, timestamp, plugin_download_callback)
383         print
384
385         if status in (core_install.PLUGIN_INSTALL_ERROR_UNABLE_TO_RECV_KEYS, core_install.PLUGIN_INSTALL_ERROR_DIGITAL_SIG_NOT_FOUND):
386             log.error("Digital signature file download failed. Without this file, it is not possible to authenticate and validate the plug-in prior to installation.")
387             cont, ans = tui.enter_yes_no("Do you still want to install the plug-in?", 'n')
388
389             if not cont or not ans:
390                 clean_exit(0)
391
392         elif status != core_install.PLUGIN_INSTALL_ERROR_NONE:
393
394             if status == core_install.PLUGIN_INSTALL_ERROR_PLUGIN_FILE_NOT_FOUND:
395                 desc = "Plug-in file not found (server returned 404 or similar error). Error code: %s" % str(ret)
396
397             elif status == core_install.PLUGIN_INSTALL_ERROR_DIGITAL_SIG_BAD:
398                 desc = "Plug-in file does not match its digital signature. File may have been corrupted or altered. Error code: %s" % str(ret)
399
400             elif status == core_install.PLUGIN_INSTALL_ERROR_PLUGIN_FILE_CHECKSUM_ERROR:
401                 desc = "Plug-in file does not match its checksum. File may have been corrupted or altered."
402
403             elif status == core_install.PLUGIN_INSTALL_ERROR_NO_NETWORK:
404                 desc = "Unable to connect to network to download the plug-in. Please check your network connection and try again."
405
406             elif status == core_install.PLUGIN_INSTALL_ERROR_DIRECTORY_ERROR:
407                 desc = "Unable to create the plug-in directory. Please check your permissions and try again."
408
409             core.delete_plugin()
410             log.error(desc)
411             clean_exit(1)
412
413
414         tui.header("INSTALLING PLUG-IN")
415
416         core.run_plugin(mode, plugin_install_callback)
417
418         cups_devices = device.getSupportedCUPSDevices(['hp']) #, 'hpfax'])
419         #print cups_devices
420
421         title = False
422
423         for dev in cups_devices:
424             mq = device.queryModelByURI(dev)
425
426             if mq.get('fw-download', 0):
427
428                 if not title:
429                     tui.header("DOWNLOADING FIRMWARE")
430                     title = True
431
432                 # Download firmware if needed
433                 log.info(log.bold("\nDownloading firmware to device %s..." % dev))
434                 try:
435                     d = device.Device(dev)
436                 except Error:
437                     log.error("Error opening device. Exiting.")
438                     clean_exit(1)
439
440                 if d.downloadFirmware():
441                     log.info("Firmware download successful.\n")
442
443                 d.close()
444
445
446     except KeyboardInterrupt:
447         log.error("User exit")
448
449 log.info("")
450 log.info("Done.")
451 clean_exit(0)
452