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 #include <vos/corelibs/vos/vos.hh>
00026
00027 #include <vos/metaobjects/property/property.hh>
00028 #include "search.hh"
00029
00030 #include <fstream>
00031
00032
00033 Search::Search(MetaObject* s) : MetaObject(s)
00034 {
00035 accessControl = &NoPropertyAccessControl::static_;
00036 }
00037
00038
00039 Search::~Search() {
00040 }
00041
00042
00043 void Search::setPropertyAccessControl(PropertyAccessControl* ac) {
00044 accessControl = ac;
00045 }
00046 PropertyAccessControl* Search::getPropertyAccessControl() {
00047 return accessControl;
00048 }
00049
00050 LocalSearch::LocalSearch(MetaObject* s) : Search(s), MetaObject(s)
00051 {
00052 addMessageHandler<LocalSearch>("misc:search", this, &LocalSearch::handleSearch);
00053 }
00054
00055 LocalSearch::~LocalSearch() {
00056
00057 }
00058
00059
00060
00061 void LocalSearch::initialize(PropertyAccessControl* ac) {
00062 accessControl = ac;
00063 initialize();
00064 }
00065
00066 void LocalSearch::initialize() {
00067 }
00068
00069 RemoteSearch::RemoteSearch(MetaObject* s) : Search(s), MetaObject(s)
00070 {
00071 addUpdateHandler<RemoteSearch>("misc:search-done", this, &RemoteSearch::handleSearchDone);
00072 }
00073
00074 RemoteSearch::~RemoteSearch() {
00075 }
00076
00077
00078
00079 const string Search::getType() {
00080 return string("misc:search");
00081 }
00082
00083
00084 void Search::registerExtenders() {
00085 static bool done = false;
00086 if(! done) {
00087 LocalSite::addLocalObjectExtension(typeid(LocalSearch).name(), &LocalSearch::new_LocalSearch);
00088 LocalSite::addLocalObjectExtension(typeid(Search).name(), &LocalSearch::new_LocalSearch);
00089 LocalSite::addLocalObjectExtension("misc:search", &LocalSearch::new_LocalSearch);
00090 RemoteSite::addRemoteObjectExtension(typeid(RemoteSearch).name(), &RemoteSearch::new_RemoteSearch);
00091 RemoteSite::addRemoteObjectExtension(typeid(Search).name(), &RemoteSearch::new_RemoteSearch);
00092 RemoteSite::addRemoteObjectExtension("misc:search", &RemoteSearch::new_RemoteSearch);
00093 done = true;
00094 }
00095 }
00096
00097
00098
00099 void RemoteSearch::sendUpdateMessage(Message* m) {
00100
00101
00102 Search::sendUpdateMessage(m);
00103
00104 bool mark = false;
00105 for(set<string>::iterator i = searches.begin(); i != searches.end(); i++) {
00106 if(m->getNonce() == (string("acquire-") + (*i))) {
00107 mark = true;
00108 break;
00109 }
00110 }
00111
00112 if(mark) {
00113 try {
00114 if(m->getMethod() == "core:insert-child-update"
00115 || m->getMethod() == "core:set-child-update") {
00116 try {
00117 for(int i=0; i < m->getNumFields(); ++i) {
00118 const Message::Field& name = m->getField(i);
00119 if(name.key != "name") continue;
00120 try {
00121 vRef<Vobject> v = findObject(name.value);
00122 v->addFlag("from-search");
00123 } catch(NoSuchObjectError) { }
00124 }
00125 } catch(Message::NoSuchFieldError) { }
00126 }
00127 } catch(NoSuchObjectError) {
00128 } catch(NoSuchSiteError) {
00129 } catch(URL::BadURLError) {
00130 }
00131 }
00132 }
00133
00134
00135
00136
00137
00138 deque<LocalSearch::PatternRule*> LocalSearch::makeRules(Message* rules)
00139 {
00140 deque<PatternRule*> prules;
00141
00142 int rc = 0, i = 0;
00143 for(; i < rules->getNumFields() && rules->getField(i).key != "typerule"; i++);
00144 for(; i < rules->getNumFields(); i+=3) {
00145 rc++;
00146
00147 const Message::Field& typerule = rules->getField(i);
00148 const Message::Field& childrule = rules->getField(i+1);
00149 const Message::Field& action = rules->getField(i+2);
00150
00151 if(typerule.key != "typerule") {
00152 LOG("search", 2, "missing type rule parmeter");
00153 return prules;
00154 }
00155 if(childrule.key != "childrule") {
00156 LOG("search", 2, "missing child rule parmeter");
00157 return prules;
00158 }
00159 if(action.key != "action") {
00160 LOG("search", 2, "missing action parmeter");
00161 return prules;
00162 }
00163
00164 PatternRule* currule = new PatternRule;
00165
00166 currule->typePatternEmpty = (typerule.value.size() == 0);
00167 LOG("search", 4, "type pattern: " << typerule.value);
00168 regcomp(&currule->typepattern, typerule.value.c_str(), REG_EXTENDED | REG_NOSUB);
00169
00170 if(! (childrule.value.size() == 0
00171 || childrule.value[0] == '/'
00172 || (childrule.value[0] == '!' && childrule.value[1] == '/')))
00173 {
00174 LOG("search", 2, "child rule doesn't start with '/' and isn't blank");
00175 return prules;
00176 }
00177
00178 unsigned int n = 1, c = 1;
00179 if(childrule.value.size() > 0) {
00180 if(childrule.value[0] == '!') {
00181 currule->negateChild = true;
00182 n++;
00183 c++;
00184 } else currule->negateChild = false;
00185
00186 vector<regex_t> childpatsplit;
00187 for(; n < childrule.value.size(); n++) {
00188 if(childrule.value[n] == '/') {
00189 LOG("search", 4, "child pattern: " << childrule.value.substr(c, n-c));
00190
00191 regex_t aregexp;
00192 regcomp(&aregexp, childrule.value.substr(c, n-c).c_str(), REG_EXTENDED | REG_NOSUB);
00193
00194 currule->childpattern.push_back(aregexp);
00195 c = n+1;
00196 }
00197 }
00198 }
00199 if(n == childrule.value.size()) {
00200 LOG("search", 4, "child pattern: " << childrule.value.substr(c, n-c));
00201
00202 regex_t aregexp;
00203 regcomp(&aregexp, childrule.value.substr(c, n-c).c_str(), REG_EXTENDED | REG_NOSUB);
00204
00205 currule->childpattern.push_back(aregexp);
00206 }
00207
00208 for(unsigned int n = 0, c = 0; n < action.value.size(); n++) {
00209 while(n < action.value.size() && isspace(action.value[n])) { n++; c++; }
00210 while(n < action.value.size() && action.value[n] != ',') n++;
00211 LOG("search", 4, "action: '" << action.value.substr(c, n-c) << "'");
00212 currule->actions.push_back(action.value.substr(c, n-c));
00213 c = n+1;
00214 }
00215 prules.push_back(currule);
00216 }
00217 return prules;
00218 }
00219
00220
00221 bool LocalSearch::doActions(Vobject* currentobject,
00222 const deque<PatternRule*>& rules,
00223 const PatternRule& thisrule,
00224 RemoteCOD* cod,
00225 list<Vobject*>& touchedObjects)
00226 {
00227 if(currentobject->isRemote()) return false;
00228 if(cod->getVobject(currentobject->getName())) return true;
00229
00230 LOG("searched", 4, "doActions looking at " << currentobject->getURL().getString());
00231
00232 vRef<LocalSite> ls = cod->getLocalSite();
00233 vRef<RemoteSite> rs = cod->getRemoteSite();
00234
00235 char nonce[32];
00236 snprintf(nonce, sizeof(nonce), "%i", (int)rand());
00237 CatchUpdatesFilter cu(nonce);
00238 bool wantIt = false;
00239 bool dorecurse = false;
00240 bool ret = true;
00241
00242 for(vector<string>::const_iterator act = thisrule.actions.begin();
00243 act != thisrule.actions.end(); act++)
00244 {
00245 if((*act) == "recurse") {
00246 dorecurse = true;
00247 } else if((*act) == "acquire") {
00248 wantIt = true;
00249 } else if((*act) == "stop") {
00250 ret = false;
00251 break;
00252 } else {
00253 try {
00254 vRef<MessageBlock> mb = rs->getMessageBlock(*act);
00255
00256
00257
00258
00259
00260
00261 rs->insertMessageFilter(-1, &cu);
00262
00263 for(int i = 0; i < mb->numMessages(); i++) {
00264 try {
00265 vRef<Message> msg = new Message(*mb->getMessage(i));
00266
00267 msg->setSourceSite(&rs);
00268
00269
00270 msg->setTo(currentobject->getURL().getString());
00271
00272 msg->setFrom(rs->getURL().getString());
00273 msg->setNonce(nonce);
00274
00275 LOG("search", 4, "to " << currentobject->getURL().getString()
00276 << " sending " << msg->getFormattedString());
00277
00278 currentobject->sendMessage(&msg);
00279 } catch(runtime_error e) {
00280 LOG("remotesite", 2, "Error applying rule: " << e.what());
00281 } catch(...) {
00282 }
00283 }
00284
00285 rs->removeMessageFilter(&cu);
00286 } catch(Site::NoSuchMessageBlockError) {
00287 LOG("search", 2, "No such message block defined \"" << *act << "\"");
00288 }
00289 }
00290 }
00291
00292 if(wantIt) {
00293 LOG("search", 4, "Adding " << currentobject->getURL().getString() << " to the COD");
00294 cod->addVobject(currentobject, currentobject->getTypes(), cu.mb, cu.savechildren);
00295 } else {
00296 rs->sendMessage(cu.mb);
00297 }
00298
00299 if(dorecurse) search(currentobject, rules, cod, touchedObjects);
00300
00301 return ret;
00302 }
00303
00304 bool LocalSearch::actOnChildren(Vobject* currentobject,
00305 const deque<PatternRule*>& rules,
00306 const PatternRule& thisrule,
00307 int patternpart,
00308 RemoteCOD* cod,
00309 list<Vobject*>& touchedObjects)
00310 {
00311 bool ret = true;
00312 const Vobject::ChildList& cl = currentobject->getChildren();
00313
00314 LOG("searched", 4, "actOnChildren looking at " << currentobject->getURL().getString());
00315
00316 for(Vobject::ChildList::const_iterator i = cl.begin(); i != cl.end(); i++) {
00317 if((*i)->child->isLocal()) {
00318 LOG("search", 4, "looking at " << (*i)->contextual_name);
00319 int e = regexec(&thisrule.childpattern[patternpart], (*i)->contextual_name.c_str(), 0, 0, 0);
00320 if(thisrule.negateChild) e = ((e == 0) ? REG_NOMATCH : 0);
00321 calledregex++;
00322 if(e == 0) {
00323 LOG("search", 4, "matched");
00324 if(thisrule.childpattern.size()-1 == (unsigned int)patternpart) {
00325 ret &= doActions((*i)->child, rules, thisrule, cod, touchedObjects);
00326 } else actOnChildren((*i)->child, rules, thisrule, patternpart+1, cod, touchedObjects);
00327 } else {
00328 if(e != REG_NOMATCH) {
00329 char errbuf[256];
00330 regerror(e, &thisrule.childpattern[patternpart], errbuf, sizeof(errbuf));
00331 LOG("search", 2, "oops bad regex " << errbuf);
00332 return false;
00333 }
00334 }
00335 }
00336 }
00337 return ret;
00338 }
00339
00340 void LocalSearch::search(Vobject* currentobject,
00341 const deque<PatternRule*>& rules,
00342 RemoteCOD* cod,
00343 list<Vobject*>& touchedObjects)
00344 {
00345 if(currentobject->isRemote()) return;
00346
00347 if(!currentobject->checkFlag("searched")) {
00348 LOG("searched", 4, "search looking at " << currentobject->getURL().getString());
00349
00350 currentobject->addFlag("searched");
00351 currentobject->acquire();
00352 touchedObjects.push_back(currentobject);
00353
00354 for(deque<PatternRule*>::const_iterator currule = rules.begin(); currule != rules.end(); currule++) {
00355 bool doRule = false;
00356 const Vobject::TypeSet& ts = currentobject->getTypes();
00357 if((*currule)->typePatternEmpty && ts.size() == 0) doRule = true;
00358 for(Vobject::TypeSet::const_iterator tsi = ts.begin(); tsi != ts.end(); tsi++) {
00359 int e = regexec(&(*currule)->typepattern, (*tsi).c_str(), 0, 0, 0);
00360 calledregex++;
00361 if(e == 0) {
00362 LOG("search", 4, "matched " << (*tsi));
00363 doRule = true;
00364 break;
00365 } else {
00366 if(e != REG_NOMATCH) {
00367 char errbuf[256];
00368 regerror(e, &(*currule)->typepattern, errbuf, sizeof(errbuf));
00369 LOG("search", 2, "oops bad regex " << errbuf);
00370 return;
00371 }
00372 }
00373 }
00374 if(doRule) {
00375 if((*currule)->childpattern.size() > 0) {
00376 if(! actOnChildren(currentobject, rules, (**currule), 0, cod, touchedObjects)) return;
00377 } else {
00378 if(! doActions(currentobject, rules, (**currule), cod, touchedObjects)) return;
00379 }
00380 }
00381 }
00382 }
00383 }
00384
00385
00386
00387
00388 void LocalSearch::handleSearch(Message* m) {
00389 deque<PatternRule*> rules = makeRules(m);
00390
00391 for(deque<PatternRule*>::iterator i = rules.begin(); i != rules.end(); i++) {
00392 for(vector<string>::iterator n = (*i)->actions.begin(); n != (*i)->actions.end(); n++)
00393 {
00394 LOG("search", 4, "ok: " << *n);
00395 }
00396 LOG("search", 4, "---");
00397 }
00398
00399
00400 vRef<LocalSite> mysite = dynamic_cast<LocalSite&>(getSite());
00401
00402 vRef<Message> reply;
00403 try {
00404 reply = new Message();
00405 LocalVobject::initReply(this, &reply, m, "misc:search-done");
00406 } catch(Message::NoSuchFieldError e) {
00407
00408 }
00409
00410 double selectwait = mysite->getTimeoutOnSelect();
00411 mysite->setTimeoutOnSelect(0);
00412
00413 vRef<RemoteSite> sourcesite = dynamic_cast<RemoteSite*>(m->getSourceSite());
00414
00415 #ifdef USE_STRSTREAM
00416 strstream of;
00417 #else
00418 stringstream of;
00419 #endif
00420 RemoteCOD rc(of, *mysite, *sourcesite);
00421
00422 LOG("search", 3, "starting search...");
00423
00424 double d = getRealTime();
00425 calledregex = 0;
00426 for(int i = 0; i < m->getNumFields(); i++){
00427 const Message::Field& f = m->getField(i);
00428 if(f.key == "start") {
00429 try {
00430 list<Vobject*> touchedObjects;
00431 vRef<Vobject> v = findObject(f.value);
00432 search(&v, rules, &rc, touchedObjects);
00433
00434 for(list<Vobject*>::iterator li = touchedObjects.begin(); li != touchedObjects.end(); li++) {
00435 (*li)->removeFlag("searched");
00436 (*li)->release();
00437 }
00438 } catch(NoSuchObjectError) {
00439 } catch(runtime_error x) {
00440 LOG("search", 1, "Got error doing search " << x.what());
00441
00442 }
00443 }
00444 }
00445 LOG("search", 3, "search done, took " << getRealTime() - d << " sec with " << calledregex << " regexes");
00446
00447 d = getRealTime();
00448 LOG("search", 3, "starting COD write");
00449
00450 unsigned char* data;
00451 unsigned int size;
00452 rc.writeCOD(&data, &size);
00453
00454 LOG("search", 3, "did write COD, took " << getRealTime() - d << " sec");
00455
00456 for(unsigned int i = 0; i < rules.size(); i++) {
00457 delete rules[i];
00458 }
00459 rules.resize(0);
00460
00461 mysite->flushIncomingBuffers();
00462 mysite->setTimeoutOnSelect(selectwait);
00463
00464 try {
00465 vRef<Vobject> from = Vobject::findObjectFromRoot(m->getFrom());
00466 reply->insertField(-1, "cod", string((char*)data, size), true);
00467 from->sendMessage(&reply);
00468 } catch(...) {
00469 }
00470 }
00471
00472
00473
00474
00475 string RemoteSearch::doSearch(const deque<string>& objects, const deque<string>& rules, SearchListener* listener) {
00476 string n = doSearch(objects, rules, true);
00477 RefCounted* r = dynamic_cast<RefCounted*>(listener);
00478 if(r) r->acquire();
00479 listeners[n] = listener;
00480 return n;
00481 }
00482
00483 string RemoteSearch::doSearch(const deque<string>& objects, const deque<string>& rules, bool async, RemoteCOD** cod) {
00484
00485
00486 vRef<Message> m = new Message();
00487 vRef<LocalSite> ls = RemoteVobject::initFields(this, &m, "misc:search", true);
00488
00489 for(deque<string>::const_iterator i = objects.begin(); i != objects.end(); i++) {
00490 m->insertField(-1, "start", *i);
00491 }
00492 for(deque<string>::const_iterator i = rules.begin(); i != rules.end(); i++) {
00493 m->insertField(-1, "typerule", *i);
00494 i++;
00495 m->insertField(-1, "childrule", *i);
00496 i++;
00497 m->insertField(-1, "action", *i);
00498 }
00499
00500 searches.insert(m->getNonce());
00501
00502 sendMessage(&m);
00503 vRef<RemoteSite> site = dynamic_cast<RemoteSite&>(getSite());
00504 if(! async) {
00505 vRef<Message> reply = ls->waitFor(m->getNonce(), &site, 3600);
00506 try {
00507 LOG("search", 4,"starting to unpack COD");
00508 double d = getRealTime();
00509
00510 const Message::Field& f = reply->getField("cod");
00511 #if 0
00512 #ifdef USE_STRSTREAM
00513 strstream input((char*)(f.value.c_str()), f.value.length());
00514
00515
00516 #else
00517 stringstream input(f.value, ios::in | ios::bin);
00518 #endif
00519 #endif
00520
00521
00522
00523
00524
00525 vRef<RemoteSite> rs = dynamic_cast<RemoteSite*>(reply->getSourceSite());
00526 vRef<LocalSite> ls = rs->getLocalPeer();
00527
00528 RemoteCOD* rc = new RemoteCOD(*ls, *rs);
00529 LOG("search", 4, "x4");
00530 rc->readCOD((unsigned char*)(f.value.c_str()), f.value.length());
00531 LOG("search", 4, "x5");
00532
00533 if(cod) {
00534 *cod = rc;
00535 } else delete rc;
00536 LOG("search", 3, "unpacking done, took " << getRealTime() - d << " sec");
00537 } catch(Message::NoSuchFieldError) {
00538 LOG("search", 2, "Received search-done message but could not find the COD field");
00539 } catch(COD::eof) {
00540 LOG("search", 2, "COD ended prematurely");
00541 }
00542 }
00543 return m->getNonce();
00544 }
00545
00546
00547
00548
00549 MetaObject* LocalSearch::new_LocalSearch(MetaObject *s, const string& type) {
00550 LocalSearch* o = new LocalSearch(s);
00551 return o;
00552 }
00553
00554 MetaObject* RemoteSearch::new_RemoteSearch(MetaObject *s, const string& type) {
00555 return new RemoteSearch(s);
00556 }
00557
00558
00559
00560 void RemoteSearch::handleSearchDone(Message* m) {
00561 string n = m->getNonce();
00562 if(listeners.find(n) != listeners.end()) {
00563 try {
00564 const Message::Field& f = m->getField("cod");
00565 #if 0
00566 #ifdef USE_STRSTREAM
00567 strstream input((char*) f.value.c_str(), f.value.length());
00568
00569
00570 #else
00571 stringstream input(f.value);
00572 #endif
00573 #endif
00574 vRef<RemoteSite> rs = dynamic_cast<RemoteSite*>(m->getSourceSite());
00575 vRef<LocalSite> ls = rs->getLocalPeer();
00576
00577 RemoteCOD cod(*ls, *rs);
00578 cod.readCOD((unsigned char*)(f.value.c_str()), f.value.length());
00579 } catch(Message::NoSuchFieldError) {
00580 LOG("search", 2, "Received search-done message but could not find the COD field");
00581 }
00582
00583 listeners[n]->notifySearchDone();
00584 RefCounted* r = dynamic_cast<RefCounted*>(listeners[n]);
00585 if(r) r->release();
00586 listeners.erase(n);
00587 }
00588 }
00589