00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #ifndef USE_PCH
00017 #include "sys.h"
00018 #include "debug.h"
00019 #endif
00020
00021 #include "ServerSession.h"
00022 #include "keys.h"
00023 #include "Identity.h"
00024 #include "ServerConnection.h"
00025 #include "ClientSession.h"
00026 #include "Application.h"
00027 #include "Authentication.h"
00028 #include "Channel.h"
00029 #include "is_channel.h"
00030 #include "exceptions.h"
00031
00032 void ServerSession::new_message_received(ServerMessageIn const& msg)
00033 {
00034 #ifdef CWDEBUG
00035
00036 bool suppress_debug_output =
00037 (msg.key() == keys::PING && Application::instance().suppress_ping()) ||
00038 ((msg.key() == 352 || msg.key() == 354 || msg.key() == 315) && Application::instance().suppress_who());
00039 Debug(if (suppress_debug_output) libcw_do.off());
00040 #endif
00041
00042 DoutEntering(dc::debug, "ServerSession::new_message_received(" << msg << ")");
00043
00044
00045 if (M_message_event_server.trigger(MatcherEventType(msg)))
00046 {
00047 Debug(if (suppress_debug_output) libcw_do.on());
00048 return;
00049 }
00050
00051
00052
00053 bool pre_network_message = true;
00054 bool fully_processed = true;
00055 switch(msg.key())
00056 {
00057 case 1:
00058 {
00059
00060 ASSERT(msg.param(0) == nicks().in_nick());
00061 ASSERT(M_server_connection.secondary_connection() || msg.param(0) == identity().mynick()->in_name(*this));
00062 authentication_successful();
00063 M_server_connection.server()->set_irc_name(msg.prefix_str());
00064 MatchRequest match_request;
00065 match_request(1).regexp("Welcome to [Tt]he [^ ]* IRC [Nn]etwork", 1);
00066 if (!match_request.is_match(msg))
00067 {
00068 std::ostringstream buf;
00069 buf << "Unrecognized 001 format [" << msg.param(1) << "]. Bailing out.";
00070 identity().send_notice(buf.str(), server_port_question);
00071 M_server_connection.disconnect();
00072 }
00073 else
00074 M_network_name.assign(msg.param(1), 15, msg.param(1).find(' ', 16) - 15);
00075 break;
00076 }
00077 case 4:
00078 {
00079
00080
00081 M_i_support.begin();
00082
00083 M_umodes = msg.param(3);
00084
00085 M_server_connection.server()->version().set_version(msg.param(2), timerRequest.get_now().tv_sec);
00086 break;
00087 }
00088 case 5:
00089 {
00090
00091 for (unsigned int n = 1; n < msg.params_size() - 1; ++n)
00092 M_i_support.add(msg.param(n));
00093 break;
00094 }
00095 case 251:
00096 {
00097
00098 M_i_support.end();
00099 break;
00100 }
00101 case 2:
00102 case 3:
00103 case 252:
00104 case 253:
00105 case 254:
00106 case 255:
00107 case 372:
00108 case 375:
00109 case 376:
00110 break;
00111 default:
00112 {
00113 pre_network_message = false;
00114
00115 switch (msg.key())
00116 {
00117 using namespace keys;
00118 case ERROR:
00119 M_server_connection.error_message_received(msg);
00120 break;
00121 case PING:
00122 M_server_connection.decode_ping(msg);
00123 break;
00124 case NICK:
00125 {
00126 if (!M_server_session_nicks.nick_received(msg.param(0)))
00127 {
00128 fully_processed = false;
00129 break;
00130 }
00131
00132 identity().mynick()->serverside_nick_changed_to(msg.server_session(), M_server_session_nicks.in_nick());
00133 if (!M_server_connection.secondary_connection())
00134 {
00135
00136 ASSERT(identity().client_session().client_in_nick() == msg.param(0));
00137 Prefix prefix(msg);
00138
00139 MessageOut msgout(default_priority(keys::NICK));
00140
00141 identity().client_session().queue_msg(msgout << prefix << keys::NICK << identity().client_session().client_out_nick());
00142 identity().mynick()->clientside_nick_changed_to(identity().client_session(), identity().client_session().client_out_nick());
00143 }
00144 if (M_server_connection.state() == real_nick_sent)
00145 {
00146 M_server_connection.change_state(real_nick_sent, real_nick_accepted);
00147 send_rejoins();
00148 }
00149 break;
00150 }
00151 case MODE:
00152 if (msg.param(0) == nicks().in_nick())
00153 user_mode().new_server_message_received(msg);
00154 else
00155 fully_processed = false;
00156 break;
00157 case 221:
00158 break;
00159 case 396:
00160 {
00161 server_connection().change_state(login_sent, real_nick_sent);
00162
00163 identity().set_hostname(msg.param(1));
00164 if (!server_connection().secondary_connection())
00165 {
00166 identity().client_session().queue_msg_me_as_is(msg);
00167 nicks().send_nick(identity().client_session().client_in_nick());
00168 }
00169 else
00170 send_rejoins();
00171 break;
00172 }
00173 case JOIN:
00174 {
00175
00176 boost::shared_ptr<Channel> channel(join_pipeline(msg.param(0)));
00177 if (!channel.get())
00178 {
00179
00180 ASSERT(!server_connection().secondary_connection());
00181 fully_processed = false;
00182 break;
00183 }
00184 Target::serverside_add_channel(*this, channel);
00185 if (!server_connection().secondary_connection())
00186 channel->reset_received_names();
00187 else if (M_join_pipeline.empty())
00188 {
00189
00190
00191 identity().swap_primary_and_secondary_connection();
00192 identity().secondary_server_connection()->disconnect();
00193 }
00194 break;
00195 }
00196 default:
00197 fully_processed = false;
00198 break;
00199 }
00200 break;
00201 }
00202 }
00203 if (pre_network_message)
00204 {
00205
00206 Prefix prefix(msg);
00207 MessageOut msgout(default_priority(msg.key()));
00208 identity().client_session().queue_msg(msgout << prefix << msg.key() << identity().client_session().client_out_nick() << msg.params(1));
00209 }
00210 else if (!fully_processed)
00211 {
00212 if (!M_server_connection.secondary_connection())
00213 {
00214
00215 identity().new_server_message_received(msg);
00216 }
00217 #ifdef CWDEBUG
00218 else
00219 Dout(dc::warning, "IGNORING secondary server message: " << msg);
00220 #endif
00221 }
00222
00223
00224 Debug(if (suppress_debug_output) libcw_do.on());
00225 }
00226
00227 boost::shared_ptr<Channel> ServerSession::join_pipeline(std::string const& channel_name)
00228 {
00229 boost::shared_ptr<Channel> result;
00230 join_pipeline_type::iterator iter = M_join_pipeline.find(channel_name);
00231 if (iter != M_join_pipeline.end())
00232 {
00233 result = iter->second;
00234 M_join_pipeline.erase(iter);
00235 }
00236 return result;
00237 }
00238
00239 void ServerSession::send_rejoins(void)
00240 {
00241 DoutEntering(dc::debug, "ServerSession::send_rejoins()");
00242
00243 MessageOut msgout(default_priority(keys::JOIN));
00244 msgout << keys::JOIN;
00245 size_t total_buffer_size = 8;
00246 bool first_channel = true;
00247 bool first_key = true;
00248 std::string keys;
00249
00250 for (int with_keys = 1; with_keys >= 0; --with_keys)
00251 {
00252 for (ClientSession::joined_channels_type::iterator iter = identity().client_session().joined_channels().begin();
00253 iter != identity().client_session().joined_channels().end(); ++iter)
00254 {
00255 boost::shared_ptr<Channel> channel(boost::static_pointer_cast<Channel>(iter->second));
00256 if (with_keys == channel->has_key())
00257 {
00258 for(;;)
00259 {
00260 total_buffer_size +=
00261 (first_channel ? 0 : 1) +
00262 channel->out_name(*this).size() +
00263 (with_keys ? 1 + channel->key().size() : 0);
00264 if (total_buffer_size <= 512)
00265 break;
00266 if (!first_key)
00267 msgout << keys;
00268 queue_msg(msgout);
00269 msgout << keys::JOIN;
00270 total_buffer_size = 8;
00271 first_channel = true;
00272 first_key = true;
00273 }
00274 if (first_channel)
00275 {
00276 msgout << channel;
00277 first_channel = false;
00278 }
00279 else
00280 {
00281 msgout << JustInTimeCatenate(',');
00282 msgout << JustInTimeCatenate(channel);
00283 }
00284 if (with_keys)
00285 {
00286 if (first_key)
00287 {
00288 keys = channel->key();
00289 first_key = false;
00290 }
00291 else
00292 {
00293 keys += ',';
00294 keys += channel->key();
00295 }
00296 }
00297
00298 #if defined(CWDEBUG) || defined(DEBUG)
00299 std::pair<join_pipeline_type::iterator, bool> res =
00300 #endif
00301 M_join_pipeline.insert(join_pipeline_type::value_type(channel->out_name(*this), channel));
00302 ASSERT(res.second);
00303 }
00304 }
00305 }
00306
00307 if (!first_key)
00308 msgout << keys;
00309 if (!first_channel)
00310 queue_msg(msgout);
00311 }
00312
00313 void ServerSession::fatal_error(char const* error_msg)
00314 {
00315 if (!must_be_removed())
00316 *this << "QUIT :Fatal error: " << error_msg << "\r\n";
00317
00318
00319 del();
00320 }
00321
00322 void ServerSession::start_authentication(void)
00323 {
00324 M_auth_state.reset(NEW(AuthState(M_server_connection)));
00325 }
00326
00327 void ServerSession::authentication_successful(void)
00328 {
00329 M_auth_state.reset();
00330 }
00331
00332 int ServerSession::S_server_sessions = 0;
00333
00334 ServerSession::~ServerSession()
00335 {
00336 #ifdef CWDEBUG
00337 debug::SecondaryConnection dummy(M_server_connection.secondary_connection());
00338 #endif
00339 Dout(dc::objects, "Destructing ServerSession" << *this);
00340 M_server_connection.session_lost();
00341 cancel_all_requests();
00342
00343 if (--S_server_sessions == 0 && !M_server_connection.reconnect())
00344 Application::instance().last_server_session_destroyed();
00345 }
00346
00347 void ServerSession::send_who_write_event(PseudoMessageEventType const& event_type, WhoRequest who_request)
00348 {
00349 ASSERT(!debug::secondary_connection);
00350 DoutEntering(dc::debug, "ServerSession::send_who_write_event((PseudoMessageEventType&){" << (void*)&event_type << "), " << who_request << ")");
00351
00352 std::string options(who_request.options().str());
00353 bool has_options = !options.empty();
00354 if (who_request.mask_contains_space())
00355 {
00356 if (has_options)
00357 *this << "WHO dummy";
00358 else
00359 *this << "WHO :" << who_request.mask();
00360 }
00361 else if (who_request.mask_is_target())
00362 *this << "WHO " << who_request.mask_target()->out_name(*this);
00363 else
00364 *this << "WHO " << who_request.mask();
00365 if (has_options)
00366 {
00367 *this << ' ' << options;
00368 if (who_request.mask_contains_space())
00369 {
00370 *this << " :";
00371 if (who_request.mask_is_target())
00372 *this << who_request.mask_target()->out_name(*this);
00373 else
00374 *this << who_request.mask();
00375 }
00376 }
00377 *this << "\r\n";
00378 Dout(dc::debug, "Pushing " << who_request << " onto M_who_queue.");
00379 M_who_queue.push(who_request);
00380 }
00381
00382 void ServerSession::queue_msg_as_is(ClientMessageIn const& msg)
00383 {
00384 Prefix prefix(msg);
00385 MessageOut msgout(default_priority(msg.key()));
00386 queue_msg(msgout << prefix << msg.key() << msg.params(0));
00387 }
00388
00389 void ServerSession::found_network_name(void)
00390 {
00391 Dout(dc::notice, "network = \"" << M_network_name << "\".");
00392 if (identity().network_name() != unknown_network_name && identity().network_name() != M_network_name)
00393 {
00394 Dout(dc::warning, "Identity " << identity().key() << " was connected to network " << identity().network_name() <<
00395 " but now tries to connect to network " << M_network_name << '!');
00396 std::string notice("Detected a possible network switch (from ");
00397 notice += identity().network_name();
00398 notice += " to ";
00399 notice += M_network_name;
00400 notice += "). Bailing out!";
00401 identity().send_notice(notice, server_port_question);
00402 M_server_connection.disconnect();
00403 return;
00404 }
00405 identity().send_notice("Successfully connected.", server_port_question);
00406 identity().set_network_name(M_network_name);
00407
00408
00409 networks_type& networks = Application::instance().networks();
00410 networks_type::iterator iter = networks.begin();
00411 while (iter != networks.end())
00412 {
00413 if (IRCStringCompare(iter->name(), M_network_name) == 0)
00414 break;
00415 ++iter;
00416 }
00417 if (iter == networks.end())
00418 {
00419 std::string server_name = M_server_connection.server()->get_irc_name();
00420 std::string::size_type pos = server_name.rfind('.');
00421 ASSERT(pos > 0);
00422 pos = server_name.rfind('.', pos - 1);
00423 if (pos != std::string::npos)
00424 {
00425 std::string domain = server_name.substr(pos + 1);
00426 std::transform(domain.begin(), domain.end(), domain.begin(), ToLower);
00427 std::ostringstream the_question;
00428 the_question << "Does the " << M_network_name << " IRC network use \"" <<
00429 domain << "\" as common domain for all servers (type: Yes|No)?";
00430 identity().client_session().user_answer_event_server
00431 (UserAnswerRequestData(identity().client_session(), network_domain_question, the_question.str()),
00432 identity(), &Identity::received_network_domain_answer,
00433 std::pair<std::string, std::string>(M_network_name, domain));
00434 }
00435 else
00436 construct_network_object(M_network_name, "");
00437 }
00438 else
00439 detected_network(iter);
00440 }
00441
00442 void ServerSession::construct_network_object(std::string const& network_name, std::string const& domain)
00443 {
00444 networks_type& networks = Application::instance().networks();
00445 networks_type::iterator iter;
00446 std::ostringstream buf;
00447 buf << "Added NEW network: \"" << network_name << "\"";
00448 if (domain.empty())
00449 {
00450 iter = networks.insert(networks.end(), Network(network_name));
00451 buf << '.';
00452 }
00453 else
00454 {
00455 iter = networks.insert(networks.end(), Network(network_name, domain));
00456 buf << ", with common domain \"" << domain << "\".";
00457 }
00458 identity().send_notice(buf.str(), network_domain_question);
00459 detected_network(iter);
00460 }
00461
00462 void ServerSession::detected_network(networks_type::iterator network_iter)
00463 {
00464 DoutEntering(dc::debug, "Identity::detected_network(" << *network_iter << ")");
00465
00466 boost::shared_ptr<Server> current_server(M_server_connection.server());
00467
00468 Dout(dc::notice, "Current server is " << *current_server.get());
00469
00470
00471 ASSERT(is_open());
00472 ASSERT(remote_port() == current_server->port());
00473 current_server->add_port(current_server->port());
00474 Dout(dc::notice, "Added remote port " << current_server->port() << " to server " << *current_server.get());
00475
00476 Dout(dc::debug, "*network_iter is now: " << *network_iter);
00477
00478
00479 current_server->set_network(&(*network_iter));
00480
00481 if (!M_server_connection.secondary_connection())
00482 {
00483
00484 identity().mynick()->set_network(&(*network_iter));
00485
00486 Dout(dc::maps, "Inserting my nick \"" << identity().mynick()->in_name(*this) << "\" in Network map.");
00487 network_iter->targets().insert(target_lookup_map_type::value_type(identity().mynick()->in_name(*this), identity().mynick()));
00488 ASSERT(network_iter->targets().find(identity().mynick()->in_name(*this)) != network_iter->targets().end());
00489 }
00490
00491
00492 std::string server_name = current_server->get_irc_name();
00493 Network::servers_type::iterator server = network_iter->servers().begin();
00494 while (server != network_iter->servers().end())
00495 {
00496 if (IRCStringCompare(server_name, (*server)->get_irc_name()) == 0)
00497 break;
00498 ++server;
00499 }
00500 if (server == network_iter->servers().end())
00501 network_iter->servers().push_back(current_server);
00502
00503 M_detected_network = true;
00504
00505
00506 std::string prefix;
00507 if (!M_i_support.get("PREFIX", prefix))
00508 {
00509 prefix = "(ov)@+";
00510 Dout(dc::warning, "No PREFIX ISUPPORT. Using " << prefix);
00511 }
00512 network_iter->init_who_flag_table(M_umodes, prefix);
00513 }
00514
00515 void ServerSession::force_quit(timer_event_type_ct const& LIBCW_UNUSED_UNLESS_DEBUG(expired_at))
00516 {
00517 DoutEntering(dc::debug, "ServerSession::force_quit(" << expired_at.get_expire_time() << ")");
00518 M_server_connection.disconnect();
00519 }
00520
00521 void ServerSession::network_connection_terminated(void)
00522 {
00523 #ifdef CWDEBUG
00524 debug::SecondaryConnection dummy(M_server_connection.secondary_connection());
00525 #endif
00526 DoutEntering(dc::debug, "ServerSession::network_connection_terminated()");
00527
00528 std::string disconnect_reason;
00529 if (M_disconnect_reason.empty())
00530 disconnect_reason = "EOF from server";
00531 else
00532 disconnect_reason = M_disconnect_reason;
00533
00534 identity().send_notice("Lost connection with " + M_server_connection.server()->get_irc_name() + ": " + disconnect_reason);
00535 }
00536
00537 boost::shared_ptr<Nick> ServerSession::get_nick(std::string const& nick_name, bool create)
00538 {
00539 ASSERT(is_nick(nick_name));
00540
00541 boost::shared_ptr<Nick> new_nick;
00542
00543 Network& network(identity().server_connection().network());
00544 ASSERT(!network.is_private());
00545
00546 target_lookup_map_type::iterator network_iter = network.targets().find(nick_name);
00547 if (network_iter != network.targets().end())
00548 new_nick = boost::static_pointer_cast<Nick>(network_iter->second.lock());
00549 else if (create)
00550 {
00551 Nick* nick = new Nick(*this, nick_name);
00552 AllocTag(nick, nick_name);
00553 new_nick.reset(nick);
00554 Dout(dc::maps, "Inserting " << nick_name << " in Network map.");
00555 #if defined(CWDEBUG) || defined(DEBUG)
00556 std::pair<target_lookup_map_type::iterator, bool> res =
00557 #endif
00558 network.targets().insert(target_lookup_map_type::value_type(nick_name, new_nick));
00559 ASSERT(res.second);
00560 }
00561 else
00562 THROW_EXCEPTION(unknown_target(), "The identity " << identity().key() << " has no server side nick \"" << nick_name << "\"");
00563
00564 return new_nick;
00565 }
00566