~ubuntu-branches/ubuntu/dapper/lurker/dapper

« back to all changes in this revision

Viewing changes to render/list.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Meurer
  • Date: 2004-09-26 16:27:51 UTC
  • Revision ID: james.westby@ubuntu.com-20040926162751-z1ohcjltv7ojtg6z
Tags: upstream-1.2
ImportĀ upstreamĀ versionĀ 1.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  $Id: list.cpp,v 1.11 2004/08/27 15:04:05 terpstra Exp $
 
2
 *  
 
3
 *  list.cpp - Handle a list/ command
 
4
 *  
 
5
 *  Copyright (C) 2002 - Wesley W. Terpstra
 
6
 *  
 
7
 *  License: GPL
 
8
 *  
 
9
 *  Authors: 'Wesley W. Terpstra' <wesley@terpstra.ca>
 
10
 *  
 
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.
 
14
 *    
 
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.
 
19
 *    
 
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
 
23
 */
 
24
 
 
25
#define _XOPEN_SOURCE 500
 
26
#define _FILE_OFFSET_BITS 64
 
27
 
 
28
#include <iostream>
 
29
#include <cerrno>
 
30
#include <cassert>
 
31
#include <ctime>
 
32
 
 
33
#include <map>
 
34
#include <vector>
 
35
 
 
36
#include <MessageId.h>
 
37
#include <XmlEscape.h>
 
38
#include <Keys.h>
 
39
#include <esort.h>
 
40
 
 
41
#include "commands.h"
 
42
#include "Cache.h"
 
43
#include "Summary.h"
 
44
 
 
45
using namespace std;
 
46
 
 
47
#define NUM_TOPICS      20
 
48
#define NUM_DAYS        14
 
49
 
 
50
int list_format_error(const string& param)
 
51
{
 
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."));
 
59
        return 1;
 
60
}
 
61
 
 
62
int list_load_error(const string& ok)
 
63
{
 
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."));
 
70
        return 1;
 
71
}
 
72
 
 
73
// nested types break g++ 2.95
 
74
struct NewTopic
 
75
{
 
76
        bool            pushed;
 
77
        Summary         newest;
 
78
        vector<int>     days;
 
79
};
 
80
 
 
81
static time_t now;
 
82
 
 
83
int load_topic(const Config& cfg, ESort::Reader* db, const string& hash, NewTopic& t)
 
84
{
 
85
        string prefix = LU_KEYWORD LU_KEYWORD_THREAD + hash + '\0';
 
86
        auto_ptr<ESort::Walker> dayWalker(db->seek(
 
87
                prefix, "\xFF\xFF\xFF\xFF",
 
88
                ESort::Backward));
 
89
        
 
90
        // set it up for num days
 
91
        t.days.resize(NUM_DAYS, 0);
 
92
        t.pushed = false;
 
93
        
 
94
        while (dayWalker->advance() != -1)
 
95
        {       // check corrupt
 
96
                if (dayWalker->key.length() != prefix.length() + 8) break;
 
97
                
 
98
                MessageId id(dayWalker->key.c_str() + prefix.length(), 8);
 
99
                
 
100
                // this is the newest if none has been set
 
101
                if (!t.newest.loaded())
 
102
                {
 
103
                        Summary sum(id);
 
104
                        string ok = sum.load(db, cfg);
 
105
                        if (ok != "") return list_load_error(ok);
 
106
                        if (!sum.deleted()) t.newest = sum;
 
107
                }
 
108
                
 
109
                int daygap = (now - id.timestamp()) / (60*60*24);
 
110
                if (daygap >= NUM_DAYS)
 
111
                {
 
112
                        if (t.newest.loaded()) break;
 
113
                }
 
114
                else
 
115
                        ++t.days[daygap];
 
116
        }
 
117
        
 
118
        return 0;
 
119
}
 
120
        
 
121
int handle_list(const Config& cfg, ESort::Reader* db, const string& param)
 
122
{
 
123
        Request req = parse_request(param);
 
124
        cfg.options = req.options;
 
125
        
 
126
        if (cfg.lists.find(req.options) == cfg.lists.end())
 
127
        {
 
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 "
 
133
                          "wrong server?"));
 
134
                return 1;
 
135
        }
 
136
        
 
137
        const List& list = cfg.lists.find(req.options)->second;
 
138
        
 
139
        // Right! Everything the user did is ok.
 
140
        
 
141
        map<string, NewTopic>   topics;
 
142
        vector<string>          order;
 
143
        
 
144
        auto_ptr<ESort::Walker> topicFinder(db->seek(
 
145
                LU_NEW_TOPICS + list.mbox + '\0',
 
146
                "\xFF\xFF\xFF\xFF",
 
147
                ESort::Backward));
 
148
        
 
149
        string::size_type skip = 1 + list.mbox.length() + 1;
 
150
        
 
151
        now = time(0);
 
152
        
 
153
        while (order.size() < NUM_TOPICS && topicFinder->advance() != -1)
 
154
        {       // check corrupt
 
155
                if (topicFinder->key.length() != skip + 4 + 8) break;
 
156
                
 
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 |
 
163
                                (time_t)tss[3];
 
164
                
 
165
                string hash(topicFinder->key, skip + 4, 8);
 
166
                
 
167
                // not already loaded?
 
168
                if (topics.find(hash) == topics.end() &&
 
169
                    load_topic(cfg, db, hash, topics[hash]) != 0)
 
170
                        return 1;
 
171
                
 
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 &&
 
176
                    !t.pushed)
 
177
                {
 
178
                        t.pushed = true;
 
179
                        order.push_back(hash);
 
180
                }
 
181
        }
 
182
        
 
183
        Cache cache(cfg, "list", param, req.ext);
 
184
        
 
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";
 
190
        
 
191
        for (vector<string>::iterator i = order.begin(); i != order.end(); ++i)
 
192
        {
 
193
                cache.o << " <row>\n";
 
194
                
 
195
                NewTopic& t = topics[*i];
 
196
                vector<int>::iterator j;
 
197
                cache.o << "  <title>" << xmlEscape << skipSubjectStart(t.newest.subject().c_str()) << "</title>\n"
 
198
                        << "  ";
 
199
                for (j = t.days.begin(); j != t.days.end(); ++j)
 
200
                        cache.o << "<day>" << *j << "</day>";
 
201
                cache.o << "\n";
 
202
                
 
203
                cache.o << "  " << t.newest << "\n";
 
204
                cache.o << " </row>\n";
 
205
        }
 
206
        
 
207
        cache.o << "</list>\n";
 
208
        
 
209
        return 0;
 
210
}