1/*****************************************************************************/
2// Jobs
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 "pr_server.h"
34#include "Jobs.h"
35// #include "PrintServerApp.h"
36
37// posix
38#include <stdlib.h>
39#include <string.h>
40
41// BeOS
42#include <kernel/fs_attr.h>
43#include <Application.h>
44#include <Node.h>
45#include <NodeInfo.h>
46#include <NodeMonitor.h>
47
48
49// Implementation of Job
50
51Job::Job(const BEntry& job, Folder* folder)
52	: fFolder(folder)
53	, fTime(-1)
54	, fStatus(kUnknown)
55	, fValid(false)
56	, fPrinter(NULL)
57{
58	// store light weight versions of BEntry and BNode
59	job.GetRef(&fEntry);
60	job.GetNodeRef(&fNode);
61
62	fValid = IsValidJobFile();
63
64	BNode node(&job);
65	if (node.InitCheck() != B_OK) return;
66
67	BString status;
68		// read status attribute
69	if (node.ReadAttrString(PSRV_SPOOL_ATTR_STATUS, &status) != B_OK) {
70		status = "";
71	}
72    UpdateStatus(status.String());
73
74    	// Now get file name and creation time from file name
75    fTime = 0;
76    BEntry entry(job);
77    char name[B_FILE_NAME_LENGTH];
78    if (entry.InitCheck() == B_OK && entry.GetName(name) == B_OK) {
79    	fName = name;
80    		// search for last '@' in file name
81		char* p = NULL, *c = name;
82		while ((c = strchr(c, '@')) != NULL) {
83			p = c; c ++;
84		}
85			// and get time from file name
86		if (p) fTime = atoi(p+1);
87    }
88}
89
90// conversion from string representation of status to JobStatus constant
91void Job::UpdateStatus(const char* status) {
92	if (strcmp(status, PSRV_JOB_STATUS_WAITING) == 0) fStatus = kWaiting;
93	else if (strcmp(status, PSRV_JOB_STATUS_PROCESSING) == 0) fStatus = kProcessing;
94	else if (strcmp(status, PSRV_JOB_STATUS_FAILED) == 0) fStatus = kFailed;
95	else if (strcmp(status, PSRV_JOB_STATUS_COMPLETED) == 0) fStatus = kCompleted;
96	else fStatus = kUnknown;
97}
98
99// Write to status attribute of node
100void Job::UpdateStatusAttribute(const char* status) {
101	BNode node(&fEntry);
102	if (node.InitCheck() == B_OK) {
103		node.WriteAttr(PSRV_SPOOL_ATTR_STATUS, B_STRING_TYPE, 0, status, strlen(status)+1);
104	}
105}
106
107
108bool Job::HasAttribute(BNode* n, const char* name) {
109	attr_info info;
110	return n->GetAttrInfo(name, &info) == B_OK;
111}
112
113
114bool Job::IsValidJobFile() {
115	BNode node(&fEntry);
116	if (node.InitCheck() != B_OK) return false;
117
118	BNodeInfo info(&node);
119	char mimeType[256];
120
121		// Is job a spool file?
122	return (info.InitCheck() == B_OK &&
123	    info.GetType(mimeType) == B_OK &&
124	    strcmp(mimeType, PSRV_SPOOL_FILETYPE) == 0) &&
125	    HasAttribute(&node, PSRV_SPOOL_ATTR_MIMETYPE) &&
126	    HasAttribute(&node, PSRV_SPOOL_ATTR_PAGECOUNT) &&
127	    HasAttribute(&node, PSRV_SPOOL_ATTR_DESCRIPTION) &&
128	    HasAttribute(&node, PSRV_SPOOL_ATTR_PRINTER) &&
129	    HasAttribute(&node, PSRV_SPOOL_ATTR_STATUS);
130}
131
132
133// Set status of object and optionally write to attribute of node
134void Job::SetStatus(JobStatus s, bool writeToNode) {
135	fStatus = s;
136	if (!writeToNode) return;
137	switch (s) {
138		case kWaiting: UpdateStatusAttribute(PSRV_JOB_STATUS_WAITING); break;
139		case kProcessing: UpdateStatusAttribute(PSRV_JOB_STATUS_PROCESSING); break;
140		case kFailed: UpdateStatusAttribute(PSRV_JOB_STATUS_FAILED); break;
141		case kCompleted: UpdateStatusAttribute(PSRV_JOB_STATUS_COMPLETED); break;
142		default: break;
143	}
144}
145
146// Synchronize file attribute with member variable
147void Job::UpdateAttribute() {
148	fValid = fValid || IsValidJobFile();
149	BNode node(&fEntry);
150	BString status;
151	if (node.InitCheck() == B_OK &&
152		node.ReadAttrString(PSRV_SPOOL_ATTR_STATUS, &status) == B_OK) {
153		UpdateStatus(status.String());
154	}
155}
156
157void Job::Remove() {
158	BEntry entry(&fEntry);
159	if (entry.InitCheck() == B_OK) entry.Remove();
160}
161
162// Implementation of Folder
163
164// BObjectList CompareFunction
165int Folder::AscendingByTime(const Job* a, const Job* b) {
166	return a->Time() - b->Time();
167}
168
169bool Folder::AddJob(BEntry& entry, bool notify) {
170	Job* job = new Job(entry, this);
171	if (job->InitCheck() == B_OK) {
172		fJobs.AddItem(job);
173		if (notify) Notify(job, kJobAdded);
174		return true;
175	} else {
176		job->Release();
177		return false;
178	}
179}
180
181// simplified assumption that ino_t identifies job file
182// will probabely not work in all cases with link to a file on another volume???
183Job* Folder::Find(node_ref* node) {
184	for (int i = 0; i < fJobs.CountItems(); i ++) {
185		Job* job = fJobs.ItemAt(i);
186		if (job->NodeRef() == *node) return job;
187	}
188	return NULL;
189}
190
191void Folder::EntryCreated(node_ref* node, entry_ref* entry) {
192	BEntry job(entry);
193	if (job.InitCheck() == B_OK && Lock()) {
194		if (AddJob(job)) {
195			fJobs.SortItems(AscendingByTime);
196		}
197		Unlock();
198	}
199}
200
201void Folder::EntryRemoved(node_ref* node) {
202	Job* job = Find(node);
203	if (job && Lock()) {
204		fJobs.RemoveItem(job);
205		Notify(job, kJobRemoved);
206		job->Release();
207		Unlock();
208	}
209}
210
211void Folder::AttributeChanged(node_ref* node) {
212	Job* job = Find(node);
213	if (job && Lock()) {
214		job->UpdateAttribute();
215		Notify(job, kJobAttrChanged);
216		Unlock();
217	}
218}
219
220// initial setup of job list
221void Folder::SetupJobList() {
222	if (inherited::Folder()->InitCheck() == B_OK) {
223		inherited::Folder()->Rewind();
224
225		BEntry entry;
226		while (inherited::Folder()->GetNextEntry(&entry) == B_OK) {
227			AddJob(entry, false);
228		}
229		fJobs.SortItems(AscendingByTime);
230	}
231}
232
233Folder::Folder(BLocker* locker, BLooper* looper, const BDirectory& spoolDir)
234	: FolderWatcher(looper, spoolDir, true)
235	, fLocker(locker)
236	, fJobs()
237{
238	SetListener(this);
239	if (Lock()) {
240		SetupJobList();
241		Unlock();
242	}
243}
244
245
246Folder::~Folder() {
247	if (!Lock()) return;
248		// release jobs
249	for (int i = 0; i < fJobs.CountItems(); i ++) {
250		Job* job = fJobs.ItemAt(i);
251		job->Release();
252	}
253	Unlock();
254}
255
256Job* Folder::GetNextJob() {
257	for (int i = 0; i < fJobs.CountItems(); i ++) {
258		Job* job = fJobs.ItemAt(i);
259		if (job->IsValid() && job->IsWaiting()) {
260			job->Acquire(); return job;
261		}
262	}
263	return NULL;
264}
265
266