root/nntp/article.cc

Revision b4f0cdb42e637fd017a965937ff7dd3170eb55ac, 8.0 KB (checked in by Antti-Juhani Kaijanaho <antti-juhani@…>, 16 months ago)

#77: Fix XRef handling in NNTP

Signed-off-by: Antti-Juhani Kaijanaho <antti-juhani@…>

  • Property mode set to 100644
Line 
1/*  This file is part of Alue, the multiprotocol Internet discussion daemon
2
3    Copyright © 2009, 2010, 2011 Antti-Juhani Kaijanaho
4
5    Alue is free software: you can redistribute it and/or modify it
6    under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    Alue is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with Alue.  If not, see <http://www.gnu.org/licenses/>.
17
18 */
19
20#include "connection.hh"
21#include "lexutils.hh"
22
23#include "../db/db.hh"
24#include "../db/msg.hh"
25#include "../db/user.hh"
26#include "../msg/entity.hh"
27#include "../msg/util.hh"
28
29namespace nntp
30{
31        class article : public connection::command
32        {
33        public:
34                enum cmd_type { ARTICLE, HEAD, BODY, STAT };
35
36                article(const char *s, cmd_type cmd) : cmd(cmd) {
37                        connection::register_command(s, this);
38                }
39
40                connection::continuation::ptr
41                perform(connection::cb, const std::string[], size_t) const;
42
43                virtual void write_capability_line(connection::cb c) const {
44                        if (cmd != ARTICLE) return;
45                        c.send_line("READER");
46                }
47        private:
48                const cmd_type cmd;
49        };
50
51        connection::continuation::ptr
52        article::perform(connection::cb c, const std::string args[], size_t cnt)
53                const
54        {
55                enum { MSGID, NUMBER, NONE } state;
56
57                std::string arg;
58                switch (cnt) {
59                case 0:
60                        state = NONE;
61                        break;
62                case 1:
63                        arg = args[0];
64                        if (arg[0] == '<') {
65                                if (arg[arg.length()-1] == '>') {
66                                        state = MSGID;
67                                        break;
68                                }
69                        } else if (is_number(arg)) {
70                                state = NUMBER;
71                                break;
72                        }
73                        /* passthrough */
74                default:
75                        c.send_line("501 syntax error");
76                        return c.dispatch();
77                }
78
79
80                boost::shared_ptr<db::msg> message;
81                std::string msgnum = "0";
82                try {
83                        switch (state) {
84                        case NUMBER:
85                                if (c.current_group()) {
86                                        db::group::number n;
87                                        std::istringstream is(arg);
88                                        is >> n;
89                                        message =
90                                                c.current_group()->get_article
91                                                (n);
92                                        msgnum = arg;
93                                        c.current_article() = n;
94                                } else {
95                                        throw db::no_such_group();
96                                }
97                                break;
98                        case MSGID:
99                                message = c.dbase().lookup_msgid(arg);
100                                break;
101                        case NONE:
102                                if (c.current_group()) {
103                                        message =
104                                                c.current_group()->get_article
105                                                (c.current_article());
106                                        std::ostringstream os;
107                                        os << c.current_article();
108                                        msgnum = os.str();
109                                } else {
110                                        throw db::no_such_group();
111                                }
112                                break;
113                        }
114                } catch (db::no_such_article) {
115                        switch (state) {
116                        case NUMBER:
117                                c.send_line("423 no such article");
118                                break;
119                        case MSGID:
120                                c.send_line("430 no such article");
121                                break;
122                        case NONE:
123                                if (c.current_article() == 0)
124                                        c.send_line("420 no such article");
125                                else
126                                        c.send_line("423 no such article");
127                                break;
128                        }
129                        return c.dispatch();
130                } catch (db::no_such_group) {
131                        c.send_line("412 no group selected");
132                        return c.dispatch();
133                }
134
135                if (!message->reading_authz(c.identity()))
136                {
137                        c.send_line("480 " + message->msgid() +
138                                    " is restricted");
139                        return c.dispatch();
140                }
141
142                if (message->is_censured())
143                {
144                        switch (state) {
145                        case NUMBER:
146                                c.send_line("423 no such article");
147                                break;
148                        case MSGID:
149                                c.send_line("430 no such article");
150                                break;
151                        case NONE:
152                                if (c.current_article() == 0)
153                                        c.send_line("420 no such article");
154                                else
155                                        c.send_line("423 no such article");
156                                break;
157                        }
158                        return c.dispatch();
159                }
160
161                std::string msgparm = msgnum + " " + message->msgid();
162
163                if (c.identity() && (cmd == ARTICLE || cmd == BODY))
164                        c.identity()->mark_read(message->msgid());
165                               
166                switch (cmd) {
167                case ARTICLE:
168                        c.send_line("220 " + msgparm + " article follows:");
169                        break;
170                case HEAD:
171                        c.send_line("221 " + msgparm + " headers follow:");
172                        break;
173                case BODY:
174                        c.send_line("222 " + msgparm + " body follows:");
175                        break;
176                case STAT:
177                        c.send_line("223 " + msgparm + " exists");
178                        return c.dispatch();
179                }
180
181                if (cmd == HEAD || cmd == ARTICLE)
182                {
183                        std::string head = message->get_entity()->get_header();
184                        head += message->get_xref_field(true);
185                        c.send_raw(head.begin(), head.end());
186                }
187                if (cmd == ARTICLE) c.send_line("");
188                if (cmd == ARTICLE || cmd == BODY)
189                {
190                        std::string b = msg::dot_stuff
191                                (message->get_entity()->get_body());
192                        c.send_raw(b.begin(), b.end());
193                }
194
195                c.send_line(".");
196                return c.dispatch();
197        }
198}
199namespace {
200        nntp::article article_("article", nntp::article::ARTICLE);
201        nntp::article head_("head", nntp::article::HEAD);
202        nntp::article body_("body", nntp::article::BODY);
203        nntp::article stat_("stat", nntp::article::STAT);
204}
Note: See TracBrowser for help on using the browser.