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*
create_condition(const char * name,const BMessage & args)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
Condition()160 Condition::Condition()
161 {
162 }
163
164
~Condition()165 Condition::~Condition()
166 {
167 }
168
169
170 bool
IsConstant(ConditionContext & context) const171 Condition::IsConstant(ConditionContext& context) const
172 {
173 return false;
174 }
175
176
177 // #pragma mark -
178
179
ConditionContainer(const BMessage & args)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
ConditionContainer()198 ConditionContainer::ConditionContainer()
199 :
200 fConditions(10, true)
201 {
202 }
203
204
205 void
AddCondition(Condition * condition)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
IsConstant(ConditionContext & context) const217 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
AddConditionsToString(BString & string) const233 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
AndCondition(const BMessage & args)249 AndCondition::AndCondition(const BMessage& args)
250 :
251 ConditionContainer(args)
252 {
253 }
254
255
AndCondition()256 AndCondition::AndCondition()
257 {
258 }
259
260
261 bool
Test(ConditionContext & context) const262 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
ToString() const274 AndCondition::ToString() const
275 {
276 BString string = "and ";
277 ConditionContainer::AddConditionsToString(string);
278 return string;
279 }
280
281
282 // #pragma mark - or
283
284
OrCondition(const BMessage & args)285 OrCondition::OrCondition(const BMessage& args)
286 :
287 ConditionContainer(args)
288 {
289 }
290
291
292 bool
Test(ConditionContext & context) const293 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
IsConstant(ConditionContext & context) const311 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
ToString() const327 OrCondition::ToString() const
328 {
329 BString string = "or ";
330 ConditionContainer::AddConditionsToString(string);
331 return string;
332 }
333
334
335 // #pragma mark - or
336
337
NotCondition(const BMessage & args)338 NotCondition::NotCondition(const BMessage& args)
339 :
340 ConditionContainer(args)
341 {
342 }
343
344
NotCondition()345 NotCondition::NotCondition()
346 {
347 }
348
349
350 bool
Test(ConditionContext & context) const351 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
ToString() const363 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
Test(ConditionContext & context) const375 SafeModeCondition::Test(ConditionContext& context) const
376 {
377 return context.IsSafeMode();
378 }
379
380
381 bool
IsConstant(ConditionContext & context) const382 SafeModeCondition::IsConstant(ConditionContext& context) const
383 {
384 return true;
385 }
386
387
388 BString
ToString() const389 SafeModeCondition::ToString() const
390 {
391 return "safemode";
392 }
393
394
395 // #pragma mark - read_only
396
397
ReadOnlyCondition(const BMessage & args)398 ReadOnlyCondition::ReadOnlyCondition(const BMessage& args)
399 :
400 fPath(args.GetString("args")),
401 fIsReadOnly(false),
402 fTestPerformed(false)
403 {
404 }
405
406
407 bool
Test(ConditionContext & context) const408 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
IsConstant(ConditionContext & context) const425 ReadOnlyCondition::IsConstant(ConditionContext& context) const
426 {
427 return true;
428 }
429
430
431 BString
ToString() const432 ReadOnlyCondition::ToString() const
433 {
434 BString string = "readonly ";
435 string << fPath;
436 return string;
437 }
438
439
440 // #pragma mark - file_exists
441
442
FileExistsCondition(const BMessage & args)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
Test(ConditionContext & context) const453 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
ToString() const466 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
Test(ConditionContext & context) const483 NetworkAvailableCondition::Test(ConditionContext& context) const
484 {
485 return NetworkWatcher::NetworkAvailable(false);
486 }
487
488
489 bool
IsConstant(ConditionContext & context) const490 NetworkAvailableCondition::IsConstant(ConditionContext& context) const
491 {
492 return false;
493 }
494
495
496 BString
ToString() const497 NetworkAvailableCondition::ToString() const
498 {
499 return "network_available";
500 }
501
502
503 // #pragma mark - setting
504
505
SettingCondition(const BMessage & args)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
Test(ConditionContext & context) const515 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
ToString() const555 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*
FromMessage(const BMessage & message)570 Conditions::FromMessage(const BMessage& message)
571 {
572 return create_condition("and", message);
573 }
574
575
576 /*static*/ Condition*
AddNotSafeMode(Condition * 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