1
/* $Id: search.cpp,v 1.17 2004/08/27 15:04:05 terpstra Exp $
3
* sindex.cpp - Handle a search/ command
5
* Copyright (C) 2002 - Wesley W. Terpstra
9
* Authors: 'Wesley W. Terpstra' <wesley@terpstra.ca>
11
* This program is free software; you can redistribute it and/or modify
12
* it under the terms of the GNU General Public License as published by
13
* the Free Software Foundation; version 2.
15
* This program is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
* GNU General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with this program; if not, write to the Free Software
22
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
#define _XOPEN_SOURCE 500
26
#define _FILE_OFFSET_BITS 64
34
#include <MessageId.h>
35
#include <XmlEscape.h>
41
#include "ConfigFile.h"
43
int search_format_error(const string& param)
45
cout << "Status: 200 OK\r\n";
46
cout << "Content-Type: text/html\r\n\r\n";
47
cout << error(_("Bad request"), param,
48
_("The given parameter was not of the correct format. "
49
"A searc request must be formatted like: "
50
"search/YYYYMMDD.HHMMSS.hashcode@word,word,word.xml"));
54
inline int fromHex(char c)
56
if (c >= 'A' && c <= 'Z') return c - 'A' + 10;
57
if (c >= 'a' && c <= 'z') return c - 'a' + 10;
61
string decipherHalf(const string& str)
63
// cout << "deciper: " << str << endl;
67
string::size_type b = 0, e;
69
while ((e = str.find_first_of("%+", b))
72
out.append(str, b, e - b);
73
if (str[e] == '+') out.append(" ");
74
else if (str.length() > e+2)
76
int ch = fromHex(str[e+1]) << 4 | fromHex(str[e+2]);
84
out.append(str, b, str.length() - b);
89
int handle_search(const Config& cfg, ESort::Reader* db, const string& param)
91
Request req = parse_request(param);
92
cfg.options = req.options;
94
string::size_type o = req.options.find('@');
95
if (o == string::npos || o != MessageId::full_len)
96
return search_format_error(param);
98
if (!MessageId::is_full(req.options.c_str()))
99
return search_format_error(param);
101
vector<string> tokens;
104
MessageId id(req.options.c_str());
105
string raw = decipherHalf(req.options.substr(o, string::npos));
107
// we need to translate '!' to '/'
108
for (string::size_type es = 0; es < keys.length(); ++es)
109
if (keys[es] == '!') keys[es] = '/';
111
tokenize(keys, tokens, ",");
113
// Right! Everything the user did is ok.
115
vector<Summary> forward, backward, queue;
117
Search backwardk(cfg, db, Backward, id);
118
Search forwardk (cfg, db, Forward, id);
120
for (vector<string>::iterator i = tokens.begin(); i != tokens.end(); ++i)
124
backwardk.keyword(key);
125
forwardk.keyword(key);
128
if (!forwardk.pull(35, forward) || !backwardk.pull(35, backward))
130
cout << "Status: 200 OK\r\n";
131
cout << "Content-Type: text/html\r\n\r\n";
132
cout << error(_("Database search seek failure"), strerror(errno),
133
_("Something internal to the database failed. "
134
"Please contact the lurker user mailing list for "
135
"furth assistence."));
139
vector<Summary>::size_type left, right, i;
140
if (forward.size() + backward.size() < 20)
142
left = backward.size();
143
right = forward.size();
145
else if (forward.size() < 10)
147
right = forward.size();
150
else if (backward.size() < 10)
152
left = backward.size();
160
assert (left <= backward.size());
161
assert (right <= forward .size());
163
for (i = left; i > 0; --i) queue.push_back(backward[i-1]);
164
for (i = 0; i < right; ++i) queue.push_back(forward[i]);
167
for (i = 0; i < queue.size(); ++i)
168
if ((ok = queue[i].load(db, cfg)) != "")
173
cout << "Status: 200 OK\r\n";
174
cout << "Content-Type: text/html\r\n\r\n";
175
cout << error(_("Database search pull failure"), ok,
176
_("Something internal to the database failed. "
177
"Please contact the lurker user mailing list for "
178
"further assistence."));
182
Cache cache(cfg, "search",
184
raw + "." + // this is transformed so the webserver can eat it
188
cache.o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
189
<< "<?xml-stylesheet type=\"text/xsl\" href=\"../fmt/search.xsl\"?>\n"
190
<< "<search xml:lang=\"" << req.language << "\">\n"
191
<< " " << cfg(req.language) << "\n"
192
<< " <query>" << xmlEscape << keys << "</query>\n";
194
if (right < forward.size())
195
{ // we need a next link
196
i = std::min(right+9, forward.size()-1);
197
MessageId nd(forward[i].id());
198
nd.increment(); // hope that it doesn't exist (-> skips one)
199
cache.o << " <next>" << nd.serialize() << "</next>\n";
202
if (left < backward.size())
203
{ // we need a prev link
204
i = std::min(left+10, backward.size()-1);
205
MessageId pd(backward[i].id());
207
cache.o << " <prev>" << pd.serialize() << "</prev>\n";
210
for (i = 0; i < queue.size(); ++i)
211
cache.o << " <row>" << queue[i] << "</row>\n";
213
cache.o << "</search>\n";