| 1 | |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | |
|---|
| 5 | |
|---|
| 6 | |
|---|
| 7 | |
|---|
| 8 | |
|---|
| 9 | |
|---|
| 10 | |
|---|
| 11 | |
|---|
| 12 | |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | #include "db.hh" |
|---|
| 21 | #include "db_reader.hh" |
|---|
| 22 | #include "msg.hh" |
|---|
| 23 | #include "role.hh" |
|---|
| 24 | #include "role_exists.hh" |
|---|
| 25 | #include "user.hh" |
|---|
| 26 | #include "user_exists.hh" |
|---|
| 27 | |
|---|
| 28 | #include "../config.hh" |
|---|
| 29 | #include "../logger/logline.hh" |
|---|
| 30 | #include "../msg/entity.hh" |
|---|
| 31 | #include "../msg/util.hh" |
|---|
| 32 | #include "../myassert.hh" |
|---|
| 33 | #include "../util.hh" |
|---|
| 34 | |
|---|
| 35 | #include <boost/date_time/posix_time/posix_time.hpp> |
|---|
| 36 | #include <sstream> |
|---|
| 37 | #include <fstream> |
|---|
| 38 | |
|---|
| 39 | namespace db |
|---|
| 40 | { |
|---|
| 41 | db::db() |
|---|
| 42 | : dbname(config["db-file"].as<std::string>()) |
|---|
| 43 | , pwh(new password_handler(config["pw-file"].as<std::string>())) |
|---|
| 44 | , global_next(0) |
|---|
| 45 | { |
|---|
| 46 | register_role(role::anon()); |
|---|
| 47 | register_role(role::authn()); |
|---|
| 48 | register_role(role::poster()); |
|---|
| 49 | } |
|---|
| 50 | |
|---|
| 51 | void db::register_role(role::ptr r) |
|---|
| 52 | { |
|---|
| 53 | boost::lock_guard<boost::recursive_mutex> lg(mt); |
|---|
| 54 | if (roles[r->get_name()]) |
|---|
| 55 | throw role_exists(r->get_name()); |
|---|
| 56 | roles[r->get_name()] = r; |
|---|
| 57 | } |
|---|
| 58 | |
|---|
| 59 | boost::shared_ptr<role> db::create_role(std::string name, |
|---|
| 60 | std::string description) |
|---|
| 61 | { |
|---|
| 62 | boost::lock_guard<boost::recursive_mutex> lg(mt); |
|---|
| 63 | role::ptr rv(new role(name,description)); |
|---|
| 64 | register_role(rv); |
|---|
| 65 | add_record("ROLE " + name + "\r\n" + |
|---|
| 66 | (description.empty() ? "" : |
|---|
| 67 | "DESCRIPTION " + description)); |
|---|
| 68 | return rv; |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | void db::do_newgroup(db_reader &dr, std::string line, |
|---|
| 72 | boost::posix_time::ptime timestamp) |
|---|
| 73 | { |
|---|
| 74 | std::string grp = line; |
|---|
| 75 | util::strip(grp); |
|---|
| 76 | std::string descr = dr.readline(line, "DESCRIPTION"); |
|---|
| 77 | std::string readperms = dr.readline(line, "READING"); |
|---|
| 78 | util::strip(readperms); |
|---|
| 79 | bool readperm; |
|---|
| 80 | if (readperms == "PERMITTED") readperm = false; |
|---|
| 81 | else if (readperms == "RESTRICTED") readperm = true; |
|---|
| 82 | else throw illformed_db("READING argument not recognised"); |
|---|
| 83 | dr.endrec(line); |
|---|
| 84 | groups[grp].reset(new group(grp, shared_from_this(), |
|---|
| 85 | timestamp, readperm, descr)); |
|---|
| 86 | } |
|---|
| 87 | |
|---|
| 88 | void db::do_user(db_reader &dr, std::string line) |
|---|
| 89 | { |
|---|
| 90 | std::string userid = line; |
|---|
| 91 | util::strip(userid); |
|---|
| 92 | boost::shared_ptr<user> &u = users[userid]; |
|---|
| 93 | if (!u) u.reset(new user(userid, shared_from_this(), pwh)); |
|---|
| 94 | while (true) |
|---|
| 95 | { |
|---|
| 96 | dr.readline(line); |
|---|
| 97 | if (line == ".END" || line == ".END\r") break; |
|---|
| 98 | std::string key = util::split(line); |
|---|
| 99 | util::strip(line); |
|---|
| 100 | if (key == "display_name") |
|---|
| 101 | u->display_name = line; |
|---|
| 102 | else if (key =="display_email") |
|---|
| 103 | u->display_email = line; |
|---|
| 104 | else if (key =="delivery_email") |
|---|
| 105 | { |
|---|
| 106 | u->delivery_email = line; |
|---|
| 107 | users_email[line] = u; |
|---|
| 108 | } |
|---|
| 109 | else if (key =="delivery_email_verified") |
|---|
| 110 | u->delivery_email_verified = line == "yes"; |
|---|
| 111 | else if (key =="delivery_email_cookie") |
|---|
| 112 | u->delivery_email_cookie = line; |
|---|
| 113 | else if (key == "allow_cleartext_password") |
|---|
| 114 | u->do_allow_cleartext_password = line == "yes"; |
|---|
| 115 | else if (key == "has_read") |
|---|
| 116 | u->read_msgids.insert(line); |
|---|
| 117 | else if (key == "has_not_read") |
|---|
| 118 | u->read_msgids.erase(line); |
|---|
| 119 | else if (key == "allow_posting") |
|---|
| 120 | { |
|---|
| 121 | if (line == "yes") |
|---|
| 122 | u->roles.insert(role::poster()); |
|---|
| 123 | else |
|---|
| 124 | u->roles.erase(role::poster()); |
|---|
| 125 | } |
|---|
| 126 | else |
|---|
| 127 | throw illformed_db("user key" + key); |
|---|
| 128 | } |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | void db::do_role(db_reader &dr, std::string line) |
|---|
| 132 | { |
|---|
| 133 | std::string roleid = line; |
|---|
| 134 | util::strip(roleid); |
|---|
| 135 | if (roleid == role::authn()->get_name() || |
|---|
| 136 | roleid == role::anon()->get_name()) |
|---|
| 137 | throw illformed_db("reserved role in role record"); |
|---|
| 138 | role::ptr &r = roles[roleid]; |
|---|
| 139 | if (!r) r.reset(new role(roleid, "")); |
|---|
| 140 | while (true) |
|---|
| 141 | { |
|---|
| 142 | dr.readline(line); |
|---|
| 143 | if (line == ".END" || line == ".END\r") break; |
|---|
| 144 | std::string key = util::split(line); |
|---|
| 145 | if (key == "DESCRIPTION") |
|---|
| 146 | { |
|---|
| 147 | if (roleid == role::poster()->get_name()) |
|---|
| 148 | throw illformed_db |
|---|
| 149 | ("bad use of 'poster'"); |
|---|
| 150 | if (role::is_subscriber_role(roleid)) |
|---|
| 151 | throw illformed_db |
|---|
| 152 | ("bad use of 'subscribers:'"); |
|---|
| 153 | r->description = line; |
|---|
| 154 | } |
|---|
| 155 | else if (key == "USER") |
|---|
| 156 | { |
|---|
| 157 | std::string key2 = util::split(line); |
|---|
| 158 | util::strip(line); |
|---|
| 159 | bool add; |
|---|
| 160 | if (key2 == "ADD") |
|---|
| 161 | add = true; |
|---|
| 162 | else if (key2 == "DEL") |
|---|
| 163 | add = false; |
|---|
| 164 | else |
|---|
| 165 | throw illformed_db("role user key"); |
|---|
| 166 | boost::shared_ptr<user> u = users[line]; |
|---|
| 167 | if (!u) throw illformed_db("no such user"); |
|---|
| 168 | if (add) |
|---|
| 169 | { |
|---|
| 170 | u->roles.insert(r); |
|---|
| 171 | r->users.insert(u); |
|---|
| 172 | } |
|---|
| 173 | else |
|---|
| 174 | { |
|---|
| 175 | u->roles.erase(r); |
|---|
| 176 | r->users.erase(u); |
|---|
| 177 | } |
|---|
| 178 | } |
|---|
| 179 | else |
|---|
| 180 | throw illformed_db("role key"); |
|---|
| 181 | } |
|---|
| 182 | } |
|---|
| 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 | |
|---|
| 215 | void db::do_article(db_reader &dr, std::string line) |
|---|
| 216 | { |
|---|
| 217 | std::string msgid = line; |
|---|
| 218 | util::strip(msgid); |
|---|
| 219 | if (msgid.empty() || msgid[0] != '<' || |
|---|
| 220 | msgid[msgid.length()-1] != '>') |
|---|
| 221 | throw illformed_db("invalid msgid"); |
|---|
| 222 | |
|---|
| 223 | dr.readline(line); |
|---|
| 224 | user::ptr u; |
|---|
| 225 | if (line.substr(0, 10) == "POSTED BY ") |
|---|
| 226 | { |
|---|
| 227 | std::string uid = line.substr(10); |
|---|
| 228 | util::strip(uid); |
|---|
| 229 | u = users[uid]; |
|---|
| 230 | BOOST_ASSERT(u); |
|---|
| 231 | dr.readline(line); |
|---|
| 232 | } |
|---|
| 233 | |
|---|
| 234 | std::map<std::string, group::number> xref; |
|---|
| 235 | while (true) |
|---|
| 236 | { |
|---|
| 237 | if (line.substr(0, 8) != "FILE AS ") break; |
|---|
| 238 | std::string fileas = line.substr(8); |
|---|
| 239 | util::strip(fileas); |
|---|
| 240 | size_t colon = fileas.find(':'); |
|---|
| 241 | if (colon == std::string::npos) |
|---|
| 242 | throw illformed_db("invalid FILE AS"); |
|---|
| 243 | std::string grp = fileas.substr(0, colon); |
|---|
| 244 | std::string nrs = fileas.substr(colon+1); |
|---|
| 245 | group::number nr = |
|---|
| 246 | boost::lexical_cast<group::number>(nrs); |
|---|
| 247 | xref[grp] = nr; |
|---|
| 248 | if (groups.find(grp) == groups.end()) |
|---|
| 249 | throw illformed_db("no such group " + grp); |
|---|
| 250 | dr.readline(line); |
|---|
| 251 | } |
|---|
| 252 | util::strip_crlf(line); |
|---|
| 253 | |
|---|
| 254 | if (line != "FOLLOWS") throw illformed_db("FOLLOWS missing"); |
|---|
| 255 | std::ostringstream ss; |
|---|
| 256 | while (true) |
|---|
| 257 | { |
|---|
| 258 | dr.readline(line); |
|---|
| 259 | if (line == ".END" || line == ".END\r") break; |
|---|
| 260 | if (!dr.valid()) throw rollback(); |
|---|
| 261 | ss << line; |
|---|
| 262 | ss << '\n'; |
|---|
| 263 | } |
|---|
| 264 | |
|---|
| 265 | std::string msgstr = |
|---|
| 266 | ::msg::dot_destuff(::msg::crlf_canonize(ss.str())); |
|---|
| 267 | |
|---|
| 268 | |
|---|
| 269 | ::msg::entity::ptr entity = ::msg::entity::mk(msgstr, false); |
|---|
| 270 | msg::ptr message(new msg::msg(msgid,entity,u)); |
|---|
| 271 | for (std::map<std::string, group::number>::const_iterator it = |
|---|
| 272 | xref.begin(); |
|---|
| 273 | it != xref.end(); it++) |
|---|
| 274 | { |
|---|
| 275 | std::string ng = it->first; |
|---|
| 276 | group::number n = it->second; |
|---|
| 277 | boost::shared_ptr<group> gr = groups[ng]; |
|---|
| 278 | gr->file(message, n); |
|---|
| 279 | } |
|---|
| 280 | if (this->msgid.find(msgid) != this->msgid.end()) |
|---|
| 281 | { |
|---|
| 282 | logger::logline ll; |
|---|
| 283 | ll << "warning: duplicate message-id " |
|---|
| 284 | << msgid |
|---|
| 285 | << " in db"; |
|---|
| 286 | } |
|---|
| 287 | this->msgid[msgid] = message; |
|---|
| 288 | thr.add_msg(global_next, message); |
|---|
| 289 | global_next++; |
|---|
| 290 | } |
|---|
| 291 | |
|---|
| 292 | void db::read_record(db_reader &dr, boost::posix_time::ptime timestamp) |
|---|
| 293 | { |
|---|
| 294 | std::string line; |
|---|
| 295 | dr.readline(line); |
|---|
| 296 | std::string cmd = util::split(line); |
|---|
| 297 | if (cmd == "NEWGROUP") |
|---|
| 298 | do_newgroup(dr, line, timestamp); |
|---|
| 299 | else if (cmd == "USER") |
|---|
| 300 | do_user(dr, line); |
|---|
| 301 | else if (cmd == "ROLE") |
|---|
| 302 | do_role(dr, line); |
|---|
| 303 | else if (cmd == "ARTICLE") |
|---|
| 304 | do_article(dr, line); |
|---|
| 305 | else if (cmd == "MODERATION") |
|---|
| 306 | do_moderation(dr, line); |
|---|
| 307 | else |
|---|
| 308 | throw illformed_db("unrecognized record type " + cmd); |
|---|
| 309 | } |
|---|
| 310 | |
|---|
| 311 | void db::init() |
|---|
| 312 | { |
|---|
| 313 | boost::lock_guard<boost::recursive_mutex> lg(mt); |
|---|
| 314 | |
|---|
| 315 | std::ifstream dbfile(dbname.c_str()); |
|---|
| 316 | db_reader dr(dbfile); |
|---|
| 317 | |
|---|
| 318 | while (true) |
|---|
| 319 | { |
|---|
| 320 | std::string line; |
|---|
| 321 | |
|---|
| 322 | std::getline(dbfile, line); |
|---|
| 323 | if (dbfile.eof()) break; |
|---|
| 324 | util::strip(line); |
|---|
| 325 | if (line.substr(0, 7) != ".BEGIN ") |
|---|
| 326 | throw illformed_db(".BEGIN missing"); |
|---|
| 327 | |
|---|
| 328 | util::strip_crlf(line); |
|---|
| 329 | |
|---|
| 330 | boost::posix_time::ptime timestamp = |
|---|
| 331 | boost::posix_time::from_iso_string |
|---|
| 332 | (line.substr(7)); |
|---|
| 333 | try |
|---|
| 334 | { |
|---|
| 335 | read_record(dr, timestamp); |
|---|
| 336 | } |
|---|
| 337 | catch (illformed_db &id) |
|---|
| 338 | { |
|---|
| 339 | logger::logline ll; |
|---|
| 340 | ll << "warning: " << id.what(); |
|---|
| 341 | while (dr.valid()) |
|---|
| 342 | { |
|---|
| 343 | dr.readline(line); |
|---|
| 344 | util::strip(line); |
|---|
| 345 | if (line == ".END") break; |
|---|
| 346 | } |
|---|
| 347 | } |
|---|
| 348 | catch (rollback) |
|---|
| 349 | { |
|---|
| 350 | logger::logline ll; |
|---|
| 351 | ll << "warning: rolled back a record"; |
|---|
| 352 | } |
|---|
| 353 | } |
|---|
| 354 | thr.recalc_roots(); |
|---|
| 355 | thr.sort(); |
|---|
| 356 | } |
|---|
| 357 | |
|---|
| 358 | boost::shared_ptr<msg> db::file(boost::shared_ptr< ::msg::entity > ent, |
|---|
| 359 | boost::asio::ip::address source, |
|---|
| 360 | boost::shared_ptr<user> poster, |
|---|
| 361 | server::conn_cb cb) |
|---|
| 362 | { |
|---|
| 363 | |
|---|
| 364 | |
|---|
| 365 | ::msg::complete_netnews(ent); |
|---|
| 366 | |
|---|
| 367 | std::string server_name = |
|---|
| 368 | config["canonical-name"].as<std::string>(); |
|---|
| 369 | |
|---|
| 370 | |
|---|
| 371 | |
|---|
| 372 | |
|---|
| 373 | { |
|---|
| 374 | std::ostringstream ss; |
|---|
| 375 | ss << server_name |
|---|
| 376 | << "; posting_host=\"" |
|---|
| 377 | << source.to_string() |
|---|
| 378 | << "\""; |
|---|
| 379 | ent->replace_field("Injection-Info", ss.str()); |
|---|
| 380 | } |
|---|
| 381 | |
|---|
| 382 | std::string path = ent->get_field("Path", false); |
|---|
| 383 | util::strip_crlf(path); |
|---|
| 384 | if (path.empty()) |
|---|
| 385 | { |
|---|
| 386 | path = "not-for-mail"; |
|---|
| 387 | } |
|---|
| 388 | else |
|---|
| 389 | { |
|---|
| 390 | if (path.find("!.POSTED") != std::string::npos) |
|---|
| 391 | { |
|---|
| 392 | throw filing_exception("Path contains " |
|---|
| 393 | "a POSTED diag-keyword"); |
|---|
| 394 | } |
|---|
| 395 | } |
|---|
| 396 | path = server_name + |
|---|
| 397 | " !.POSTED." + source.to_string() + " !" + path; |
|---|
| 398 | ent->replace_field("Path", path); |
|---|
| 399 | |
|---|
| 400 | ::msg::validate_netnews(ent); |
|---|
| 401 | |
|---|
| 402 | if (ent->has_field("Control")) |
|---|
| 403 | throw filing_exception("Control messages " |
|---|
| 404 | "are not supported"); |
|---|
| 405 | if (ent->has_field("Supersedes")) |
|---|
| 406 | throw filing_exception("Supersession is not supported"); |
|---|
| 407 | |
|---|
| 408 | msg::ptr mg(new msg(ent, poster)); |
|---|
| 409 | |
|---|
| 410 | boost::lock_guard<boost::recursive_mutex> lg(mt); |
|---|
| 411 | |
|---|
| 412 | std::list<std::string> ngs = mg->get_newsgroups(); |
|---|
| 413 | const std::set<role::ptr> *rdset = 0; |
|---|
| 414 | bool have_groups = false; |
|---|
| 415 | for (std::list<std::string>::iterator it = ngs.begin(); |
|---|
| 416 | it != ngs.end(); it++) |
|---|
| 417 | { |
|---|
| 418 | std::string ng = *it; |
|---|
| 419 | boost::shared_ptr<group> gr = groups[ng]; |
|---|
| 420 | if (gr) |
|---|
| 421 | { |
|---|
| 422 | if (!rdset) |
|---|
| 423 | { |
|---|
| 424 | rdset = &gr->get_readers(); |
|---|
| 425 | } |
|---|
| 426 | else |
|---|
| 427 | { |
|---|
| 428 | if (*rdset != gr->get_readers()) |
|---|
| 429 | throw filing_exception("crossposting between differently reading-restricted groups is not allowed"); |
|---|
| 430 | } |
|---|
| 431 | if (!gr->posting_authz(poster)) |
|---|
| 432 | throw filing_exception("you are not authorized to post to " + gr->name()); |
|---|
| 433 | have_groups = true; |
|---|
| 434 | } |
|---|
| 435 | } |
|---|
| 436 | if (!have_groups) throw filing_exception("none of our groups"); |
|---|
| 437 | |
|---|
| 438 | if (this->msgid.find(mg->msgid()) != this->msgid.end()) |
|---|
| 439 | { |
|---|
| 440 | logger::logline ll; |
|---|
| 441 | ll << "duplicate message ID " |
|---|
| 442 | << mg->msgid() |
|---|
| 443 | << " offered by " |
|---|
| 444 | << source; |
|---|
| 445 | throw filing_exception("duplicate message ID"); |
|---|
| 446 | } |
|---|
| 447 | |
|---|
| 448 | |
|---|
| 449 | |
|---|
| 450 | std::map<std::string, group::number> xref; |
|---|
| 451 | for (std::list<std::string>::iterator it = ngs.begin(); |
|---|
| 452 | it != ngs.end(); it++) |
|---|
| 453 | { |
|---|
| 454 | std::string ng = *it; |
|---|
| 455 | boost::shared_ptr<group> gr = groups[ng]; |
|---|
| 456 | if (gr) |
|---|
| 457 | { |
|---|
| 458 | group::number n = gr->file(mg, cb); |
|---|
| 459 | xref[ng] = n; |
|---|
| 460 | } |
|---|
| 461 | } |
|---|
| 462 | |
|---|
| 463 | msgid[mg->msgid()] = mg; |
|---|
| 464 | thr.add_msg(global_next, mg); |
|---|
| 465 | thr.recalc_roots(); |
|---|
| 466 | thr.sort(); |
|---|
| 467 | global_next++; |
|---|
| 468 | |
|---|
| 469 | std::ofstream dbfile(dbname.c_str(), std::ios_base::app); |
|---|
| 470 | dbfile << ".BEGIN " |
|---|
| 471 | << boost::posix_time::to_iso_string( |
|---|
| 472 | boost::posix_time::second_clock::universal_time() |
|---|
| 473 | ) |
|---|
| 474 | << "\n" |
|---|
| 475 | << "ARTICLE " |
|---|
| 476 | << mg->msgid() |
|---|
| 477 | << "\n" |
|---|
| 478 | << "POSTED BY " |
|---|
| 479 | << poster->get_userid() |
|---|
| 480 | << "\n"; |
|---|
| 481 | for (std::map<std::string, group::number>::const_iterator it |
|---|
| 482 | = xref.begin(); |
|---|
| 483 | it != xref.end(); it++) |
|---|
| 484 | { |
|---|
| 485 | dbfile << "FILE AS " |
|---|
| 486 | << it->first |
|---|
| 487 | << ":" |
|---|
| 488 | << it->second |
|---|
| 489 | << "\n"; |
|---|
| 490 | } |
|---|
| 491 | dbfile << "FOLLOWS\n" |
|---|
| 492 | << ::msg::dot_stuff(mg->get_entity()->get_entity()) |
|---|
| 493 | << ".END\n"; |
|---|
| 494 | dbfile.close(); |
|---|
| 495 | return mg; |
|---|
| 496 | } |
|---|
| 497 | |
|---|
| 498 | void db::add_record(std::string record) |
|---|
| 499 | { |
|---|
| 500 | if (record.empty()) return; |
|---|
| 501 | |
|---|
| 502 | boost::lock_guard<boost::recursive_mutex> lg(mt); |
|---|
| 503 | |
|---|
| 504 | for (size_t i = 0; i < record.length(); i++) |
|---|
| 505 | { |
|---|
| 506 | if (record[i] != '.') continue; |
|---|
| 507 | if (i > 0 && record[i-1] != '\n' && record[i-1] != '\r') |
|---|
| 508 | continue; |
|---|
| 509 | record.insert(i, "."); |
|---|
| 510 | } |
|---|
| 511 | if (record[record.length() - 1] != '\n' && |
|---|
| 512 | record[record.length() - 1] != '\r') |
|---|
| 513 | record += '\n'; |
|---|
| 514 | record += ".END\n"; |
|---|
| 515 | |
|---|
| 516 | boost::posix_time::ptime timestamp = |
|---|
| 517 | boost::posix_time::second_clock::universal_time(); |
|---|
| 518 | |
|---|
| 519 | std::ofstream dbfile(dbname.c_str(), std::ios_base::app); |
|---|
| 520 | dbfile << ".BEGIN " |
|---|
| 521 | << boost::posix_time::to_iso_string(timestamp) |
|---|
| 522 | << "\n" |
|---|
| 523 | << record; |
|---|
| 524 | dbfile.close(); |
|---|
| 525 | std::istringstream ss(record); |
|---|
| 526 | db_reader dr(ss); |
|---|
| 527 | read_record(dr, timestamp); |
|---|
| 528 | } |
|---|
| 529 | |
|---|
| 530 | boost::shared_ptr<user> db::create_user(std::string userid, |
|---|
| 531 | std::string passwd) |
|---|
| 532 | { |
|---|
| 533 | boost::lock_guard<boost::recursive_mutex> lg(mt); |
|---|
| 534 | boost::shared_ptr<user> &rv = users[userid]; |
|---|
| 535 | if (rv) throw user_exists(userid); |
|---|
| 536 | rv.reset(new user(userid, shared_from_this(), pwh)); |
|---|
| 537 | rv->set_password(passwd); |
|---|
| 538 | |
|---|
| 539 | add_record(std::string("USER ") + userid); |
|---|
| 540 | |
|---|
| 541 | rv->roles.insert(role::poster()); |
|---|
| 542 | add_record(std::string("ROLE poster\nUSER ADD ") + userid); |
|---|
| 543 | |
|---|
| 544 | logger::logline ll; |
|---|
| 545 | ll << "user '" << userid << "' created"; |
|---|
| 546 | |
|---|
| 547 | return rv; |
|---|
| 548 | } |
|---|
| 549 | } |
|---|