1
2
3 __doc__ = """GNUmed web client launcher.
4 """
5
6
7
8 __version__ = "$Revision: 1 $"
9 __author__ = "S. Hilbert <Sebastian.Hilbert@gmx.net>"
10 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
11
12 import cherrypy
13 from Cheetah.Template import Template
14
15 try:
16 from json import loads, dumps
17 except ImportError:
18 from simplejson import loads, dumps
19
20
21
22
23 import sys, time, os, cPickle, zlib, locale, os.path, datetime as pyDT, shutil, logging, urllib2, re as regex
24
25
26 from Gnumed.pycommon import gmI18N, gmTools, gmDateTime, gmHooks
27 from Gnumed.pycommon import gmLoginInfo, gmPG2, gmBackendListener, gmTools, gmCfg2, gmI18N, gmDispatcher
28
29 from Gnumed.business import gmDocuments
30 from Gnumed.CherryPy import gmGuiHelpersWeb
31
32
33
34
35
36
37 _cfg = gmCfg2.gmCfgData()
38 _provider = None
39 _scripting_listener = None
40
41 _log = logging.getLogger('gm.main')
42 _log.info(__version__)
43 _log.info('web GUI framework')
44
45
46
47
48 -def connect_to_database(login_info=None, max_attempts=3, expected_version=None, require_version=True):
49 """Display the login dialog and try to log into the backend.
50
51 - up to max_attempts times
52 - returns True/False
53 """
54
55 expected_hash = gmPG2.known_schema_hashes[expected_version]
56 client_version = _cfg.get(option = u'client_version')
57 global current_db_name
58 current_db_name = u'gnumed_v%s' % expected_version
59
60 attempt = 0
61
62 while attempt < max_attempts:
63
64 _log.debug('login attempt %s of %s', (attempt+1), max_attempts)
65
66 connected = False
67
68 login = login_info
69 if login is None:
70 _log.info("did not provide a login information")
71
72
73 dsn = gmPG2.make_psycopg2_dsn (
74 database = login.database,
75 host = login.host,
76 port = login.port,
77 user = login.user,
78 password = login.password
79 )
80 try:
81 conn = gmPG2.get_raw_connection(dsn = dsn, verbose = True, readonly = True)
82 connected = True
83
84 except gmPG2.cAuthenticationError, e:
85 attempt += 1
86 _log.error(u"login attempt failed: %s", e)
87 if attempt < max_attempts:
88 if (u'host=127.0.0.1' in (u'%s' % e)) or (u'host=' not in (u'%s' % e)):
89 msg = _(
90 'Unable to connect to database:\n\n'
91 '%s\n\n'
92 "Are you sure you have got a local database installed ?\n"
93 '\n'
94 "Please retry with proper credentials or cancel.\n"
95 '\n'
96 'You may also need to check the PostgreSQL client\n'
97 'authentication configuration in pg_hba.conf. For\n'
98 'details see:\n'
99 '\n'
100 'wiki.gnumed.de/bin/view/Gnumed/ConfigurePostgreSQL'
101 )
102 else:
103 msg = _(
104 "Unable to connect to database:\n\n"
105 "%s\n\n"
106 "Please retry with proper credentials or cancel.\n"
107 "\n"
108 'You may also need to check the PostgreSQL client\n'
109 'authentication configuration in pg_hba.conf. For\n'
110 'details see:\n'
111 '\n'
112 'wiki.gnumed.de/bin/view/Gnumed/ConfigurePostgreSQL'
113 )
114 msg = msg % e
115 msg = regex.sub(r'password=[^\s]+', u'password=%s' % gmTools.u_replacement_character, msg)
116 gmGuiHelpersWeb.gm_show_error (
117 msg,
118 _('Connecting to backend')
119 )
120 del e
121 continue
122
123 except gmPG2.dbapi.OperationalError, e:
124 _log.error(u"login attempt failed: %s", e)
125 msg = _(
126 "Unable to connect to database:\n\n"
127 "%s\n\n"
128 "Please retry another backend / user / password combination !\n"
129 ) % gmPG2.extract_msg_from_pg_exception(e)
130 msg = regex.sub(r'password=[^\s]+', u'password=%s' % gmTools.u_replacement_character, msg)
131 gmGuiHelpersWeb.gm_show_error (
132 msg,
133 _('Connecting to backend')
134 )
135 del e
136 continue
137
138
139 gmPG2.set_default_login(login = login)
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 listener = gmBackendListener.gmBackendListener(conn = conn)
184 break
185
186
187
188 return connected
189
194 """Get server profiles from the configuration files.
195
196 1) from system-wide file
197 2) from user file
198
199 Profiles in the user file which have the same name
200 as a profile in the system file will override the
201 system file.
202 """
203
204 src_order = [
205 (u'explicit', u'extend'),
206 (u'system', u'extend'),
207 (u'user', u'extend'),
208 (u'workbase', u'extend')
209 ]
210
211 profile_names = gmTools.coalesce (
212 _cfg.get(group = u'backend', option = u'profiles', source_order = src_order),
213 []
214 )
215
216
217 src_order = [
218 (u'explicit', u'return'),
219 (u'workbase', u'return'),
220 (u'user', u'return'),
221 (u'system', u'return')
222 ]
223
224 profiles = {}
225
226 for profile_name in profile_names:
227
228
229 profile = cBackendProfile()
230 profile_section = 'profile %s' % profile_name
231
232 profile.name = profile_name
233 profile.host = gmTools.coalesce(_cfg.get(profile_section, u'host', src_order), u'').strip()
234 port = gmTools.coalesce(_cfg.get(profile_section, u'port', src_order), 5432)
235 try:
236 profile.port = int(port)
237 if profile.port < 1024:
238 raise ValueError('refusing to use priviledged port (< 1024)')
239 except ValueError:
240 _log.warning('invalid port definition: [%s], skipping profile [%s]', port, profile_name)
241 continue
242 profile.database = gmTools.coalesce(_cfg.get(profile_section, u'database', src_order), u'').strip()
243 if profile.database == u'':
244 _log.warning('database name not specified, skipping profile [%s]', profile_name)
245 continue
246 profile.encoding = gmTools.coalesce(_cfg.get(profile_section, u'encoding', src_order), u'UTF8')
247 profile.public_db = bool(_cfg.get(profile_section, u'public/open access', src_order))
248 profile.helpdesk = _cfg.get(profile_section, u'help desk', src_order)
249
250 label = u'%s (%s@%s)' % (profile_name, profile.database, profile.host)
251 profiles[label] = profile
252
253
254
255 if not (_cfg.get(option = 'debug') or current_db_name.endswith('_devel')):
256 profiles2remove = []
257 for label in profiles:
258 if profiles[label].database != current_db_name:
259 profiles2remove.append(label)
260 for label in profiles2remove:
261 del profiles[label]
262
263 if len(profiles) == 0:
264 host = u'publicdb.gnumed.de'
265 label = u'public GNUmed database (%s@%s)' % (current_db_name, host)
266 profiles[label] = cBackendProfile()
267 profiles[label].name = label
268 profiles[label].host = host
269 profiles[label].port = 5432
270 profiles[label].database = current_db_name
271 profiles[label].encoding = u'UTF8'
272 profiles[label].public_db = True
273 profiles[label].helpdesk = u'http://wiki.gnumed.de'
274
275 return profiles
276
277
278 -def GetLoginInfo(username=None, password=None, backend=None ):
279
280
281
282
283 """convenience function for compatibility with gmLoginInfo.LoginInfo"""
284
285
286
287
288 __backend_profiles = __get_backend_profiles()
289 profile = __backend_profiles[backend.encode('utf8').strip()]
290
291 _log.debug(u'backend profile "%s" selected', profile.name)
292 _log.debug(u' details: <%s> on %s@%s:%s (%s, %s)',
293 username,
294 profile.database,
295 profile.host,
296 profile.port,
297 profile.encoding,
298 gmTools.bool2subst(profile.public_db, u'public', u'private')
299 )
300
301 login = gmLoginInfo.LoginInfo (
302 user = username,
303 password = password,
304 host = profile.host,
305 database = profile.database,
306 port = profile.port
307 )
308
309
310 return login
311
314 try:
315 kwargs['originated_in_database']
316 print '==> got notification from database "%s":' % kwargs['signal']
317 except KeyError:
318 print '==> received signal from client: "%s"' % kwargs['signal']
319
320 del kwargs['signal']
321 for key in kwargs.keys():
322 print ' [%s]: %s' % (key, kwargs[key])
323
327
331 print "before_handler jsonrpc"
332
333 req = cherrypy.request
334 try:
335 size = int(req.headers["Content-Length"])
336 except:
337 size = 1
338 try:
339 json_string = req.body.read()
340 print "json_string [%s]" % json_string
341 obj = loads(json_string)
342 myparams = {}
343 for key, val in obj.items():
344 mykey = str(key)
345 myparams[mykey] = val
346 req.params = myparams
347 except:
348 pass
349 cherrypy.tools.jsonrpchdl = cherrypy.Tool('before_handler',jsonrpchdl)
350
351 PYJSDIR = sys._getframe().f_code.co_filename
352 PYJSDIR = os.path.split(os.path.dirname(PYJSDIR))[0]
353 PYJSDIR = os.path.join(PYJSDIR, 'pyjamas')
354
355 DEFAULT_BACKEND = "GNUmed database on this machine (Linux/Mac) (gnumed_v19@)"
358
359 @cherrypy.expose
361 fname = os.path.join(PYJSDIR,args[0])
362 print "try to return contents of file %s" % fname
363 f = file(fname)
364 s = f.read()
365 return s
366
367 @cherrypy.expose
368 @cherrypy.tools.jsonrpchdl()
370 print "echo service"
371 print args
372 print kwargs
373 method = kwargs['method']
374 f = getattr(self,method)
375 res = f(*kwargs['params'])
376 return dumps({'id':kwargs['id'],'result':res,'error':None})
377 - def echo(self, text):
385
388
394
402
403 - def login(self, username=None, password=None, backend=None):
417
418 - def doLogin(self, username=None, password=None, backend=None):
434
435 doLogin.exposed = True
436
437
439
440
441 t = Template( file="CherryPy/templates/index.tmpl"
442
443 , searchList = {
444 "title" : "Welcome to GNUmed - Login"
445 , "cssFiles" : ["css/ext-all.css", "css/xtheme-gray.css"]
446 , "jsFiles" : ["ext/ext-base.js", "ext/ext-core.js"]
447 , "backend" : "GNUmed database on this machine (Linux/Mac) (gnumed_v19@)"
448 }
449 )
450 return str( t )
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468 index.exposed = True
469
470
471
472
473
474 -def main():
475
476 if _cfg.get(option = 'debug'):
477 gmDispatcher.connect(receiver = _signal_debugging_monitor)
478 _log.debug('gmDispatcher signal monitor activated')
479
480 cherrypy.quickstart(gmApp(), "/",
481 {'global':{'server.socket_port':8080,'log.screen':True}})
482