00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include "session.hh"
00034 #include "XPath.h"
00035 #include <sigc++/object_slot.h>
00036 #include <sha.h>
00037 using namespace SigC;
00038 namespace jabberoo {
00039
00040
00041
00042 Session::Session()
00043 : ElementStream(this),
00044 _ID(0),
00045 _ConnState(csNotConnected),
00046 _StreamStart(false),
00047 _Roster(*this),
00048 _DDB(*this),
00049 _PDB(*this)
00050 {}
00051
00052 Session::~Session()
00053 {
00054
00055 if (_ConnState != csNotConnected)
00056 {
00057 std::cerr << "Disconnecting in Jabberoo-Session" << std::endl;
00058 disconnect();
00059 }
00060
00061 for (XPCBMap::iterator it = _incoming_XPaths.begin();
00062 it != _incoming_XPaths.end(); ++it)
00063 {
00064 delete it->first;
00065 }
00066 for (XPCBMap::iterator it = _outgoing_XPaths.begin();
00067 it != _outgoing_XPaths.end(); ++it)
00068 {
00069 delete it->first;
00070 }
00071 }
00072
00073
00074
00075
00076 Session& Session::operator<<(const Packet& p)
00077 {
00078 const judo::Element& elem(p.getBaseElement());
00079
00080 for (XPQueryList::iterator it = _outgoing_queries.begin();
00081 it != _outgoing_queries.end(); ++it)
00082 {
00083 if ( (*it)->check(elem) )
00084 {
00085 _outgoing_XPaths[(*it)](elem);
00086 }
00087 }
00088
00089 if (elem.getName() == "presence")
00090 {
00091
00092 Presence pres = Presence(elem);
00093 if (pres.getFrom().empty() && pres.getTo().empty())
00094 evtMyPresence(pres);
00095 }
00096 evtTransmitPacket(p);
00097 evtTransmitXML(p.toString().c_str());
00098 return *this;
00099 }
00100
00101
00102
00103
00104 void Session::connect(const std::string& server, AuthType atype,
00105 const std::string& username, const std::string& resource, const std::string& password,
00106 bool newuser,
00107 bool should_auth)
00108 {
00109
00110 if (_ConnState == csConnected)
00111 return;
00112
00113
00114 _AuthType = atype;
00115 _ServerID = server;
00116 _Username = username;
00117 _Resource = resource;
00118 _Password = password;
00119 _Authenticate = should_auth;
00120
00121
00122 if (_ConnState == csNotConnected)
00123 {
00124 this->reset();
00125
00126 *this << "<stream:stream to='" << server.c_str()
00127 << "' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
00128
00129 if (!newuser)
00130 _ConnState = csAuthReq;
00131 else
00132 _ConnState = csCreateUser;
00133 }
00134
00135 else
00136 {
00137 if (newuser)
00138 _ConnState = csCreateUser;
00139 if (should_auth)
00140 authenticate();
00141 else
00142 evtConnected(*_StreamElement);
00143 }
00144 }
00145
00146 bool Session::disconnect()
00147 {
00148 bool success = false;
00149
00150
00151 if ((_ConnState != csNotConnected) && _StreamStart)
00152 {
00153 *this << "</stream:stream>";
00154 success = true;
00155 }
00156 _ConnState = csNotConnected;
00157 _StreamStart = false;
00158
00159 return success;
00160 }
00161
00162
00163
00164
00165 const Roster& Session::roster() const
00166 {
00167 return _Roster;
00168 }
00169
00170 Roster& Session::roster()
00171 {
00172 return _Roster;
00173 }
00174
00175 const DiscoDB& Session::discoDB() const
00176 {
00177 return _DDB;
00178 }
00179
00180 DiscoDB& Session::discoDB()
00181 {
00182 return _DDB;
00183 }
00184
00185 const PresenceDB& Session::presenceDB() const
00186 {
00187 return _PDB;
00188 }
00189
00190 PresenceDB& Session::presenceDB()
00191 {
00192 return _PDB;
00193 }
00194
00195 Session::AuthType Session::getAuthType() const
00196 {
00197 return _AuthType;
00198 }
00199
00200 const std::string& Session::getUserName() const
00201 {
00202 return _Username;
00203 }
00204
00205
00206
00207
00208 std::string Session::getNextID()
00209 {
00210 char buf[8];
00211 _snprintf(buf, 8, "j%ld", _ID++);
00212 return std::string(buf);
00213 }
00214
00215 std::string Session::getDigest()
00216 {
00217 std::string basic = _SessionID + _Password;
00218 return shahash(basic.c_str());
00219 }
00220
00221
00222
00223
00224 void Session::push(const char* data, int datasz)
00225 {
00226 evtRecvXML(data);
00227 try {
00228 ElementStream::push(data, datasz);
00229 } catch (const ElementStream::exception::ParserError& error) {
00230
00231 evtXMLParserError(error.getCode(), error.getMessage());
00232
00233
00234 _ConnState = csNotConnected;
00235 _StreamStart = false;
00236
00237 disconnect();
00238 }
00239 }
00240
00241 void Session::registerIQ(const std::string& id, ElementCallbackFunc f)
00242 {
00243 _Callbacks.insert(std::make_pair(id, f));
00244 }
00245
00246 judo::XPath::Query* Session::registerXPath(const std::string& query,
00247 ElementCallbackFunc f, bool incoming)
00248 {
00249 judo::XPath::Query* xpq = new judo::XPath::Query(query);
00250 if (incoming)
00251 {
00252 _incoming_XPaths.insert(XPCBMap::value_type(xpq, f));
00253 _incoming_queries.push_front(xpq);
00254 }
00255 else
00256 {
00257 _outgoing_XPaths.insert(XPCBMap::value_type(xpq, f));
00258 _outgoing_queries.push_front(xpq);
00259 }
00260
00261 return xpq;
00262 }
00263
00264 void Session::unregisterXPath(judo::XPath::Query* id, bool incoming)
00265 {
00266 if (incoming)
00267 {
00268 _incoming_XPaths.erase(id);
00269 _incoming_queries.erase(std::find(_incoming_queries.begin(),
00270 _incoming_queries.end(), id));
00271 }
00272 else
00273 {
00274 _outgoing_XPaths.erase(id);
00275 _outgoing_queries.erase(std::find(_outgoing_queries.begin(),
00276 _outgoing_queries.end(), id));
00277 }
00278
00279 delete id;
00280 }
00281
00282 void Session::queryNamespace(const std::string& nspace, ElementCallbackFunc f, const std::string& to)
00283 {
00284
00285 std::string id = getNextID();
00286
00287
00288 judo::Element iq("iq");
00289 iq.putAttrib("type", "get");
00290 iq.putAttrib("id", id);
00291 if (!to.empty())
00292 iq.putAttrib("to", to);
00293 iq.addElement("query")->putAttrib("xmlns", nspace);
00294
00295
00296 _Callbacks.insert(std::make_pair(id, f));
00297
00298
00299 *this << iq.toString().c_str();
00300 }
00301
00302
00303
00304
00305
00306
00307 void Session::authenticate()
00308 {
00309
00310 std::string id = getNextID();
00311 judo::Element iq("iq");
00312 iq.putAttrib("type", "get");
00313 iq.putAttrib("id", id);
00314 judo::Element* query = iq.addElement("query");
00315 query->putAttrib("xmlns", "jabber:iq:auth");
00316 query->addElement("username", _Username, false);
00317 *this << iq.toString().c_str();
00318
00319
00320
00321 if (_AuthType != Session::atAutoAuth)
00322 sendLogin(_AuthType, NULL);
00323 else
00324 registerIQ(id, SigC::slot(*this, &Session::OnAuthTypeReceived));
00325 }
00326
00327 void Session::OnAuthTypeReceived(const judo::Element& t)
00328 {
00329
00330 Session::AuthType atype = Session::atPlaintextAuth;
00331 const judo::Element* query = NULL;
00332 if (!t.empty())
00333 {
00334 query = t.findElement("query");
00335 if (query != NULL)
00336 {
00337
00338 const judo::Element* password = query->findElement("password");
00339 if (password != NULL)
00340 atype = Session::atPlaintextAuth;
00341
00342 const judo::Element* digest = query->findElement("digest");
00343 if (digest != NULL)
00344 atype = Session::atDigestAuth;
00345
00346 const judo::Element* token = query->findElement("token");
00347 const judo::Element* sequence = query->findElement("sequence");
00348 if (token != NULL && sequence != NULL)
00349 atype = Session::at0kAuth;
00350 }
00351 }
00352 sendLogin(atype, query);
00353 }
00354
00355 void Session::sendLogin(Session::AuthType atype, const judo::Element* squery)
00356 {
00357
00358 std::string id = getNextID();
00359
00360
00361 judo::Element iq("iq");
00362 iq.putAttrib("type", "set");
00363 iq.putAttrib("id", id);
00364
00365
00366 judo::Element* query = iq.addElement("query");
00367
00368
00369 query->addElement("username", _Username);
00370 query->addElement("resource", _Resource);
00371
00372
00373 if (_ConnState == csCreateUser)
00374 {
00375 query->putAttrib("xmlns", "jabber:iq:register");
00376 query->addElement("password", _Password);
00377
00378 registerIQ(id, slot(*this, &Session::IQHandler_CreateUser));
00379
00380 }
00381 else if (_ConnState == csAuthReq)
00382 {
00383 query->putAttrib("xmlns", "jabber:iq:auth");
00384
00385 switch (atype)
00386 {
00387 case atDigestAuth:
00388 query->addElement("digest", getDigest());
00389 break;
00390 case atPlaintextAuth:
00391 query->addElement("password", _Password);
00392 break;
00393 case at0kAuth:
00394 std::string hashA = shahash(_Password.c_str());
00395 std::string token = squery->getChildCData("token");
00396 int seq = atoi(squery->getChildCData("sequence").c_str());
00397 std::string hash = shahash(std::string(hashA + token).c_str());
00398 for (int i = 0; i < seq; i++)
00399 hash = shahash(hash.c_str());
00400 query->addElement("hash", hash);
00401 break;
00402 }
00403
00404 registerIQ(id, slot(*this, &Session::IQHandler_Auth));
00405 }
00406
00407
00408 *this << iq.toString().c_str();
00409
00410
00411 _ConnState = csAwaitingAuth;
00412 }
00413
00414
00415
00416
00417
00418
00419 void Session::onDocumentStart(judo::Element* t)
00420 {
00421
00422 _SessionID = t->getAttrib("id");
00423
00424 _StreamElement = t;
00425 _StreamStart = true;
00426
00427 if (_Authenticate)
00428 authenticate();
00429 else
00430 evtConnected(*_StreamElement);
00431 }
00432
00433 void Session::onElement(judo::Element* t)
00434 {
00435 judo::Element& tref = *t;
00436
00437 for (XPQueryList::iterator it = _incoming_queries.begin();
00438 it != _incoming_queries.end(); ++it)
00439 {
00440 if ( (*it)->check(tref) )
00441 {
00442 _incoming_XPaths[(*it)](tref);
00443 }
00444 }
00445
00446
00447
00448 if (tref.getName() == "message")
00449 handleMessage(tref);
00450 else if (tref.getName() == "presence")
00451 handlePresence(tref);
00452 else if (tref.getName() == "iq")
00453 handleIQ(tref);
00454 else
00455 evtUnknownPacket(tref);
00456 delete t;
00457 }
00458
00459 void Session::onCDATA(judo::CDATA* c)
00460 {
00461
00462
00463
00464 delete c;
00465 }
00466
00467 void Session::onDocumentEnd()
00468 {
00469
00470 _Roster.reset();
00471
00472 _PDB.clear();
00473
00474
00475 _ConnState = csNotConnected;
00476 _StreamStart = false;
00477
00478
00479 *this << "</stream:stream>";
00480
00481
00482 evtDisconnected();
00483 }
00484
00485
00486
00487
00488
00489
00490 void Session::handleMessage(judo::Element& t)
00491 {
00492
00493 evtMessage(Message(t));
00494 }
00495
00496 void Session::handlePresence(judo::Element& t)
00497 {
00498 Presence p(t);
00499
00500
00501 if (p.getFrom() == "")
00502 {
00503 evtMyPresence(p);
00504 }
00505
00506
00507 else if ((p.getType() == Presence::ptSubRequest) ||
00508 (p.getType() == Presence::ptUnsubRequest))
00509 evtPresenceRequest(p);
00510
00511
00512 else
00513 {
00514
00515 Presence::Type prev_type;
00516 try {
00517 prev_type = _PDB.find(p.getFrom())->getType();
00518 } catch (PresenceDB::XCP_InvalidJID) {
00519 prev_type = Presence::ptUnavailable;
00520 }
00521
00522
00523 _PDB.insert(p);
00524
00525
00526 if (_Roster.containsJID(p.getFrom()))
00527 _Roster.update(p, prev_type);
00528
00529 evtPresence(p, prev_type);
00530 }
00531 }
00532
00533 void Session::handleIQ(judo::Element& t)
00534 {
00535
00536 typedef std::multimap<std::string, ElementCallbackFunc>::iterator CIT;
00537 std::pair<CIT, CIT> cb = _Callbacks.equal_range(t.getAttrib("id"));
00538 if (cb.first != cb.second)
00539 {
00540
00541 for (CIT it = cb.first; it != cb.second; )
00542 {
00543
00544
00545
00546 if (it->first == t.getAttrib("id")) {
00547
00548 ElementCallbackFunc& f = it->second;
00549 f(t);
00550
00551
00552 CIT nit = it++;
00553 _Callbacks.erase(nit);
00554 } else
00555 it++;
00556 }
00557 }
00558
00559 else
00560 {
00561 judo::Element* q = t.findElement("query");
00562
00563
00564 if (q == NULL) return;
00565
00566
00567 if (q->cmpAttrib("xmlns", "jabber:iq:roster"))
00568 {
00569 _Roster.update(*q);
00570 evtOnRoster();
00571 }
00572
00573
00574 if (t.cmpAttrib("type", "get") && q->cmpAttrib("xmlns", "jabber:iq:version"))
00575 {
00576 std::string name, ver, os;
00577 evtOnVersion(name, ver, os);
00578 ver += " (powered by jabberoo 1.1.2)";
00579 Packet iq("iq");
00580 iq.getBaseElement().putAttrib("type", "result");
00581 iq.setTo(t.getAttrib("from"));
00582 iq.setID(t.getAttrib("id"));
00583 judo::Element* query = iq.getBaseElement().addElement("query");
00584 query->putAttrib("xmlns", "jabber:iq:version");
00585 query->addElement("name", name);
00586 query->addElement("version", ver);
00587 query->addElement("os", os);
00588 *this << iq;
00589 }
00590
00591 else if (t.cmpAttrib("type", "get") && q->cmpAttrib("xmlns", "jabber:iq:last"))
00592 {
00593 std::string seconds;
00594 evtOnLast(seconds);
00595 Packet iq("iq");
00596 iq.getBaseElement().putAttrib("type", "result");
00597 iq.setTo(t.getAttrib("from"));
00598 iq.setID(t.getAttrib("id"));
00599 judo::Element* query = iq.getBaseElement().addElement("query");
00600 query->putAttrib("xmlns", "jabber:iq:last");
00601 query->putAttrib("seconds", seconds);
00602 *this << iq;
00603 }
00604
00605 else if (t.cmpAttrib("type", "get") && q->cmpAttrib("xmlns", "jabber:iq:time"))
00606 {
00607 #ifndef WIN32
00608 Packet iq("iq");
00609 iq.getBaseElement().putAttrib("type", "result");
00610 iq.setTo(t.getAttrib("from"));
00611 iq.setID(t.getAttrib("id"));
00612 judo::Element* query = iq.getBaseElement().addElement("query");
00613 query->putAttrib("xmlns", "jabber:iq:time");
00614 query->addElement("utc", jutil::getTimeStamp());
00615
00616 struct tm *loctime;
00617 time_t curtime;
00618 char timestr[1024];
00619
00620 curtime = time(0);
00621 loctime = localtime(&curtime);
00622
00623
00624
00625 strftime(timestr, 1024, "%c", loctime);
00626 std::string local= std::string(timestr);
00627 strftime(timestr, 1024, "%Z", loctime);
00628 std::string tz = std::string(timestr);
00629 if (local.find(tz)!=std::string::npos)
00630 local.erase(local.find(tz));
00631 evtOnTime(local, tz);
00632
00633 query->addElement("display", local);
00634 query->addElement("tz", tz);
00635 *this << iq;
00636 #endif
00637 }
00638
00639 else
00640 evtIQ(t);
00641 }
00642 }
00643
00644
00645
00646
00647
00648
00649
00650 void Session::IQHandler_Auth(const judo::Element& t)
00651 {
00652
00653
00654 if (t.cmpAttrib("type", "result"))
00655 {
00656 _ConnState = Session::csConnected;
00657 evtConnected(*_StreamElement);
00658 if (_Authenticate)
00659 _Roster.fetch();
00660 delete _StreamElement;
00661 }
00662 else
00663 {
00664
00665 const judo::Element* error = t.findElement("error");
00666 if (error != NULL)
00667 {
00668 _ConnState = Session::csAuthReq;
00669 evtAuthError(atoi(error->getAttrib("code").c_str()), error->getCDATA().c_str());
00670 }
00671 else
00672 evtAuthError(-1, t.toString().c_str());
00673 }
00674 }
00675
00676 void Session::IQHandler_CreateUser(const judo::Element& t)
00677 {
00678
00679
00680 _ConnState = Session::csAuthReq;
00681 if (t.cmpAttrib("type", "result"))
00682 authenticate();
00683 else
00684 {
00685 const judo::Element* error = t.findElement("error");
00686 if (error != NULL)
00687 {
00688 evtAuthError(atoi(error->getAttrib("code").c_str()), error->getCDATA().c_str());
00689 }
00690 }
00691 }
00692
00693 }