1/*****************************************************************************/
2// FolderWatcher
3//
4// Author
5//   Michael Pfeiffer
6//
7// This application and all source files used in its construction, except
8// where noted, are licensed under the MIT License, and have been written
9// and are:
10//
11// Copyright (c) 2002 Haiku Project
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30/*****************************************************************************/
31
32
33#include "FolderWatcher.h"
34
35#include <stdio.h>
36
37// BeOS
38#include <kernel/fs_attr.h>
39#include <Node.h>
40#include <NodeInfo.h>
41#include <NodeMonitor.h>
42
43
44// Implementation of FolderWatcher
45
46FolderWatcher::FolderWatcher(BLooper* looper, const BDirectory& folder, bool watchAttrChanges)
47	: fFolder(folder)
48	, fListener(NULL)
49	, fWatchAttrChanges(watchAttrChanges)
50{
51		// add this handler to the application for node monitoring
52	if (looper->Lock()) {
53		looper->AddHandler(this);
54		looper->Unlock();
55	}
56
57		// start attribute change watching for existing files
58	if (watchAttrChanges) {
59		BEntry entry;
60		node_ref node;
61		while (fFolder.GetNextEntry(&entry) == B_OK && entry.GetNodeRef(&node) == B_OK) {
62			StartAttrWatching(&node);
63		}
64	}
65
66		// start watching the spooler directory
67	node_ref ref;
68	fFolder.GetNodeRef(&ref);
69	watch_node(&ref, B_WATCH_DIRECTORY, this);
70}
71
72FolderWatcher::~FolderWatcher() {
73		// stop node watching for spooler directory
74	node_ref ref;
75	fFolder.GetNodeRef(&ref);
76	watch_node(&ref, B_STOP_WATCHING, this);
77		// stop sending notifications to this handler
78	stop_watching(this);
79
80	if (LockLooper()) {
81		BLooper* looper = Looper();
82			// and remove it
83		looper->RemoveHandler(this);
84		looper->Unlock();
85	}
86}
87
88void FolderWatcher::SetListener(FolderListener* listener) {
89	fListener = listener;
90}
91
92bool FolderWatcher::BuildEntryRef(BMessage* msg, const char* dirName, entry_ref* entry) {
93	const char* name;
94	if (msg->FindInt32("device", &entry->device) == B_OK &&
95		msg->FindInt64(dirName, &entry->directory) == B_OK &&
96		msg->FindString("name", &name) == B_OK) {
97		entry->set_name(name);
98		return true;
99	}
100	return false;
101}
102
103bool FolderWatcher::BuildNodeRef(BMessage* msg, node_ref* node) {
104	return (msg->FindInt32("device", &node->device) == B_OK &&
105		msg->FindInt64("node", &node->node) == B_OK);
106}
107
108void FolderWatcher::HandleCreatedEntry(BMessage* msg, const char* dirName) {
109	node_ref node;
110	entry_ref entry;
111	if (BuildEntryRef(msg, dirName, &entry) &&
112		BuildNodeRef(msg, &node)) {
113		if (fWatchAttrChanges) StartAttrWatching(&node);
114		fListener->EntryCreated(&node, &entry);
115	}
116}
117
118void FolderWatcher::HandleRemovedEntry(BMessage* msg) {
119	node_ref node;
120	if (BuildNodeRef(msg, &node)) {
121		if (fWatchAttrChanges) StopAttrWatching(&node);
122		fListener->EntryRemoved(&node);
123	}
124}
125
126void FolderWatcher::HandleChangedAttr(BMessage* msg) {
127	node_ref node;
128	if (BuildNodeRef(msg, &node)) {
129		fListener->AttributeChanged(&node);
130	}
131}
132
133void FolderWatcher::MessageReceived(BMessage* msg) {
134	int32 opcode;
135	node_ref folder;
136	ino_t dir;
137
138	if (msg->what == B_NODE_MONITOR) {
139		if (fListener == NULL || msg->FindInt32("opcode", &opcode) != B_OK) return;
140
141		switch (opcode) {
142			case B_ENTRY_CREATED:
143				HandleCreatedEntry(msg, "directory");
144				break;
145			case B_ENTRY_REMOVED:
146				HandleRemovedEntry(msg);
147				break;
148			case B_ENTRY_MOVED:
149				fFolder.GetNodeRef(&folder);
150				if (msg->FindInt64("to directory", &dir) == B_OK && folder.node == dir) {
151					// entry moved into this folder
152					HandleCreatedEntry(msg, "to directory");
153				} else if (msg->FindInt64("from directory", &dir) == B_OK && folder.node == dir) {
154					// entry removed from this folder
155					HandleRemovedEntry(msg);
156				}
157				break;
158			case B_ATTR_CHANGED:
159				HandleChangedAttr(msg);
160				break;
161			default: // nothing to do
162				break;
163		}
164	} else {
165		inherited::MessageReceived(msg);
166	}
167}
168
169status_t FolderWatcher::StartAttrWatching(node_ref* node) {
170	return watch_node(node, B_WATCH_ATTR, this);
171}
172
173status_t FolderWatcher::StopAttrWatching(node_ref* node) {
174	return watch_node(node, B_STOP_WATCHING, this);
175}
176