Tizen 2.1 base
[platform/upstream/hplip.git] / base / g.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: Don Welch
20 #
21 # NOTE: This module is safe for 'from g import *'
22 #
23
24 # Std Lib
25 import sys
26 import os
27 import os.path
28 import ConfigParser
29 import locale
30 import pwd
31 import stat
32 import re
33
34 # Local
35 from codes import *
36 import logger
37
38 # System wide logger
39 log = logger.Logger('', logger.Logger.LOG_LEVEL_INFO, logger.Logger.LOG_TO_CONSOLE)
40 log.set_level('info')
41
42
43 MINIMUM_PYQT_MAJOR_VER = 3
44 MINIMUM_PYQT_MINOR_VER = 14
45 MINIMUM_QT_MAJOR_VER = 3
46 MINIMUM_QT_MINOR_VER = 0
47
48
49 def to_bool(s, default=False):
50     if isinstance(s, str) and s:
51         if s[0].lower() in ['1', 't', 'y']:
52             return True
53         elif s[0].lower() in ['0', 'f', 'n']:
54             return False
55     elif isinstance(s, bool):
56         return s
57
58     return default
59
60
61 # System wide properties
62 class Properties(dict):
63
64     def __getattr__(self, attr):
65         if attr in self.keys():
66             return self.__getitem__(attr)
67         else:
68             return ""
69
70     def __setattr__(self, attr, val):
71         self.__setitem__(attr, val)
72
73 prop = Properties()
74
75
76
77 class ConfigBase(object):
78     def __init__(self, filename):
79         self.filename = filename
80         self.conf = ConfigParser.ConfigParser()
81         self.read()
82
83
84     def get(self, section, key, default=u''):
85         try:
86             return self.conf.get(section, key)
87         except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
88             return default
89
90
91     def set(self, section, key, value):
92         if not self.conf.has_section(section):
93             self.conf.add_section(section)
94
95         self.conf.set(section, key, value)
96         self.write()
97
98
99     def sections(self):
100         return self.conf.sections()
101
102
103     def has_section(self, section):
104         return self.conf.has_section(section)
105
106
107     def options(self, section):
108         return self.conf.options(section)
109
110     keys = options
111
112     def read(self):
113         if self.filename is not None:
114             filename = self.filename
115             if filename.startswith("/root/"):
116                 # Don't try opening a file in root's home directory.
117                 log.error("attempted to read from '%s'" % self.filename)
118                 return
119             try:
120                 fp = open(self.filename, "r")
121                 self.conf.readfp(fp)
122                 fp.close()
123             except (OSError, IOError):
124                 log.debug("Unable to open file %s for reading." % self.filename)
125
126     def write(self):
127         if self.filename is not None:
128             filename = self.filename
129             if filename.startswith("/root/") or filename.startswith("/etc/"):
130                 # Don't try writing a file in root's home directory or
131                 # the system-wide config file.
132                 # See bug #479178.
133                 log.error("attempted to write to '%s'" % self.filename)
134                 return
135
136             try:
137                 fp = open(self.filename, "w")
138                 self.conf.write(fp)
139                 fp.close()
140             except (OSError, IOError):
141                 log.debug("Unable to open file %s for writing." % self.filename)
142
143
144
145 class SysConfig(ConfigBase):
146     def __init__(self):
147         ConfigBase.__init__(self, '/etc/hp/hplip.conf')
148
149
150 class State(ConfigBase):
151     def __init__(self):
152         ConfigBase.__init__(self, '/var/lib/hp/hplip.state')
153
154
155 class UserConfig(ConfigBase):
156     def __init__(self):
157         if not os.geteuid() == 0:
158             prop.user_dir = os.path.expanduser('~/.hplip')
159
160             try:
161                 if not os.path.exists(prop.user_dir):
162                     os.makedirs(prop.user_dir)
163             except OSError:
164                 pass # This is sometimes OK, if running hpfax: for example
165
166             prop.user_config_file = os.path.join(prop.user_dir, 'hplip.conf')
167
168             if not os.path.exists(prop.user_config_file):
169                 try:
170                     file(prop.user_config_file, 'w').close()
171                     s = os.stat(os.path.dirname(prop.user_config_file))
172                     os.chown(prop.user_config_file, s[stat.ST_UID], s[stat.ST_GID])
173                 except IOError:
174                     pass
175
176             ConfigBase.__init__(self, prop.user_config_file)
177
178         else:
179             # If running as root, conf file is None
180             prop.user_dir = None
181             prop.user_config_file = None
182             ConfigBase.__init__(self, None)
183
184
185     def workingDirectory(self):
186         t = self.get('last_used', 'working_dir', os.path.expanduser("~"))
187         try:
188             t = t.decode('utf-8')
189         except UnicodeError:
190             log.error("Invalid unicode: %s"  % t)
191         log.debug("working directory: %s" % t)
192         return t
193
194
195     def setWorkingDirectory(self, t):
196         self.set('last_used', 'working_dir', t.encode('utf-8'))
197         log.debug("working directory: %s" % t.encode('utf-8'))
198
199
200
201 os.umask(0037)
202
203 # System Config File: Directories and build settings. Not altered after installation.
204 sys_conf = SysConfig()
205
206 # System State File: System-wide runtime settings
207 sys_state = State()
208
209 # Per-user Settings File: (Note: For Qt4 code, limit the use of this to non-GUI apps. only)
210 user_conf = UserConfig()
211
212
213 # Language settings
214 try:
215     prop.locale, prop.encoding = locale.getdefaultlocale()
216 except ValueError:
217     prop.locale = 'en_US'
218     prop.encoding = 'UTF8'
219
220 prop.version = sys_conf.get('hplip', 'version', '0.0.0') # e.g., 3.9.2b.10
221 _p, _x = re.compile(r'(\d\w*)', re.I), []
222 for _y in prop.version.split('.')[:3]:
223     _z = _p.match(_y)
224     if _z is not None:
225         _x.append(_z.group(1))
226
227 prop.installed_version = '.'.join(_x) # e.g., '3.9.2'
228 try:
229     prop.installed_version_int = int(''.join(['%02x' % int(_y) for _y in _x]), 16) # e.g., 0x030902 -> 198914
230 except ValueError:
231     prop.installed_version_int = 0
232
233 prop.home_dir = sys_conf.get('dirs', 'home', os.path.realpath(os.path.normpath(os.getcwd())))
234 prop.username = pwd.getpwuid(os.getuid())[0]
235 pdb = pwd.getpwnam(prop.username)
236 prop.userhome = pdb[5]
237
238 prop.history_size = 50
239
240 prop.data_dir = os.path.join(prop.home_dir, 'data')
241 prop.image_dir = os.path.join(prop.home_dir, 'data', 'images')
242 prop.xml_dir = os.path.join(prop.home_dir, 'data', 'xml')
243 prop.models_dir = os.path.join(prop.home_dir, 'data', 'models')
244 prop.localization_dir = os.path.join(prop.home_dir, 'data', 'localization')
245
246 prop.max_message_len = 8192
247 prop.max_message_read = 65536
248 prop.read_timeout = 90
249
250 prop.ppd_search_path = '/usr/share;/usr/local/share;/usr/lib;/usr/local/lib;/usr/libexec;/opt;/usr/lib64'
251 prop.ppd_search_pattern = 'HP-*.ppd.*'
252 prop.ppd_download_url = 'http://www.linuxprinting.org/ppd-o-matic.cgi'
253 prop.ppd_file_suffix = '-hpijs.ppd'
254
255 # Build and install configurations
256 prop.gui_build = to_bool(sys_conf.get('configure', 'gui-build', '0'))
257 prop.net_build = to_bool(sys_conf.get('configure', 'network-build', '0'))
258 prop.par_build = to_bool(sys_conf.get('configure', 'pp-build', '0'))
259 prop.usb_build = True
260 prop.scan_build = to_bool(sys_conf.get('configure', 'scanner-build', '0'))
261 prop.fax_build = to_bool(sys_conf.get('configure', 'fax-build', '0'))
262 prop.doc_build = to_bool(sys_conf.get('configure', 'doc-build', '0'))
263 prop.foomatic_xml_install = to_bool(sys_conf.get('configure', 'foomatic-xml-install', '0'))
264 prop.foomatic_ppd_install = to_bool(sys_conf.get('configure', 'foomatic-ppd-install', '0'))
265 prop.hpcups_build = to_bool(sys_conf.get('configure', 'hpcups-install', '0'))
266 prop.hpijs_build = to_bool(sys_conf.get('configure', 'hpijs-install', '0'))
267
268 # Spinner, ala Gentoo Portage
269 spinner = "\|/-\|/-"
270 spinpos = 0
271 enable_spinner = True
272
273 def change_spinner_state(enable =True):
274     global enable_spinner
275     enable_spinner = enable
276
277 def update_spinner():
278     global spinner, spinpos, enable_spinner
279     if enable_spinner and not log.is_debug() and sys.stdout.isatty():
280         sys.stdout.write("\b" + spinner[spinpos])
281         spinpos=(spinpos + 1) % 8
282         sys.stdout.flush()
283
284 def cleanup_spinner():
285     global enable_spinner
286     if enable_spinner and not log.is_debug() and sys.stdout.isatty():
287         sys.stdout.write("\b \b")
288         sys.stdout.flush()
289
290
291 # Internal/messaging errors
292
293 ERROR_STRINGS = {
294                 ERROR_SUCCESS : 'No error',
295                 ERROR_UNKNOWN_ERROR : 'Unknown error',
296                 ERROR_DEVICE_NOT_FOUND : 'Device not found',
297                 ERROR_INVALID_DEVICE_ID : 'Unknown/invalid device-id field',
298                 ERROR_INVALID_DEVICE_URI : 'Unknown/invalid device-uri field',
299                 ERROR_DATA_LENGTH_EXCEEDS_MAX : 'Data length exceeds maximum',
300                 ERROR_DEVICE_IO_ERROR : 'Device I/O error',
301                 ERROR_NO_PROBED_DEVICES_FOUND : 'No probed devices found',
302                 ERROR_DEVICE_BUSY : 'Device busy',
303                 ERROR_DEVICE_STATUS_NOT_AVAILABLE : 'DeviceStatus not available',
304                 ERROR_INVALID_SERVICE_NAME : 'Invalid service name',
305                 ERROR_ERROR_INVALID_CHANNEL_ID : 'Invalid channel-id (service name)',
306                 ERROR_CHANNEL_BUSY : 'Channel busy',
307                 ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION : 'Device does not support operation',
308                 ERROR_DEVICEOPEN_FAILED : 'Device open failed',
309                 ERROR_INVALID_DEVNODE : 'Invalid device node',
310                 ERROR_INVALID_HOSTNAME : "Invalid hostname ip address",
311                 ERROR_INVALID_PORT_NUMBER : "Invalid JetDirect port number",
312                 ERROR_NO_CUPS_QUEUE_FOUND_FOR_DEVICE : "No CUPS queue found for device.",
313                 ERROR_DATFILE_ERROR: "DAT file error",
314                 ERROR_INVALID_TIMEOUT: "Invalid timeout",
315                 ERROR_IO_TIMEOUT: "I/O timeout",
316                 ERROR_FAX_INCOMPATIBLE_OPTIONS: "Incompatible fax options",
317                 ERROR_FAX_INVALID_FAX_FILE: "Invalid fax file",
318                 ERROR_FAX_FILE_NOT_FOUND: "Fax file not found",
319                 ERROR_INTERNAL : 'Unknown internal error',
320                }
321
322
323 class Error(Exception):
324     def __init__(self, opt=ERROR_INTERNAL):
325         self.opt = opt
326         self.msg = ERROR_STRINGS.get(opt, ERROR_STRINGS[ERROR_INTERNAL])
327         log.debug("Exception: %d (%s)" % (opt, self.msg))
328         Exception.__init__(self, self.msg, opt)
329
330
331 # Make sure True and False are avail. in pre-2.2 versions
332 try:
333     True
334 except NameError:
335     True = (1==1)
336     False = not True
337
338 # as new translations are completed, add them here
339 supported_locales =  { 'en_US': ('us', 'en', 'en_us', 'american', 'america', 'usa', 'english'),}
340 # Localization support was disabled in 3.9.2
341                        #'zh_CN': ('zh', 'cn', 'zh_cn' , 'china', 'chinese', 'prc'),
342                        #'de_DE': ('de', 'de_de', 'german', 'deutsche'),
343                        #'fr_FR': ('fr', 'fr_fr', 'france', 'french', 'français'),
344                        #'it_IT': ('it', 'it_it', 'italy', 'italian', 'italiano'),
345                        #'ru_RU': ('ru', 'ru_ru', 'russian'),
346                        #'pt_BR': ('pt', 'br', 'pt_br', 'brazil', 'brazilian', 'portuguese', 'brasil', 'portuguesa'),
347                        #'es_MX': ('es', 'mx', 'es_mx', 'mexico', 'spain', 'spanish', 'espanol', 'español'),
348                      #}
349
350