Changeset 2114058982a08f59f0f312544a16250eceab0098
- Timestamp:
- 01/09/11 21:00:35 (17 months ago)
- Author:
- Antti-Juhani Kaijanaho <antti-juhani@…>
- Children:
- cbc895b6da7a6196ef0422747bd7dec2a40551d5
- Parents:
- 0062266a64c1492ae732d9e77d88016547e82c16
- git-author:
- Antti-Juhani Kaijanaho <antti-juhani@…> (01/09/11 21:00:21)
- git-committer:
- Antti-Juhani Kaijanaho <antti-juhani@…> (01/09/11 21:00:35)
- Message:
-
#14: Support moderators with kill/mark-as-spam and resurrection powers
Signed-off-by: Antti-Juhani Kaijanaho <antti-juhani@…>
- Files:
-
Legend:
- Unmodified
- Added
- Removed
-
|
rba71d64
|
r2114058
|
|
| 69 | 69 | |
| 70 | 70 | NOTE: The following role names are reserved: anonymous, authenticated, |
| 71 | | poster. Similarly, all names starting with "subscribers:" are |
| 72 | | reserved. No ROLE records may be given for "anonymous" and |
| 73 | | "authenticated". ROLE records for the other reserved names are |
| 74 | | forbidden to include DESCRIPTION. |
| | 71 | poster. Similarly, all names starting with "subscribers:" and |
| | 72 | "moderator:" are reserved. No ROLE records may be given for |
| | 73 | "anonymous" and "authenticated". ROLE records for the other reserved |
| | 74 | names are forbidden to include DESCRIPTION. |
| 75 | 75 | |
| 76 | 76 | |
| … |
… |
|
| 111 | 111 | |
| 112 | 112 | |
| | 113 | MODERATION records |
| | 114 | |
| | 115 | .BEGIN <datetime> |
| | 116 | MODERATION <userid> |
| | 117 | <verb> <msgid> <reason> |
| | 118 | .END |
| | 119 | |
| | 120 | <verb> ::= KILL |
| | 121 | | SPAM |
| | 122 | | CLEAR |
| | 123 | |
| | 124 | <reason> ::= <text not containing line terminator or separator> |
| | 125 | |
| | 126 | The MODERATION line is REQUIRED and MUST NOT be repeated. The other |
| | 127 | lines MAY occur multiple times. The <userid> indicates the user who |
| | 128 | authorized these moderation actions. |
| | 129 | |
| | 130 | The effect of a KILL or SPAM line is to make the message disappear |
| | 131 | (the reason is free form text that is intended to be used as |
| | 132 | rationale); it will be as if it never existed. The effect of a CLEAR |
| | 133 | line is to undo a previous KILL or SPAM line. |
-
|
rba71d64
|
r2114058
|
|
| 1 | 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
| 2 | 2 | |
| 3 | | Copyright © 2009, 2010 Antti-Juhani Kaijanaho |
| | 3 | Copyright © 2009, 2010, 2011 Antti-Juhani Kaijanaho |
| 4 | 4 | |
| 5 | 5 | Alue is free software: you can redistribute it and/or modify it |
| … |
… |
|
| 182 | 182 | } |
| 183 | 183 | |
| | 184 | void db::do_moderation(db_reader &dr, std::string line) |
| | 185 | { |
| | 186 | std::string uid = line; |
| | 187 | util::strip(uid); |
| | 188 | user::ptr u = users[uid]; |
| | 189 | if (!u) throw illformed_db("MODERATION user invalid: " + uid); |
| | 190 | while (true) |
| | 191 | { |
| | 192 | dr.readline(line); |
| | 193 | if (line == ".END" || line == ".END\r") break; |
| | 194 | std::string keys = util::split(line); |
| | 195 | std::string mid = util::split(line); |
| | 196 | std::string reason = line; |
| | 197 | msg::ptr m = msgid[mid]; |
| | 198 | if (!m) |
| | 199 | { |
| | 200 | logger::logline ll; |
| | 201 | ll << "unknown message in MODERATION ignored: " |
| | 202 | << keys << " " << mid; |
| | 203 | continue; |
| | 204 | } |
| | 205 | msg::moderation::kind_type key; |
| | 206 | /**/ if (keys == "KILL") key = msg::moderation::KILL; |
| | 207 | else if (keys == "SPAM") key = msg::moderation::SPAM; |
| | 208 | else if (keys == "CLEAR") key = msg::moderation::CLEAR; |
| | 209 | else throw illformed_db("MODERATION key error: " + |
| | 210 | keys); |
| | 211 | m->moderate(msg::moderation(key, u, reason)); |
| | 212 | } |
| | 213 | } |
| | 214 | |
| 184 | 215 | void db::do_article(db_reader &dr, std::string line) |
| 185 | 216 | { |
| … |
… |
|
| 272 | 303 | else if (cmd == "ARTICLE") |
| 273 | 304 | do_article(dr, line); |
| | 305 | else if (cmd == "MODERATION") |
| | 306 | do_moderation(dr, line); |
| 274 | 307 | else |
| 275 | 308 | throw illformed_db("unrecognized record type " + cmd); |
-
|
r1a970cd
|
r2114058
|
|
| 1 | 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
| 2 | 2 | |
| 3 | | Copyright © 2009, 2010 Antti-Juhani Kaijanaho |
| | 3 | Copyright © 2009, 2010, 2011 Antti-Juhani Kaijanaho |
| 4 | 4 | |
| 5 | 5 | Alue is free software: you can redistribute it and/or modify it |
| … |
… |
|
| 137 | 137 | void do_article(db_reader &, std::string); |
| 138 | 138 | void do_role(db_reader &, std::string); |
| | 139 | void do_moderation(db_reader &, std::string); |
| 139 | 140 | void read_record(db_reader &dr, boost::posix_time::ptime); |
| 140 | 141 | void register_role(boost::shared_ptr<role>); |
-
|
rba71d64
|
r2114058
|
|
| 1 | 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
| 2 | 2 | |
| 3 | | Copyright © 2009, 2010 Antti-Juhani Kaijanaho |
| | 3 | Copyright © 2009, 2010, 2011 Antti-Juhani Kaijanaho |
| 4 | 4 | |
| 5 | 5 | Alue is free software: you can redistribute it and/or modify it |
| … |
… |
|
| 39 | 39 | { |
| 40 | 40 | public: |
| | 41 | struct moderation |
| | 42 | { |
| | 43 | const enum kind_type { CLEAR, KILL, SPAM } kind; |
| | 44 | const boost::shared_ptr<const user> u; |
| | 45 | const std::string reason; |
| | 46 | |
| | 47 | moderation(kind_type k, |
| | 48 | boost::shared_ptr<const user> u, |
| | 49 | std::string reason) |
| | 50 | : kind(k) |
| | 51 | , u(u) |
| | 52 | , reason(reason) |
| | 53 | {} |
| | 54 | |
| | 55 | bool is_censure() const { return kind != CLEAR; } |
| | 56 | }; |
| | 57 | |
| 41 | 58 | typedef boost::shared_ptr<msg> ptr; |
| 42 | 59 | typedef boost::shared_ptr<const msg> const_ptr; |
| … |
… |
|
| 51 | 68 | explicit msg(boost::shared_ptr< ::msg::entity >, |
| 52 | 69 | boost::shared_ptr<user>); |
| | 70 | |
| | 71 | void moderate(moderation m) { mods.push_back(m); } |
| | 72 | |
| | 73 | bool is_censured() const |
| | 74 | { return !mods.empty() && mods.back().is_censure(); } |
| | 75 | |
| | 76 | boost::shared_ptr<const user> censor() const { |
| | 77 | boost::shared_ptr<const user> rv; |
| | 78 | if (!mods.empty()) rv = mods.back().u; |
| | 79 | return rv; |
| | 80 | } |
| | 81 | |
| | 82 | const std::list<moderation> &get_moderations() const |
| | 83 | { return mods; } |
| 53 | 84 | |
| 54 | 85 | boost::shared_ptr<const ::msg::entity> get_entity() const |
| … |
… |
|
| 79 | 110 | boost::shared_ptr< ::msg::entity > actual; |
| 80 | 111 | boost::shared_ptr<user> poster; |
| | 112 | std::list<moderation> mods; |
| 81 | 113 | }; |
| 82 | 114 | |
-
|
re1af652
|
r2114058
|
|
| 1 | 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
| 2 | 2 | |
| 3 | | Copyright © 2010 Antti-Juhani Kaijanaho |
| | 3 | Copyright © 2010, 2011 Antti-Juhani Kaijanaho |
| 4 | 4 | |
| 5 | 5 | Alue is free software: you can redistribute it and/or modify it |
| … |
… |
|
| 19 | 19 | |
| 20 | 20 | #include "role.hh" |
| | 21 | #include "../nntp/wildmat.hh" |
| 21 | 22 | |
| 22 | 23 | namespace db |
| 23 | 24 | { |
| 24 | 25 | const std::string role::subscriber_prefix = "subscribers:"; |
| | 26 | const std::string role::moderator_prefix = "moderator:"; |
| | 27 | |
| | 28 | bool role::is_moderator_for(std::string grn) |
| | 29 | { |
| | 30 | if (!is_moderator()) return false; |
| | 31 | std::string mg = name.substr(moderator_prefix.length()); |
| | 32 | if (!nntp::is_wildmat(mg)) return false; |
| | 33 | return nntp::wildmatch(mg, grn); |
| | 34 | } |
| 25 | 35 | } |
-
|
r4c34ee2
|
r2114058
|
|
| 1 | 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
| 2 | 2 | |
| 3 | | Copyright © 2009, 2010 Antti-Juhani Kaijanaho |
| | 3 | Copyright © 2009, 2010, 2011 Antti-Juhani Kaijanaho |
| 4 | 4 | |
| 5 | 5 | Alue is free software: you can redistribute it and/or modify it |
| … |
… |
|
| 40 | 40 | |
| 41 | 41 | static const std::string subscriber_prefix; |
| | 42 | static const std::string moderator_prefix; |
| 42 | 43 | public: |
| 43 | 44 | typedef boost::shared_ptr<role> ptr; |
| … |
… |
|
| 50 | 51 | std::string get_name() const { return name; } |
| 51 | 52 | std::string get_description() const { return name; } |
| | 53 | |
| | 54 | bool is_moderator() { |
| | 55 | return name.substr(0, moderator_prefix.length()) |
| | 56 | == |
| | 57 | moderator_prefix; |
| | 58 | } |
| | 59 | bool is_moderator_for(std::string grn); |
| 52 | 60 | |
| 53 | 61 | const std::set<boost::shared_ptr<user> > &get_users() const { |
-
|
r1a970cd
|
r2114058
|
|
| 1 | 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
| 2 | 2 | |
| 3 | | Copyright © 2009, 2010 Antti-Juhani Kaijanaho |
| | 3 | Copyright © 2009, 2010, 2011 Antti-Juhani Kaijanaho |
| 4 | 4 | |
| 5 | 5 | Alue is free software: you can redistribute it and/or modify it |
| … |
… |
|
| 227 | 227 | } |
| 228 | 228 | |
| | 229 | bool user::is_group_moderator(std::string grn) const |
| | 230 | { |
| | 231 | for (std::set<role::ptr>::const_iterator it = roles.begin(); |
| | 232 | it != roles.end(); it++) |
| | 233 | { |
| | 234 | role::ptr r = *it; |
| | 235 | if (r->is_moderator_for(grn)) return true; |
| | 236 | } |
| | 237 | return false; |
| | 238 | } |
| | 239 | |
| | 240 | bool user::is_moderator(boost::shared_ptr<const group> gr) const |
| | 241 | { |
| | 242 | return is_group_moderator(gr->name()); |
| | 243 | } |
| | 244 | |
| | 245 | bool user::is_moderator(boost::shared_ptr<const msg> m) const |
| | 246 | { |
| | 247 | std::list<std::string> ngs = m->get_newsgroups(); |
| | 248 | for (std::list<std::string>::const_iterator it = ngs.begin(); |
| | 249 | it != ngs.end(); it++) |
| | 250 | { |
| | 251 | if (is_group_moderator(*it)) return true; |
| | 252 | } |
| | 253 | return false; |
| | 254 | } |
| | 255 | |
| 229 | 256 | } |
-
|
r563ece2
|
r2114058
|
|
| 1 | 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
| 2 | 2 | |
| 3 | | Copyright © 2009, 2010 Antti-Juhani Kaijanaho |
| | 3 | Copyright © 2009, 2010, 2011 Antti-Juhani Kaijanaho |
| 4 | 4 | |
| 5 | 5 | Alue is free software: you can redistribute it and/or modify it |
| … |
… |
|
| 33 | 33 | { |
| 34 | 34 | class db; |
| | 35 | class group; |
| | 36 | class msg; |
| 35 | 37 | class role; |
| 36 | 38 | |
| … |
… |
|
| 52 | 54 | |
| 53 | 55 | std::set<std::string> read_msgids; |
| | 56 | |
| | 57 | bool is_group_moderator(std::string) const; |
| 54 | 58 | |
| 55 | 59 | friend class db; |
| … |
… |
|
| 108 | 112 | void unmark_read(std::string msgid); |
| 109 | 113 | |
| | 114 | bool is_moderator(boost::shared_ptr<const group> gr) const; |
| | 115 | bool is_moderator(boost::shared_ptr<const msg> m) const; |
| | 116 | |
| 110 | 117 | static std::string passwd_problems(std::string pw) { |
| 111 | 118 | std::string err; |
-
|
r1a970cd
|
r2114058
|
|
| 1 | 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
| 2 | 2 | |
| 3 | | Copyright © 2009, 2010 Antti-Juhani Kaijanaho |
| | 3 | Copyright © 2009, 2010, 2011 Antti-Juhani Kaijanaho |
| 4 | 4 | |
| 5 | 5 | Alue is free software: you can redistribute it and/or modify it |
| … |
… |
|
| 140 | 140 | } |
| 141 | 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 | |
| 142 | 161 | std::string msgparm = msgnum + " " + message->msgid(); |
| 143 | 162 | |
-
|
r1a970cd
|
r2114058
|
|
| 1 | 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
| 2 | 2 | |
| 3 | | Copyright © 2009, 2010 Antti-Juhani Kaijanaho |
| | 3 | Copyright © 2009, 2010, 2011 Antti-Juhani Kaijanaho |
| 4 | 4 | |
| 5 | 5 | Alue is free software: you can redistribute it and/or modify it |
| … |
… |
|
| 84 | 84 | return c.dispatch(); |
| 85 | 85 | } |
| | 86 | if (m->is_censured()) |
| | 87 | { |
| | 88 | c.send_line("430 no such article"); |
| | 89 | return c.dispatch(); |
| | 90 | } |
| | 91 | |
| 86 | 92 | |
| 87 | 93 | c.send_line("221 " + hname + " follows:"); |
| … |
… |
|
| 133 | 139 | if (!m->reading_authz(c.identity())) |
| 134 | 140 | return; |
| | 141 | if (m->is_censured()) return; |
| 135 | 142 | |
| 136 | 143 | std::string hn = util::to_lower(hname); |
-
|
r1a970cd
|
r2114058
|
|
| 1 | 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
| 2 | 2 | |
| 3 | | Copyright © 2009, 2010 Antti-Juhani Kaijanaho |
| | 3 | Copyright © 2009, 2010, 2011 Antti-Juhani Kaijanaho |
| 4 | 4 | |
| 5 | 5 | Alue is free software: you can redistribute it and/or modify it |
| … |
… |
|
| 85 | 85 | return c.dispatch(); |
| 86 | 86 | } |
| | 87 | if (m->is_censured()) |
| | 88 | { |
| | 89 | c.send_line("430 no such article"); |
| | 90 | return c.dispatch(); |
| | 91 | } |
| 87 | 92 | c.send_line("221 overview follows:"); |
| 88 | 93 | write_article_line(c, 0, m); |
| … |
… |
|
| 139 | 144 | if (!m->reading_authz(c.identity())) |
| 140 | 145 | return; |
| | 146 | if (m->is_censured()) return; |
| 141 | 147 | std::ostringstream os; |
| 142 | 148 | os << artnum; |
-
|
rba71d64
|
r2114058
|
|
| 1 | 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
| 2 | 2 | |
| 3 | | Copyright © 2009, 2010 Antti-Juhani Kaijanaho |
| | 3 | Copyright © 2009, 2010, 2011 Antti-Juhani Kaijanaho |
| 4 | 4 | |
| 5 | 5 | Alue is free software: you can redistribute it and/or modify it |
| … |
… |
|
| 135 | 135 | std::string msgid = tn->get_msgid(); |
| 136 | 136 | bool rauthz = m ? m->reading_authz(u) : false; |
| | 137 | bool show = rauthz && !m->is_censured(); |
| 137 | 138 | bool authn = u; |
| 138 | 139 | value::const_ptr rv; |
| … |
… |
|
| 151 | 152 | uri::percent_encode(msgid), |
| 152 | 153 | false))); |
| 153 | | else if (var == "followup-allowed" && m->posting_authz(u)) |
| | 154 | else if (var == "followup-allowed" && |
| | 155 | m->posting_authz(u) && show) |
| 154 | 156 | rv.reset(new sv(http::compose::get_followup_uri(m))); |
| 155 | | else if (var == "followup-uri") |
| | 157 | else if (var == "followup-uri" && show) |
| 156 | 158 | rv.reset(new sv(http::compose::get_followup_uri(m))); |
| 157 | 159 | else if (!rauthz && var == "restricted") |
| 158 | 160 | rv.reset(new empty_value); |
| 159 | | else if (rauthz && m && var == "from") |
| | 161 | else if (show && m && var == "from") |
| 160 | 162 | rv.reset(new sv(quote(decode_unstructured |
| 161 | 163 | (e->get_field("From", false)), |
| 162 | 164 | !authn))); |
| 163 | | else if (rauthz && m && var == "subject") |
| | 165 | else if (show && m && var == "subject") |
| 164 | 166 | rv.reset(new sv(quote(decode_unstructured |
| 165 | 167 | (e->get_field("Subject", false)), |
| … |
… |
|
| 171 | 173 | rv.reset(new date_value((tn->get_latest_date()))); |
| 172 | 174 | |
| 173 | | else if (rauthz && m && var == "header") |
| | 175 | else if (show && m && var == "header") |
| 174 | 176 | rv.reset(new header_value(e,!u)); |
| 175 | | else if (rauthz && m && var == "body") |
| | 177 | else if (show && m && var == "body") |
| 176 | 178 | rv = e->get_tlate_value(authn); |
| 177 | 179 | else if (rauthz && m && var == "xref") |
| … |
… |
|
| 203 | 205 | else if (u && var == "marked_read" && u->has_read(msgid)) |
| 204 | 206 | rv.reset(new empty_value); |
| 205 | | else if (u && rauthz && m && var == "poster" && m->get_poster()) |
| | 207 | else if (u && show && m && var == "poster" && m->get_poster()) |
| 206 | 208 | rv.reset(new user_value(m->get_poster())); |
| | 209 | else if (u && rauthz && m && var == "own" && |
| | 210 | m->get_poster() == u) |
| | 211 | rv.reset(new empty_value); |
| | 212 | else if (rauthz && m->is_censured() && var == "moderated") |
| | 213 | rv.reset(new empty_value); |
| | 214 | else if (rauthz && m && var == "moderator") |
| | 215 | { |
| | 216 | db::user::const_ptr u = m->censor(); |
| | 217 | if (u) rv.reset(new user_value(u)); |
| | 218 | } |
| | 219 | else if (u && m && var == "mod-uri" && u->is_moderator(m)) |
| | 220 | rv.reset(new sv(quote("/mod", false))); |
| 207 | 221 | return rv; |
| 208 | 222 | } |