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 #include <arpa/inet.h>
00020 #include <algorithm>
00021 #endif
00022
00023 #include "ServerConnection.h"
00024 #include "Identity.h"
00025 #include "Server.h"
00026 #include "UserAnswer.h"
00027 #include "ClientSession.h"
00028 #include "keys.h"
00029 #include "Authentication.h"
00030 #include "Expression.h"
00031 #include "Matcher.h"
00032 #include "Bool.inl"
00033 #include "Application.h"
00034
00035
00036
00037
00038
00039 struct ConnectionOrder {
00040 bool operator()(boost::shared_ptr<Server> const& server1, boost::shared_ptr<Server> const& server2)
00041 {
00042 timeval const now = timerRequest.get_now();
00043 timeval const ten_minutes = { 600, 0 };
00044 timeval ten_minutes_ago;
00045 timersub(&now, &ten_minutes, &ten_minutes_ago);
00046 bool server1_more_than_10_minutes_ago = (timercmp(&server1->last_connection_event(), &ten_minutes_ago, <));
00047 bool server2_more_than_10_minutes_ago = (timercmp(&server2->last_connection_event(), &ten_minutes_ago, <));
00048 bool server1_longer_ago_than_server2 = (timercmp(&server1->last_connection_event(), &server2->last_connection_event(), <));
00049 if (server1_more_than_10_minutes_ago != server2_more_than_10_minutes_ago)
00050 return server1_more_than_10_minutes_ago;
00051
00052 bool different_connection_event = (timercmp(&server1->last_connection_event(), &server2->last_connection_event(), !=));
00053 return different_connection_event && server1_longer_ago_than_server2 != server1_more_than_10_minutes_ago;
00054 }
00055 };
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073 void ServerConnection::connect(bool auto_reconnect)
00074 {
00075 DoutEntering(dc::debug, "ServerConnection::connect() for " << *this);
00076 ASSERT(!is_connected());
00077 ASSERT(M_state == disconnected);
00078 if (!M_server_session)
00079
00080
00081 {
00082 M_server_session = new ServerSession(identity(), *this);
00083 AllocTag(M_server_session, (M_secondary_connection ? "Secondary Server session of " : "Server session of ") << identity().key());
00084 }
00085 if (auto_reconnect)
00086 {
00087
00088 for (networks_type::iterator network_iter = Application::instance().networks().begin(); network_iter != Application::instance().networks().end(); ++network_iter)
00089 if (network_iter->name() == identity().network_name())
00090 {
00091 if (has_server())
00092 {
00093 if (!M_timed_out_count)
00094 {
00095 M_timed_out_count = 3 * network_iter->servers().size();
00096 ASSERT(M_timed_out_count != 0);
00097 }
00098 if (M_timed_out)
00099 --M_timed_out_count;
00100 }
00101 if (network_iter->servers().empty() || (M_timed_out && !M_timed_out_count))
00102 M_server.reset();
00103 else
00104 {
00105 std::stable_sort(network_iter->servers().begin(), network_iter->servers().end(), ConnectionOrder());
00106 M_server = network_iter->servers().front();
00107 }
00108 break;
00109 }
00110 }
00111 M_reconnect = true;
00112 if (has_server())
00113 {
00114 timeval next_connect;
00115 timeradd(&M_server->last_connection_event(), &M_connect_delay, &next_connect);
00116 timeval now = timerRequest.get_now();
00117 if (timercmp(&now, &next_connect, >))
00118 do_connect();
00119 else
00120 timerRequest(M_connect_delay, *this, &ServerConnection::delayed_connect);
00121 }
00122 else if (identity().has_client_session())
00123 {
00124 ASSERT(!M_secondary_connection);
00125 identity().client_session().user_answer_event_server
00126 (UserAnswerRequestData(identity().client_session(), server_port_question,
00127 "What server should I connect to (type: <server> [<port> [<localip>]])?"),
00128 *this, &ServerConnection::received_server_port_answer);
00129 }
00130 else
00131 disconnect();
00132 }
00133
00134
00135
00136
00137
00138 void ServerConnection::delayed_connect(timer_event_type_ct const& LIBCW_UNUSED_UNLESS_DEBUG(expired_at))
00139 {
00140 DoutEntering(dc::debug, "ServerConnection::delayed_connect(" << expired_at.get_expire_time() << ")");
00141 if (!is_connected() && M_reconnect && M_server_session && has_server())
00142 do_connect();
00143 }
00144
00145
00146
00147
00148
00149 void ServerConnection::do_connect(void)
00150 {
00151 DoutEntering(dc::debug, "ServerConnection::do_connect()");
00152 ASSERT(M_state == disconnected);
00153
00154 if (identity().has_client_session())
00155 {
00156 std::ostringstream buf;
00157 buf << "Trying to connect to \"" << M_server->hostname().c_str() << "\" (port " << M_server->next_port() << ").";
00158 identity().send_notice(buf.str(), server_port_question);
00159 }
00160
00161
00162 M_timed_out = false;
00163
00164 timeval delay = { 4, 0 };
00165 timerRequest(delay, *this, &ServerConnection::connect_timed_out);
00166
00167 ASSERT(M_server->next_port() != 0);
00168 if (M_server_session->connect(M_server->hostname().c_str(), M_server->next_port(), M_server->vhost()))
00169 {
00170 M_server->attempting_to_connect();
00171 M_connected = true;
00172 M_server_session->start_authentication();
00173 M_state = authentication_started;
00174 }
00175 else
00176 {
00177 ASSERT(!M_secondary_connection);
00178
00179 M_server.reset();
00180 if (identity().has_client_session())
00181 identity().client_session().del_private_target(question_target_name(server_port_question));
00182
00183 connect(true);
00184 }
00185 }
00186
00187 void ServerConnection::connect_timed_out(timer_event_type_ct const& LIBCW_UNUSED_UNLESS_DEBUG(expired_at))
00188 {
00189 if (M_state > authentication_started)
00190 return;
00191
00192 DoutEntering(dc::debug, "ServerConnection::connect_timed_out(" << expired_at.get_expire_time() << ")");
00193 M_server_session->close();
00194 M_timed_out = true;
00195 }
00196
00197
00198
00199
00200
00201 void ServerConnection::received_server_port_answer(UserAnswerEventType const& event_type)
00202 {
00203 DoutEntering(dc::debug, "ServerConnection::received_server_port_answer(" << event_type << ")");
00204 std::string answer = event_type.answer();
00205 std::string hostname;
00206 unsigned int port = 6667;
00207 struct in_addr vhost = { INADDR_ANY };
00208
00209 unsigned int count = 0;
00210 std::stringstream ss(answer);
00211 std::string buf;
00212 while ((ss >> buf))
00213 {
00214 if (count == 0)
00215 hostname = buf;
00216 else if (count == 1)
00217 port = atoi(buf.c_str());
00218 else if (count == 2)
00219 inet_aton(buf.c_str(), &vhost);
00220 ++count;
00221 }
00222
00223
00224 bool found = false;
00225 for (networks_type::iterator network_iter = Application::instance().networks().begin(); network_iter != Application::instance().networks().end(); ++network_iter)
00226 {
00227
00228 if (has_server() && server()->network().name() != network_iter->name())
00229 continue;
00230 for (Network::servers_type::iterator server_iter = network_iter->servers().begin(); server_iter != network_iter->servers().end(); ++server_iter)
00231 if (strcasecmp((*server_iter)->hostname().c_str(), hostname.c_str()) == 0 && memcmp(&(*server_iter)->vhost(), &vhost, sizeof(vhost)) == 0)
00232 {
00233 Dout(dc::notice, "Found existing server " << *server_iter);
00234 found = true;
00235 M_server = *server_iter;
00236 M_server->set_port(port);
00237 break;
00238 }
00239 if (found)
00240 break;
00241 }
00242 if (!found)
00243 M_server.reset(NEW(Server(identity().private_network(), hostname, port, vhost)));
00244 M_timed_out = false;
00245 connect(false);
00246 }
00247
00248
00249
00250
00251
00252 void ServerConnection::session_lost(void)
00253 {
00254 if (M_state != disconnected)
00255 {
00256
00257 M_server->connection_lost();
00258
00259 if (!M_secondary_connection && identity().has_client_session())
00260 identity().client_session().serversession_lost();
00261 }
00262 M_connected = false;
00263 M_state = disconnected;
00264 M_server_session = NULL;
00265 if (M_secondary_connection)
00266 {
00267 M_reconnect = false;
00268 identity().destroy_secondary_server_connection();
00269 }
00270 if (M_reconnect)
00271 connect(true);
00272 }
00273
00274
00275 timeval ServerConnection::S_connect_delay_init = { 1, 0 };
00276
00277
00278
00279
00280
00281 void ServerConnection::error_message_received(ServerMessageIn const& msg)
00282 {
00283 DoutEntering(dc::notice, "ServerConnection::error_message_received(" << msg << ")");
00284
00285 server_session().set_disconnect_reason(msg.param(0));
00286
00287 if (M_state < successful_connect)
00288 {
00289
00290
00291 M_connect_delay.tv_sec *= 2;
00292 if (M_connect_delay.tv_sec > 600)
00293 M_connect_delay.tv_sec = 600;
00294 Dout(dc::notice, "Incrementing connect delay to " << M_connect_delay);
00295 }
00296 }
00297
00298
00299
00300 void ServerConnection::successful_connection(void)
00301 {
00302 ASSERT(M_state == authentication_started || M_state == successful_connect);
00303 M_state = successful_connect;
00304 M_connect_delay = S_connect_delay_init;
00305 M_timed_out_count = 0;
00306 }
00307
00308
00309
00310
00311
00312 void ServerConnection::decode_ping(ServerMessageIn const& ping_msg)
00313 {
00314 if (M_state >= real_nick_sent)
00315 {
00316 MessageOut msgout(default_priority(keys::PONG));
00317 server_session().queue_msg(msgout << keys::PONG << ping_msg.param(0));
00318 }
00319 else if (M_state >= pong_sent)
00320 *M_server_session << "PONG " << ping_msg.param(0) << "\r\n";
00321 else
00322 {
00323 successful_connection();
00324 M_server_session->set_auth_ping_argument(ping_msg.param(0));
00325 }
00326 }
00327
00328
00329
00330
00331
00332
00333 void ServerConnection::send_pong_and_mode(ExpressionEventType const& LIBCW_UNUSED_UNLESS_DEBUG(event_type))
00334 {
00335 DoutEntering(dc::notice, "ServerConnection::send_pong_and_mode(" << event_type << ")");
00336
00337 ASSERT(M_state == successful_connect);
00338 ASSERT(!M_server_session->auth_ping_argument().empty());
00339
00340 *M_server_session << "PONG " << M_server_session->auth_ping_argument() << "\r\n";
00341 M_state = pong_sent;
00342
00343
00344
00345
00346
00347
00348
00349 char service_mode = 'x';
00350
00351
00352
00353 if (!M_server->version().get_user_supports_umode())
00354 {
00355
00356 *M_server_session << "MODE " << M_server_session->nicks().out_nick() << " +i\r\n";
00357
00358 *M_server_session << "MODE " << M_server_session->nicks().out_nick() << "\r\n";
00359
00360 *M_server_session << "MODE " << M_server_session->nicks().out_nick() << " +" << service_mode << "\r\n";
00361 }
00362 else if (!*M_server->version().get_user_supports_umode())
00363 *M_server_session << "MODE " << M_server_session->nicks().out_nick() << " +i" << service_mode << "\r\n";
00364
00365 ConstExpression<UserModeMask> mask(UserModeMask('i') | service_mode);
00366 ON_TRUE((*M_server_session->user_mode() & mask) == mask && *M_server_session->M_detected_network, *this, &ServerConnection::invisible_event);
00367
00368 MatchRequest match_request;
00369 match_request(keys::MODE).mynick(0);
00370 M_server_session->message_event_server()(match_request, *this, &ServerConnection::received_first_umode);
00371 }
00372
00373
00374
00375
00376
00377 void ServerConnection::invisible_event(ExpressionEventType const& LIBCW_UNUSED_UNLESS_DEBUG(event_type))
00378 {
00379 DoutEntering(dc::notice, "ServerConnection::invisible_event(" << event_type << ')');
00380 ASSERT(event_type);
00381
00382 ASSERT(M_state == pong_sent);
00383 M_state = login_sent;
00384 *M_server_session << "PRIVMSG X@channels.undernet.org :login " <<
00385 identity().key() << ' ' << identity().authentication().service_pass() << "\r\n";
00386 }
00387
00388
00389
00390
00391
00392 void ServerConnection::received_first_umode(MatcherEventType const& event_type)
00393 {
00394 DoutEntering(dc::debug, "ServerConnection::received_first_umode(" << event_type << ")");
00395 MessageIn::Part const& prefix(event_type.message().prefix_part());
00396 char const* p = strchr(prefix.start(), '!');
00397 ASSERT(p);
00398 char const* ident = p + 1;
00399 p = strchr(p, '@');
00400 ASSERT(p);
00401 size_t ident_len = p - ident;
00402 char const* real_hostname = p + 1;
00403 size_t real_hostname_len = prefix.end() - real_hostname;
00404 identity().set_real_hostname(std::string(real_hostname, real_hostname_len));
00405 identity().set_ident(std::string(ident, ident_len));
00406 }
00407
00408 void ServerConnection::quit(std::string const& msg)
00409 {
00410 M_reconnect = false;
00411 if (M_server_session)
00412 {
00413 *M_server_session << "QUIT :" << msg << "\r\n";
00414
00415 timeval delay = { 1, 0 };
00416 timerRequest(delay, *M_server_session, &ServerSession::force_quit);
00417 }
00418 }