1/* 2 * Copyright 2015, Axel D��rfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include "Conditions.h" 8 9#include <stdio.h> 10 11#include <Entry.h> 12#include <File.h> 13#include <ObjectList.h> 14#include <Message.h> 15#include <Path.h> 16#include <StringList.h> 17 18#include "NetworkWatcher.h" 19#include "Utility.h" 20 21 22class ConditionContainer : public Condition { 23protected: 24 ConditionContainer(const BMessage& args); 25 ConditionContainer(); 26 27public: 28 void AddCondition(Condition* condition); 29 30 virtual bool IsConstant(ConditionContext& context) const; 31 32protected: 33 void AddConditionsToString(BString& string) const; 34 35protected: 36 BObjectList<Condition> fConditions; 37}; 38 39 40class AndCondition : public ConditionContainer { 41public: 42 AndCondition(const BMessage& args); 43 AndCondition(); 44 45 virtual bool Test(ConditionContext& context) const; 46 virtual BString ToString() const; 47}; 48 49 50class OrCondition : public ConditionContainer { 51public: 52 OrCondition(const BMessage& args); 53 54 virtual bool Test(ConditionContext& context) const; 55 virtual bool IsConstant(ConditionContext& context) const; 56 57 virtual BString ToString() const; 58}; 59 60 61class NotCondition : public ConditionContainer { 62public: 63 NotCondition(const BMessage& args); 64 NotCondition(); 65 66 virtual bool Test(ConditionContext& context) const; 67 virtual BString ToString() const; 68}; 69 70 71class SafeModeCondition : public Condition { 72public: 73 virtual bool Test(ConditionContext& context) const; 74 virtual bool IsConstant(ConditionContext& context) const; 75 76 virtual BString ToString() const; 77}; 78 79 80class ReadOnlyCondition : public Condition { 81public: 82 ReadOnlyCondition(const BMessage& args); 83 84 virtual bool Test(ConditionContext& context) const; 85 virtual bool IsConstant(ConditionContext& context) const; 86 87 virtual BString ToString() const; 88 89private: 90 BString fPath; 91 mutable bool fIsReadOnly; 92 mutable bool fTestPerformed; 93}; 94 95 96class FileExistsCondition : public Condition { 97public: 98 FileExistsCondition(const BMessage& args); 99 100 virtual bool Test(ConditionContext& context) const; 101 virtual BString ToString() const; 102 103private: 104 BStringList fPaths; 105}; 106 107 108class NetworkAvailableCondition : public Condition { 109public: 110 virtual bool Test(ConditionContext& context) const; 111 virtual bool IsConstant(ConditionContext& context) const; 112 113 virtual BString ToString() const; 114}; 115 116 117class SettingCondition : public Condition { 118public: 119 SettingCondition(const BMessage& args); 120 121 virtual bool Test(ConditionContext& context) const; 122 123 virtual BString ToString() const; 124 125private: 126 BPath fPath; 127 BString fField; 128 BString fValue; 129}; 130 131 132static Condition* 133create_condition(const char* name, const BMessage& args) 134{ 135 if (strcmp(name, "and") == 0 && !args.IsEmpty()) 136 return new AndCondition(args); 137 if (strcmp(name, "or") == 0 && !args.IsEmpty()) 138 return new OrCondition(args); 139 if (strcmp(name, "not") == 0 && !args.IsEmpty()) 140 return new NotCondition(args); 141 142 if (strcmp(name, "safemode") == 0) 143 return new SafeModeCondition(); 144 if (strcmp(name, "read_only") == 0) 145 return new ReadOnlyCondition(args); 146 if (strcmp(name, "file_exists") == 0) 147 return new FileExistsCondition(args); 148 if (strcmp(name, "network_available") == 0) 149 return new NetworkAvailableCondition(); 150 if (strcmp(name, "setting") == 0) 151 return new SettingCondition(args); 152 153 return NULL; 154} 155 156 157// #pragma mark - 158 159 160Condition::Condition() 161{ 162} 163 164 165Condition::~Condition() 166{ 167} 168 169 170bool 171Condition::IsConstant(ConditionContext& context) const 172{ 173 return false; 174} 175 176 177// #pragma mark - 178 179 180ConditionContainer::ConditionContainer(const BMessage& args) 181 : 182 fConditions(10, true) 183{ 184 char* name; 185 type_code type; 186 int32 count; 187 for (int32 index = 0; args.GetInfo(B_MESSAGE_TYPE, index, &name, &type, 188 &count) == B_OK; index++) { 189 BMessage message; 190 for (int32 messageIndex = 0; args.FindMessage(name, messageIndex, 191 &message) == B_OK; messageIndex++) { 192 AddCondition(create_condition(name, message)); 193 } 194 } 195} 196 197 198ConditionContainer::ConditionContainer() 199 : 200 fConditions(10, true) 201{ 202} 203 204 205void 206ConditionContainer::AddCondition(Condition* condition) 207{ 208 if (condition != NULL) 209 fConditions.AddItem(condition); 210} 211 212 213/*! A single constant failing condition makes this constant, too, otherwise, 214 a single non-constant condition makes this non-constant as well. 215*/ 216bool 217ConditionContainer::IsConstant(ConditionContext& context) const 218{ 219 bool fixed = true; 220 for (int32 index = 0; index < fConditions.CountItems(); index++) { 221 const Condition* condition = fConditions.ItemAt(index); 222 if (condition->IsConstant(context)) { 223 if (!condition->Test(context)) 224 return true; 225 } else 226 fixed = false; 227 } 228 return fixed; 229} 230 231 232void 233ConditionContainer::AddConditionsToString(BString& string) const 234{ 235 string += "["; 236 237 for (int32 index = 0; index < fConditions.CountItems(); index++) { 238 if (index != 0) 239 string += ", "; 240 string += fConditions.ItemAt(index)->ToString(); 241 } 242 string += "]"; 243} 244 245 246// #pragma mark - and 247 248 249AndCondition::AndCondition(const BMessage& args) 250 : 251 ConditionContainer(args) 252{ 253} 254 255 256AndCondition::AndCondition() 257{ 258} 259 260 261bool 262AndCondition::Test(ConditionContext& context) const 263{ 264 for (int32 index = 0; index < fConditions.CountItems(); index++) { 265 Condition* condition = fConditions.ItemAt(index); 266 if (!condition->Test(context)) 267 return false; 268 } 269 return true; 270} 271 272 273BString 274AndCondition::ToString() const 275{ 276 BString string = "and "; 277 ConditionContainer::AddConditionsToString(string); 278 return string; 279} 280 281 282// #pragma mark - or 283 284 285OrCondition::OrCondition(const BMessage& args) 286 : 287 ConditionContainer(args) 288{ 289} 290 291 292bool 293OrCondition::Test(ConditionContext& context) const 294{ 295 if (fConditions.IsEmpty()) 296 return true; 297 298 for (int32 index = 0; index < fConditions.CountItems(); index++) { 299 Condition* condition = fConditions.ItemAt(index); 300 if (condition->Test(context)) 301 return true; 302 } 303 return false; 304} 305 306 307/*! If there is a single succeeding constant condition, this is constant, too. 308 Otherwise, it is non-constant if there is a single non-constant condition. 309*/ 310bool 311OrCondition::IsConstant(ConditionContext& context) const 312{ 313 bool fixed = true; 314 for (int32 index = 0; index < fConditions.CountItems(); index++) { 315 const Condition* condition = fConditions.ItemAt(index); 316 if (condition->IsConstant(context)) { 317 if (condition->Test(context)) 318 return true; 319 } else 320 fixed = false; 321 } 322 return fixed; 323} 324 325 326BString 327OrCondition::ToString() const 328{ 329 BString string = "or "; 330 ConditionContainer::AddConditionsToString(string); 331 return string; 332} 333 334 335// #pragma mark - or 336 337 338NotCondition::NotCondition(const BMessage& args) 339 : 340 ConditionContainer(args) 341{ 342} 343 344 345NotCondition::NotCondition() 346{ 347} 348 349 350bool 351NotCondition::Test(ConditionContext& context) const 352{ 353 for (int32 index = 0; index < fConditions.CountItems(); index++) { 354 Condition* condition = fConditions.ItemAt(index); 355 if (condition->Test(context)) 356 return false; 357 } 358 return true; 359} 360 361 362BString 363NotCondition::ToString() const 364{ 365 BString string = "not "; 366 ConditionContainer::AddConditionsToString(string); 367 return string; 368} 369 370 371// #pragma mark - safemode 372 373 374bool 375SafeModeCondition::Test(ConditionContext& context) const 376{ 377 return context.IsSafeMode(); 378} 379 380 381bool 382SafeModeCondition::IsConstant(ConditionContext& context) const 383{ 384 return true; 385} 386 387 388BString 389SafeModeCondition::ToString() const 390{ 391 return "safemode"; 392} 393 394 395// #pragma mark - read_only 396 397 398ReadOnlyCondition::ReadOnlyCondition(const BMessage& args) 399 : 400 fPath(args.GetString("args")), 401 fIsReadOnly(false), 402 fTestPerformed(false) 403{ 404} 405 406 407bool 408ReadOnlyCondition::Test(ConditionContext& context) const 409{ 410 if (fTestPerformed) 411 return fIsReadOnly; 412 413 if (fPath.IsEmpty() || fPath == "/boot") 414 fIsReadOnly = context.BootVolumeIsReadOnly(); 415 else 416 fIsReadOnly = Utility::IsReadOnlyVolume(fPath); 417 418 fTestPerformed = true; 419 420 return fIsReadOnly; 421} 422 423 424bool 425ReadOnlyCondition::IsConstant(ConditionContext& context) const 426{ 427 return true; 428} 429 430 431BString 432ReadOnlyCondition::ToString() const 433{ 434 BString string = "readonly "; 435 string << fPath; 436 return string; 437} 438 439 440// #pragma mark - file_exists 441 442 443FileExistsCondition::FileExistsCondition(const BMessage& args) 444{ 445 for (int32 index = 0; 446 const char* path = args.GetString("args", index, NULL); index++) { 447 fPaths.Add(Utility::TranslatePath(path)); 448 } 449} 450 451 452bool 453FileExistsCondition::Test(ConditionContext& context) const 454{ 455 for (int32 index = 0; index < fPaths.CountStrings(); index++) { 456 BEntry entry; 457 if (entry.SetTo(fPaths.StringAt(index)) != B_OK 458 || !entry.Exists()) 459 return false; 460 } 461 return true; 462} 463 464 465BString 466FileExistsCondition::ToString() const 467{ 468 BString string = "file_exists ["; 469 for (int32 index = 0; index < fPaths.CountStrings(); index++) { 470 if (index != 0) 471 string << ", "; 472 string << fPaths.StringAt(index); 473 } 474 string += "]"; 475 return string; 476} 477 478 479// #pragma mark - network_available 480 481 482bool 483NetworkAvailableCondition::Test(ConditionContext& context) const 484{ 485 return NetworkWatcher::NetworkAvailable(false); 486} 487 488 489bool 490NetworkAvailableCondition::IsConstant(ConditionContext& context) const 491{ 492 return false; 493} 494 495 496BString 497NetworkAvailableCondition::ToString() const 498{ 499 return "network_available"; 500} 501 502 503// #pragma mark - setting 504 505 506SettingCondition::SettingCondition(const BMessage& args) 507{ 508 fPath.SetTo(Utility::TranslatePath(args.GetString("args", 0, NULL))); 509 fField = args.GetString("args", 1, NULL); 510 fValue = args.GetString("args", 2, NULL); 511} 512 513 514bool 515SettingCondition::Test(ConditionContext& context) const 516{ 517 BFile file(fPath.Path(), B_READ_ONLY); 518 if (file.InitCheck() != B_OK) 519 return false; 520 521 BMessage settings; 522 if (settings.Unflatten(&file) == B_OK) { 523 type_code type; 524 int32 count; 525 if (settings.GetInfo(fField, &type, &count) == B_OK) { 526 switch (type) { 527 case B_BOOL_TYPE: 528 { 529 bool value = settings.GetBool(fField); 530 bool expect = fValue.IsEmpty(); 531 if (!expect) { 532 expect = fValue == "true" || fValue == "yes" 533 || fValue == "on" || fValue == "1"; 534 } 535 return value == expect; 536 } 537 case B_STRING_TYPE: 538 { 539 BString value = settings.GetString(fField); 540 if (fValue.IsEmpty() && !value.IsEmpty()) 541 return true; 542 543 return fValue == value; 544 } 545 } 546 } 547 } 548 // TODO: check for driver settings, too? 549 550 return false; 551} 552 553 554BString 555SettingCondition::ToString() const 556{ 557 BString string = "setting file "; 558 string << fPath.Path() << ", field " << fField; 559 if (!fValue.IsEmpty()) 560 string << ", value " << fValue; 561 562 return string; 563} 564 565 566// #pragma mark - 567 568 569/*static*/ Condition* 570Conditions::FromMessage(const BMessage& message) 571{ 572 return create_condition("and", message); 573} 574 575 576/*static*/ Condition* 577Conditions::AddNotSafeMode(Condition* condition) 578{ 579 AndCondition* andCondition = dynamic_cast<AndCondition*>(condition); 580 if (andCondition == NULL) 581 andCondition = new AndCondition(); 582 if (andCondition != condition && condition != NULL) 583 andCondition->AddCondition(condition); 584 585 NotCondition* notCondition = new NotCondition(); 586 notCondition->AddCondition(new SafeModeCondition()); 587 588 andCondition->AddCondition(notCondition); 589 return andCondition; 590} 591