95
void help(const string& about)
97
cout << "Status: 200 OK\r\n";
98
cout << "Content-Type: text/html\r\n\r\n";
99
cout << error(_("Not invoked correctly"), about,
100
_("The lurker.cgi must have two parameters: the config file "
101
"and the request to render. The value of QUERY_STRING is "
102
"taken to be the config file, and REQUEST_URI is taken "
103
"to be the requested page to render. Setting a 404 error "
104
"handler to lurker.cgi?config.file will usually set these "
105
"environment variables. Additionally, lurker may be invoked "
106
"from the command-line. Here, the first parameter is the "
107
"config file and the second is the requested uri."));
110
87
Request parse_request(const string& param)
112
89
string::size_type dot1 = param.rfind('.');
113
90
if (dot1 == string::npos || dot1 == 0)
115
cout << "Status: 200 OK\r\n";
116
cout << "Content-Type: text/html\r\n\r\n";
117
cout << error(_("Missing extension"), param,
118
_("An extension for the request was required, but missing"));
91
error(_("Missing extension"), param,
92
_("An extension for the request was required, but missing"));
122
94
string::size_type dot2 = param.rfind('.', dot1-1);
145
117
out.ext = param.substr(dot1+1, string::npos);
120
if (out.ext.length() < 3 || out.ext.length() > 6)
121
error(_("Bogus extension"), out.ext,
122
_("The extension is not 3-6 characters long."));
124
for (string::size_type i = 0; i < out.ext.length(); ++i)
125
if ((out.ext[i] < 'a' || out.ext[i] > 'z') &&
126
(out.ext[i] < '0' || out.ext[i] > '9'))
127
error(_("Bogus extension"), out.ext,
128
_("Not simple lower-case alphanumeric letters."));
148
130
if (!lstring::locale_normalize(out.language))
150
cout << "Status: 200 OK\r\n";
151
cout << "Content-Type: text/html\r\n\r\n";
152
cout << error(_("Bogus locale"), out.language,
153
_("The specified locale is not valid."));
131
error(_("Bogus locale"), out.language,
132
_("The specified locale is not valid."));
160
137
int main(int argc, char** argv)
162
string config, cdpath, request, host, port, cgipath, https;
139
string config, frontend, document, request, host, port, cgipath, https, ok;
169
146
bind_textdomain_codeset(PACKAGE, "utf-8");
172
if ((tmp = getenv("QUERY_STRING")) != 0) config = tmp;
173
if ((tmp = getenv("REQUEST_URI" )) != 0) request = tmp;
174
if ((tmp = getenv("SERVER_NAME" )) != 0) host = tmp;
175
if ((tmp = getenv("SERVER_PORT" )) != 0) port = tmp;
176
if ((tmp = getenv("SCRIPT_NAME" )) != 0) cgipath = tmp;
177
if ((tmp = getenv("HTTPS" )) != 0) https = tmp;
179
if (argc > 1) config = argv[1];
180
if (argc > 2) request = argv[2];
182
string::size_type csplit;
183
if ((csplit = config.find('?')) != string::npos)
185
cdpath = config.substr(csplit+1, string::npos);
188
if (csplit != 0 && config[csplit-1] == '\\')
189
config.resize(csplit-1);
190
else config.resize(csplit);
195
help(_("no config file set"));
200
help(_("no request set"));
204
if (cdpath != "" && chdir(cdpath.c_str()) != 0)
206
cout << "Status: 200 OK\r\n";
207
cout << "Content-Type: text/html\r\n\r\n";
208
cout << error(_("Cannot chdir"), cdpath + ":" + strerror(errno),
209
_("The specified path to the document root could "
210
"not be entered. Check the argument and permissions."));
149
// Every document about CGI agrees these exist:
150
if ((tmp = getenv("SERVER_NAME" )) != 0) host = tmp;
151
if ((tmp = getenv("SERVER_PORT" )) != 0) port = tmp;
152
if ((tmp = getenv("SCRIPT_NAME" )) != 0) cgipath = tmp;
153
// Many CGI 'standards' seem to agree this one exists for https:
154
if ((tmp = getenv("HTTPS" )) != 0) https = tmp;
156
// CGI guarantees this in case called as an error document
157
if ((tmp = getenv("REDIRECT_URL")) != 0) request = tmp;
158
// ... however, as we aren't always called that way, try this too:
159
if ((tmp = getenv("REQUEST_URI" )) != 0) request = tmp;
161
// get an over-ridden config location
162
if ((tmp = getenv("REDIRECT_LURKER_CONFIG")) != 0) config = tmp;
163
if ((tmp = getenv("LURKER_CONFIG")) != 0) config = tmp;
164
// get the frontend location
165
if ((tmp = getenv("REDIRECT_LURKER_FRONTEND")) != 0) document = tmp;
166
if ((tmp = getenv("LURKER_FRONTEND")) != 0) document = tmp;
168
if (request == "" || host == "" || port == "" || cgipath == "")
169
error(_("Not invoked correctly"),
170
_("CGI environment variables missing"),
171
_("The lurker.cgi must be run as a CGI script. See the "
172
"INSTALL file distributed with lurker for help setting "
173
"up your webserver to run lurker.cgi. Lurker.cgi reads "
174
"the environment variables REDIRECT_URL or REQUEST_URI "
175
"to determine the missing file requested by the user. "
176
"Also, SERVER_NAME, SERVER_PORT, and SCRIPT_NAME are "
177
"used to build absolute redirected URLs."));
179
// be nice: use a default config file
180
if (config == "") config = DEFAULT_CONFIG_FILE;
215
183
if (cfg.load(config.c_str()) != 0)
217
cout << "Status: 200 OK\r\n";
218
cout << "Content-Type: text/html\r\n\r\n";
219
cout << error(_("Cannot open config file"), "Config::load",
184
error(_("Cannot open config file"), "Config::load",
186
_("\nPerhaps you should set the LURKER_CONFIG "
187
"environment variable to select the correct "
188
"config file location. See the INSTALL file for "
189
"help on configuring your webserver."));
191
if (document == "" && cfg.frontends.size() > 1)
192
error(_("No frontend specified"), "LURKER_FRONTEND",
193
_("The lurker config file lists multiple frontends, "
194
"however, the environment variable LURKER_FRONTEND "
195
"does not specify which to use. See the INSTALL file "
196
"for help on configuring your webserver."));
198
// be nice: if only one frontend, use it by default:
199
if (document == "" && cfg.frontends.size() == 1)
201
document = cfg.frontends.begin()->first;
205
// Simplify the path to the requested document
206
if ((ok = simplifyPath(document)) != "")
207
error(_("Bad document request"), document,
208
_("The path '") + ok + _("' could not be resolved while "
209
"attempting to determine which frontend the document "
210
"belongs to. Perhaps a directory does not exist?"));
213
// Look for the matching front-end
215
Config::Frontends::const_iterator i, e;
216
for (i = cfg.frontends.begin(), e = cfg.frontends.end(); i != e; ++i)
218
// Either document IS the frontend path or it is a file
219
// contained in the frontend path.
220
if (i->first == document.substr(0, i->first.length()) ||
221
i->first + "/" == document.substr(0, i->first.length()+1))
229
error(_("No matching frontend"), document,
230
_("The frontend specified in the webserver "
231
"configuration does not match any frontend in the "
232
"lurker config file."));
234
cfg.set_permissions(cfg.frontends[frontend]);
236
if (chdir(frontend.c_str()) != 0)
237
error(_("Cannot chdir"), frontend + ":" + strerror(errno),
238
_("The specified frontend path could "
239
"not be entered. Check the path and permissions."));
224
241
auto_ptr<ESort::Reader> db(ESort::Reader::opendb(cfg.dbdir + "/db"));
227
cout << "Status: 200 OK\r\n";
228
cout << "Content-Type: text/html\r\n\r\n";
229
cout << error(_("Cannot open database snapshot"), strerror(errno),
230
_("The configured database 'dbdir' in the config file "
231
"could not be opened. Typically this means that it is "
232
"not readable by the user which the cgi is invoked as. "
233
"We suggest making dbdir and all files in it readable "
234
"by everyone since you are serving them on a website "
243
error(_("Cannot open database snapshot"), strerror(errno),
244
_("The configured database 'dbdir' in the config file "
245
"could not be opened. Typically this means that it is "
246
"not readable by the user which the cgi is invoked as. "
247
"We suggest making dbdir and all files in it readable "
248
"by everyone since you are serving them on a website "
251
request = decipherHalf(request);
239
252
vector<string> tokens;
240
253
tokenize(request, tokens, "/");
241
254
if (tokens.size() < 2)
243
cout << "Status: 200 OK\r\n";
244
cout << "Content-Type: text/html\r\n\r\n";
245
cout << error(_("Request malformed"), "tokenize(request)",
246
_("The request does not have at least two directory "
247
"components. It must be like ..../command/param.xml"));
255
error(_("Request malformed"), "tokenize(request)",
256
_("The request does not have at least two directory "
257
"components. It must be like ..../command/param.xml"));
251
259
string param = tokens[tokens.size()-1];
252
260
string command = tokens[tokens.size()-2];
263
if (document != frontend &&
264
frontend + '/' + command + '/' + param != document)
265
error(_("Requested document is in error"), document,
266
_("The requested document does not match the file "
267
"lurker intends to generate: ")
268
+ frontend + '/' + command + '/' + param);
255
270
if (https == "on")
257
272
server = "https://" + host;