1
/* $Id: list.cpp,v 1.11 2004/08/27 15:04:05 terpstra Exp $
3
* list.cpp - Handle a list/ 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
36
#include <MessageId.h>
37
#include <XmlEscape.h>
50
int list_format_error(const string& param)
52
cout << "Status: 200 OK\r\n";
53
cout << "Content-Type: text/html\r\n\r\n";
54
cout << error(_("Bad request"), param,
55
_("The given parameter was not of the correct format. "
56
"A list request must be formatted like: "
57
"list/listid.xml where listid "
58
"is the id of an indexed mailing list."));
62
int list_load_error(const string& ok)
64
cout << "Status: 200 OK\r\n";
65
cout << "Content-Type: text/html\r\n\r\n";
66
cout << error(_("Database list pull failure"), ok,
67
_("Something internal to the database failed. "
68
"Please contact the lurker user mailing list for "
69
"further assistence."));
73
// nested types break g++ 2.95
83
int load_topic(const Config& cfg, ESort::Reader* db, const string& hash, NewTopic& t)
85
string prefix = LU_KEYWORD LU_KEYWORD_THREAD + hash + '\0';
86
auto_ptr<ESort::Walker> dayWalker(db->seek(
87
prefix, "\xFF\xFF\xFF\xFF",
90
// set it up for num days
91
t.days.resize(NUM_DAYS, 0);
94
while (dayWalker->advance() != -1)
96
if (dayWalker->key.length() != prefix.length() + 8) break;
98
MessageId id(dayWalker->key.c_str() + prefix.length(), 8);
100
// this is the newest if none has been set
101
if (!t.newest.loaded())
104
string ok = sum.load(db, cfg);
105
if (ok != "") return list_load_error(ok);
106
if (!sum.deleted()) t.newest = sum;
109
int daygap = (now - id.timestamp()) / (60*60*24);
110
if (daygap >= NUM_DAYS)
112
if (t.newest.loaded()) break;
121
int handle_list(const Config& cfg, ESort::Reader* db, const string& param)
123
Request req = parse_request(param);
124
cfg.options = req.options;
126
if (cfg.lists.find(req.options) == cfg.lists.end())
128
cout << "Status: 200 OK\r\n";
129
cout << "Content-Type: text/html\r\n\r\n";
130
cout << error(_("No such list"), req.options,
131
_("The specified mailing list is not available in this "
132
"archive. Perhaps you misspelled it or went to the "
137
const List& list = cfg.lists.find(req.options)->second;
139
// Right! Everything the user did is ok.
141
map<string, NewTopic> topics;
142
vector<string> order;
144
auto_ptr<ESort::Walker> topicFinder(db->seek(
145
LU_NEW_TOPICS + list.mbox + '\0',
149
string::size_type skip = 1 + list.mbox.length() + 1;
153
while (order.size() < NUM_TOPICS && topicFinder->advance() != -1)
155
if (topicFinder->key.length() != skip + 4 + 8) break;
157
const unsigned char* tss =
158
reinterpret_cast<const unsigned char*>
159
(topicFinder->key.c_str() + skip);
160
time_t when = (time_t)tss[0] << 24 |
161
(time_t)tss[1] << 16 |
162
(time_t)tss[2] << 8 |
165
string hash(topicFinder->key, skip + 4, 8);
167
// not already loaded?
168
if (topics.find(hash) == topics.end() &&
169
load_topic(cfg, db, hash, topics[hash]) != 0)
172
// Is this point in time the first (non-deleted) hit?
173
NewTopic& t = topics[hash];
174
if (t.newest.loaded() && // does the thread have any hits?
175
t.newest.id().timestamp() == when &&
179
order.push_back(hash);
183
Cache cache(cfg, "list", param, req.ext);
185
cache.o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
186
<< "<?xml-stylesheet type=\"text/xsl\" href=\"../fmt/list.xsl\"?>\n"
187
<< "<list xml:lang=\"" << req.language << "\">\n"
188
<< " " << cfg(req.language) << "\n"
189
<< " " << list(req.language) << "\n";
191
for (vector<string>::iterator i = order.begin(); i != order.end(); ++i)
193
cache.o << " <row>\n";
195
NewTopic& t = topics[*i];
196
vector<int>::iterator j;
197
cache.o << " <title>" << xmlEscape << skipSubjectStart(t.newest.subject().c_str()) << "</title>\n"
199
for (j = t.days.begin(); j != t.days.end(); ++j)
200
cache.o << "<day>" << *j << "</day>";
203
cache.o << " " << t.newest << "\n";
204
cache.o << " </row>\n";
207
cache.o << "</list>\n";