1 """
2 Basic OS interface/utility functions that depend on our configuration.
3
4 (everything that doesn't need getConfig is somewhere in gavo.utils)
5 """
6
7
8
9
10
11
12
13 import grp
14 import inspect
15 import re
16 import os
17 import subprocess
18 import time
19 import urllib
20 import urlparse
21 from email import charset
22 from email import utils as emailutils
23 from email.header import Header
24 from email.parser import Parser
25 from email.mime.nonmultipart import MIMENonMultipart
26
27
28 import pkg_resources
29
30 from gavo.base import config
31 from gavo import utils
35 gavoGroup = config.get("group")
36 try:
37 return grp.getgrnam(gavoGroup)[2]
38 except KeyError as ex:
39 raise utils.ReportableError("Group %s does not exist"%str(ex),
40 hint="You should have created this (unix) group when you"
41 " created the server user (usually, 'gavo'). Just do it"
42 " now and re-run this program.")
43
46 """creates a directory with group ownership [general]group.
47
48 There's much that can to wrong; we try to raise useful error messages.
49 """
50 if not os.path.isdir(path):
51 try:
52 os.makedirs(path)
53 except os.error as err:
54 raise utils.ReportableError(
55 "Could not create directory %s"%path,
56 hint="The operating system reported: %s"%err)
57 except Exception as msg:
58 raise utils.ReportableError(
59 "Could not create directory %s (%s)"%(path, msg))
60
61 gavoGroup = getGroupId()
62 stats = os.stat(path)
63 if stats.st_mode&0060!=060 or stats.st_gid!=gavoGroup:
64 try:
65 os.chown(path, -1, gavoGroup)
66 if writable:
67 os.chmod(path, stats.st_mode | 0060)
68 except Exception as msg:
69 raise utils.ReportableError(
70 "Cannot set %s to group ownership %s, group writable"%(
71 path, gavoGroup),
72 hint="Certain directories must be writable by multiple user ids."
73 " They must therefore belong to the group %s and be group"
74 " writeable. The attempt to make sure that's so just failed"
75 " with the error message %s."
76 " Either grant the directory in question to yourself, or"
77 " fix permissions manually. If you own the directory and"
78 " sill see permission errors, try 'newgrp %s'"%(
79 config.get("group"), msg, config.get("group")))
80
84 """returns the server's base URL for the http protocol.
85
86 This is just serverURL from the configuration, unless serverURL is https;
87 in that case, we replace https with http. serverPort is ignored here
88 under the assumption that there's a reverse proxy. If that bites you,
89 we could introduce an alternativeServerURL config item.
90 """
91 serverURL = config.get("web", "serverurl")
92 if serverURL.startswith("https:"):
93 return "http:"+serverURL[6:]
94 else:
95 return serverURL
96
100 """return the server's base URL for the https protocol.
101
102 If serverURL already is https, that's what's returned. If not, the URL is
103 parsed, any port specification is removed (i.e., we only support https on
104 port 443), the protocol is changed to https, and the result is returned.
105 """
106 serverURL = config.get("web", "serverurl")
107 if serverURL.startswith("https:"):
108 return serverURL
109 else:
110 parts = urlparse.urlparse(serverURL)
111 return urlparse.urlunparse(("https", parts.hostname, parts.path,
112 parts.params, parts.query, parts.fragment))
113
116 """tries to make an https URL from an http one and vice versa.
117
118 This function will raise a ValueError if url doesn't start with either
119 HTTPBase or HTTPSBase. Otherwise, it will replace one by the other.
120 """
121 httpBase = getHTTPBase()
122 httpsBase = getHTTPSBase()
123
124 if url.startswith(httpBase):
125 return httpsBase+url[len(httpBase):]
126 elif url.startswith(httpsBase):
127 return httpBase+url[len(httpsBase):]
128 else:
129 raise ValueError("Cannot switch protocol on a URL not configured"
130 " in [web]serverURL")
131
134 """returns the server URL pertinent for the current request.
135
136 This looks upstack for a renderer object having a HANDLING_HTTPS attribute.
137 If it finds one, it will return HTTPBase() or HTTPSBase() as appropriate.
138 If not, it will return [web]serverurl
139 """
140 frame = inspect.currentframe().f_back.f_back
141 while frame:
142 if "self" in frame.f_locals:
143 if hasattr(frame.f_locals["self"], "HANDLING_HTTPS"):
144 if frame.f_locals["self"].HANDLING_HTTPS:
145 return getHTTPSBase()
146 else:
147 return getHTTPBase()
148 break
149 frame = frame.f_back
150
151 return config.get("web", "serverurl")
152
156 """returns a rooted local part for a server-internal URL.
157
158 uri itself needs to be server-absolute; a leading slash is recommended
159 for clarity but not mandatory.
160 """
161 return str(config.get("web", "nevowRoot")+path.lstrip("/"))
162
166 """returns a fully qualified URL for a rooted local part.
167
168 This will reflect the http/https access mode unless you pass
169 canonical=True, in which case [web]serverURL will be used unconditionally.
170 """
171 if canonical:
172 serverURL = config.get("web", "serverurl")
173 else:
174 serverURL = getCurrentServerURL()
175 return str(serverURL+makeSitePath(path))
176
179 """returns the name of a binary it thinks is appropriate for the platform.
180
181 To do this, it asks config for the platform name, sees if there's a binary
182 <bin>-<platname> if platform is nonempty. If it exists, it returns that name,
183 in all other cases, it returns baseName unchanged.
184 """
185 platform = config.get("platform")
186 if platform:
187 platName = baseName+"-"+platform
188 if os.path.exists(platName):
189 return platName
190 return baseName
191
194 """returns a path for a "dist resource", i.e., a file distributed
195 with DaCHS.
196
197 name is the file relative to resources.
198
199 This is essentially pkg_resources.resource_filename with a dash
200 of built-in configuration.
201 """
202 return pkg_resources.resource_filename('gavo', "resources/"+name)
203
206 """returns an open file for a "dist resource", i.e., a file distributed
207 with DaCHS.
208
209 see getPathForDistFile
210 """
211 return open(getPathForDistFile(name))
212
215 """returns (as a string) the DaCHS version running.
216
217 The information is obtained from setuptools.
218 """
219 return pkg_resources.require("gavodachs")[0].version
220
247
250 """sends mailText (which has to have all the headers) via sendmail.
251
252 (which is configured in [general]sendmail).
253
254 This will return True when sendmail has accepted the mail, False
255 otherwise.
256 """
257 if not config.get("sendmail"):
258 utils.setUIEvent("Warning", "Wanted to send maintainer mail but"
259 " could not since [general]sendmail is not configured.")
260
261 mailText = formatMail(mailText)
262
263 pipe = subprocess.Popen(config.get("sendmail"), shell=True,
264 stdin=subprocess.PIPE)
265 pipe.stdin.write(mailText)
266 pipe.stdin.close()
267
268 if pipe.wait():
269 utils.sendUIEvent("Error", "Wanted to send mail starting with"
270 " '%s', but sendmail returned an error message"
271 " (check the [general]sendmail setting)."%
272 utils.makeEllipsis(mailText, 300))
273 return False
274
275 return True
276
279 """tries to reload the rdId on a running service
280
281 This only works if there's [web]adminpasswd and[web]serverURL
282 set, and both match what the actual server uses.
283 """
284 pw = config.get("web", "adminpasswd")
285
286 if pw=="" or pw=="this_is_the_unittest_suite":
287 return
288
289 try:
290 f = utils.urlopenRemote(makeAbsoluteURL("/seffe/%s"%rdId),
291 urllib.urlencode({"__nevow_form__": "adminOps", "submit": "Reload RD"}),
292 creds=("gavoadmin", pw))
293 f.read()
294 except IOError as ex:
295 utils.sendUIEvent("Debug", "Could not reload %s RD (%s). This means"
296 " that the server may still use stale metadata. You may want"
297 " to reload %s manually (or restart the server)."%(rdId, ex, rdId))
298