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 22 class ConditionContainer : public Condition { 23 protected: 24 ConditionContainer(const BMessage& args); 25 ConditionContainer(); 26 27 public: 28 void AddCondition(Condition* condition); 29 30 virtual bool IsConstant(ConditionContext& context) const; 31 32 protected: 33 void AddConditionsToString(BString& string) const; 34 35 protected: 36 BObjectList<Condition> fConditions; 37 }; 38 39 40 class AndCondition : public ConditionContainer { 41 public: 42 AndCondition(const BMessage& args); 43 AndCondition(); 44 45 virtual bool Test(ConditionContext& context) const; 46 virtual BString ToString() const; 47 }; 48 49 50 class OrCondition : public ConditionContainer { 51 public: 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 61 class NotCondition : public ConditionContainer { 62 public: 63 NotCondition(const BMessage& args); 64 NotCondition(); 65 66 virtual bool Test(ConditionContext& context) const; 67 virtual BString ToString() const; 68 }; 69 70 71 class SafeModeCondition : public Condition { 72 public: 73 virtual bool Test(ConditionContext& context) const; 74 virtual bool IsConstant(ConditionContext& context) const; 75 76 virtual BString ToString() const; 77 }; 78 79 80 class ReadOnlyCondition : public Condition { 81 public: 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 89 private: 90 BString fPath; 91 mutable bool fIsReadOnly; 92 mutable bool fTestPerformed; 93 }; 94 95 96 class FileExistsCondition : public Condition { 97 public: 98 FileExistsCondition(const BMessage& args); 99 100 virtual bool Test(ConditionContext& context) const; 101 virtual BString ToString() const; 102 103 private: 104 BStringList fPaths; 105 }; 106 107 108 class NetworkAvailableCondition : public Condition { 109 public: 110 virtual bool Test(ConditionContext& context) const; 111 virtual bool IsConstant(ConditionContext& context) const; 112 113 virtual BString ToString() const; 114 }; 115 116 117 class SettingCondition : public Condition { 118 public: 119 SettingCondition(const BMessage& args); 120 121 virtual bool Test(ConditionContext& context) const; 122 123 virtual BString ToString() const; 124 125 private: 126 BPath fPath; 127 BString fField; 128 BString fValue; 129 }; 130 131 132 static Condition* 133 create_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 160 Condition::Condition() 161 { 162 } 163 164 165 Condition::~Condition() 166 { 167 } 168 169 170 bool 171 Condition::IsConstant(ConditionContext& context) const 172 { 173 return false; 174 } 175 176 177 // #pragma mark - 178 179 180 ConditionContainer::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 198 ConditionContainer::ConditionContainer() 199 : 200 fConditions(10, true) 201 { 202 } 203 204 205 void 206 ConditionContainer::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 */ 216 bool 217 ConditionContainer::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 232 void 233 ConditionContainer::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 249 AndCondition::AndCondition(const BMessage& args) 250 : 251 ConditionContainer(args) 252 { 253 } 254 255 256 AndCondition::AndCondition() 257 { 258 } 259 260 261 bool 262 AndCondition::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 273 BString 274 AndCondition::ToString() const 275 { 276 BString string = "and "; 277 ConditionContainer::AddConditionsToString(string); 278 return string; 279 } 280 281 282 // #pragma mark - or 283 284 285 OrCondition::OrCondition(const BMessage& args) 286 : 287 ConditionContainer(args) 288 { 289 } 290 291 292 bool 293 OrCondition::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 */ 310 bool 311 OrCondition::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 326 BString 327 OrCondition::ToString() const 328 { 329 BString string = "or "; 330 ConditionContainer::AddConditionsToString(string); 331 return string; 332 } 333 334 335 // #pragma mark - or 336 337 338 NotCondition::NotCondition(const BMessage& args) 339 : 340 ConditionContainer(args) 341 { 342 } 343 344 345 NotCondition::NotCondition() 346 { 347 } 348 349 350 bool 351 NotCondition::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 362 BString 363 NotCondition::ToString() const 364 { 365 BString string = "not "; 366 ConditionContainer::AddConditionsToString(string); 367 return string; 368 } 369 370 371 // #pragma mark - safemode 372 373 374 bool 375 SafeModeCondition::Test(ConditionContext& context) const 376 { 377 return context.IsSafeMode(); 378 } 379 380 381 bool 382 SafeModeCondition::IsConstant(ConditionContext& context) const 383 { 384 return true; 385 } 386 387 388 BString 389 SafeModeCondition::ToString() const 390 { 391 return "safemode"; 392 } 393 394 395 // #pragma mark - read_only 396 397 398 ReadOnlyCondition::ReadOnlyCondition(const BMessage& args) 399 : 400 fPath(args.GetString("args")), 401 fIsReadOnly(false), 402 fTestPerformed(false) 403 { 404 } 405 406 407 bool 408 ReadOnlyCondition::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 424 bool 425 ReadOnlyCondition::IsConstant(ConditionContext& context) const 426 { 427 return true; 428 } 429 430 431 BString 432 ReadOnlyCondition::ToString() const 433 { 434 BString string = "readonly "; 435 string << fPath; 436 return string; 437 } 438 439 440 // #pragma mark - file_exists 441 442 443 FileExistsCondition::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 452 bool 453 FileExistsCondition::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 465 BString 466 FileExistsCondition::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 482 bool 483 NetworkAvailableCondition::Test(ConditionContext& context) const 484 { 485 return NetworkWatcher::NetworkAvailable(false); 486 } 487 488 489 bool 490 NetworkAvailableCondition::IsConstant(ConditionContext& context) const 491 { 492 return false; 493 } 494 495 496 BString 497 NetworkAvailableCondition::ToString() const 498 { 499 return "network_available"; 500 } 501 502 503 // #pragma mark - setting 504 505 506 SettingCondition::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 514 bool 515 SettingCondition::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 554 BString 555 SettingCondition::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* 570 Conditions::FromMessage(const BMessage& message) 571 { 572 return create_condition("and", message); 573 } 574 575 576 /*static*/ Condition* 577 Conditions::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