1 /*
2 * Copyright 2001-2010, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Ithamar R. Adema
7 * Michael Pfeiffer
8 */
9 #include "Printer.h"
10
11 #include "BeUtils.h"
12 #include "pr_server.h"
13 #include "PrintAddOnServer.h"
14 #include "PrintServerApp.h"
15
16 // posix
17 #include <limits.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21
22 // BeOS API
23 #include <Application.h>
24 #include <Autolock.h>
25 #include <Message.h>
26 #include <NodeMonitor.h>
27 #include <String.h>
28 #include <StorageKit.h>
29 #include <SupportDefs.h>
30
31
SpoolFolder(BLocker * locker,BLooper * looper,const BDirectory & spoolDir)32 SpoolFolder::SpoolFolder(BLocker* locker, BLooper* looper,
33 const BDirectory& spoolDir)
34 : Folder(locker, looper, spoolDir)
35 {
36 }
37
38
39 // Notify print_server that there is a job file waiting for printing
40 void
Notify(Job * job,int kind)41 SpoolFolder::Notify(Job* job, int kind)
42 {
43 if ((kind == kJobAdded || kind == kJobAttrChanged)
44 && job->IsValid() && job->IsWaiting()) {
45 be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB);
46 }
47 }
48
49
50 // ---------------------------------------------------------------
51 BObjectList<Printer> Printer::sPrinters;
52
53
54 // ---------------------------------------------------------------
55 // Find [static]
56 //
57 // Searches the static object list for a printer object with the
58 // specified name.
59 //
60 // Parameters:
61 // name - Printer definition name we're looking for.
62 //
63 // Returns:
64 // Pointer to Printer object, or NULL if not found.
65 // ---------------------------------------------------------------
66 Printer*
Find(const BString & name)67 Printer::Find(const BString& name)
68 {
69 // Look in list to find printer definition
70 for (int32 idx = 0; idx < sPrinters.CountItems(); idx++) {
71 if (name == sPrinters.ItemAt(idx)->Name())
72 return sPrinters.ItemAt(idx);
73 }
74 return NULL;
75 }
76
77
78 Printer*
Find(node_ref * node)79 Printer::Find(node_ref* node)
80 {
81 node_ref n;
82 // Look in list to find printer definition
83 for (int32 idx = 0; idx < sPrinters.CountItems(); idx++) {
84 Printer* printer = sPrinters.ItemAt(idx);
85 printer->SpoolDir()->GetNodeRef(&n);
86 if (n == *node)
87 return printer;
88 }
89
90 // None found, so return NULL
91 return NULL;
92 }
93
94
95 Printer*
At(int32 idx)96 Printer::At(int32 idx)
97 {
98 return sPrinters.ItemAt(idx);
99 }
100
101
102 void
Remove(Printer * printer)103 Printer::Remove(Printer* printer)
104 {
105 sPrinters.RemoveItem(printer);
106 }
107
108
109 int32
CountPrinters()110 Printer::CountPrinters()
111 {
112 return sPrinters.CountItems();
113 }
114
115
116 // ---------------------------------------------------------------
117 // Printer [constructor]
118 //
119 // Initializes the printer object with data read from the
120 // attributes attached to the printer definition node.
121 //
122 // Parameters:
123 // node - Printer definition node for this printer.
124 //
125 // Returns:
126 // none.
127 // ---------------------------------------------------------------
Printer(const BDirectory * node,Resource * res)128 Printer::Printer(const BDirectory* node, Resource* res)
129 : Inherited(B_EMPTY_STRING),
130 fPrinter(gLock, be_app, *node),
131 fResource(res),
132 fSinglePrintThread(res->NeedsLocking()),
133 fJob(NULL),
134 fProcessing(0),
135 fAbort(false)
136 {
137 BString name;
138 // Set our name to the name of the passed node
139 if (SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name) == B_OK)
140 SetName(name.String());
141
142 // Add us to the global list of known printer definitions
143 sPrinters.AddItem(this);
144
145 ResetJobStatus();
146 }
147
148
~Printer()149 Printer::~Printer()
150 {
151 ((PrintServerApp*)be_app)->NotifyPrinterDeletion(this);
152 }
153
154
155 void
MessageReceived(BMessage * msg)156 Printer::MessageReceived(BMessage* msg)
157 {
158 switch(msg->what) {
159 case B_GET_PROPERTY:
160 case B_SET_PROPERTY:
161 case B_CREATE_PROPERTY:
162 case B_DELETE_PROPERTY:
163 case B_COUNT_PROPERTIES:
164 case B_EXECUTE_PROPERTY:
165 HandleScriptingCommand(msg);
166 break;
167
168 default:
169 Inherited::MessageReceived(msg);
170 }
171 }
172
173
174 // Remove printer spooler directory
175 status_t
Remove()176 Printer::Remove()
177 {
178 status_t rc = B_OK;
179 BPath path;
180
181 if ((rc = ::find_directory(B_USER_PRINTERS_DIRECTORY, &path)) == B_OK) {
182 path.Append(Name());
183 rc = rmdir(path.Path());
184 }
185
186 return rc;
187 }
188
189
190 status_t
FindPathToDriver(const char * driverName,BPath * path)191 Printer::FindPathToDriver(const char* driverName, BPath* path)
192 {
193 return PrintAddOnServer::FindPathToDriver(driverName, path);
194 }
195
196
197 // ---------------------------------------------------------------
198 // ConfigurePrinter
199 //
200 // Handles calling the printer addon's add_printer function.
201 //
202 // Parameters:
203 // driverName - the name of the printer driver add-on
204 // printerName - the name of the printer spool folder
205 //
206 // Returns:
207 // B_OK if successful or errorcode otherwise.
208 // ---------------------------------------------------------------
209 status_t
ConfigurePrinter(const char * driverName,const char * printerName)210 Printer::ConfigurePrinter(const char* driverName,
211 const char* printerName)
212 {
213 PrintAddOnServer addOn(driverName);
214 return addOn.AddPrinter(printerName);
215 }
216
217
218 // ---------------------------------------------------------------
219 // ConfigurePage
220 //
221 // Handles calling the printer addon's config_page function.
222 //
223 // Parameters:
224 // settings - Page settings to display. The contents of this
225 // message will be replaced with the new settings
226 // if the function returns success.
227 //
228 // Returns:
229 // B_OK if successful or errorcode otherwise.
230 // ---------------------------------------------------------------
231 status_t
ConfigurePage(BMessage & settings)232 Printer::ConfigurePage(BMessage& settings)
233 {
234 BString driver;
235 status_t result = GetDriverName(&driver);
236 if (result != B_OK)
237 return result;
238
239 PrintAddOnServer addOn(driver.String());
240 result = addOn.ConfigPage(SpoolDir(), &settings);
241 if (result == B_OK) {
242 AddCurrentPrinter(settings);
243 }
244 return result;
245 }
246
247
248 // ---------------------------------------------------------------
249 // ConfigureJob
250 //
251 // Handles calling the printer addon's config_job function.
252 //
253 // Parameters:
254 // settings - Job settings to display. The contents of this
255 // message will be replaced with the new settings
256 // if the function returns success.
257 //
258 // Returns:
259 // B_OK if successful or errorcode otherwise.
260 // ---------------------------------------------------------------
261 status_t
ConfigureJob(BMessage & settings)262 Printer::ConfigureJob(BMessage& settings)
263 {
264 BString driver;
265 status_t result = GetDriverName(&driver);
266 if (result != B_OK)
267 return result;
268
269 PrintAddOnServer addOn(driver.String());
270 result = addOn.ConfigJob(SpoolDir(), &settings);
271 if (result == B_OK)
272 AddCurrentPrinter(settings);
273
274 return result;
275 }
276
277
278 // ---------------------------------------------------------------
279 // HandleSpooledJobs
280 //
281 // Print spooled jobs in a new thread.
282 // ---------------------------------------------------------------
283 void
HandleSpooledJob()284 Printer::HandleSpooledJob()
285 {
286 BAutolock lock(gLock);
287 if (lock.IsLocked()
288 && (!fSinglePrintThread || fProcessing == 0) && FindSpooledJob()) {
289 StartPrintThread();
290 }
291 }
292
293
294 // ---------------------------------------------------------------
295 // GetDefaultSettings
296 //
297 // Retrieve the default configuration message from printer add-on
298 //
299 // Parameters:
300 // settings, output paramter.
301 //
302 // Returns:
303 // B_OK if successful or errorcode otherwise.
304 // ---------------------------------------------------------------
305 status_t
GetDefaultSettings(BMessage & settings)306 Printer::GetDefaultSettings(BMessage& settings)
307 {
308 BString driver;
309 status_t result = GetDriverName(&driver);
310 if (result != B_OK)
311 return result;
312
313 PrintAddOnServer addOn(driver.String());
314 result = addOn.DefaultSettings(SpoolDir(), &settings);
315 if (result == B_OK)
316 AddCurrentPrinter(settings);
317
318 return result;
319 }
320
321
322 void
AbortPrintThread()323 Printer::AbortPrintThread()
324 {
325 fAbort = true;
326 }
327
328
329 status_t
GetDriverName(BString * name)330 Printer::GetDriverName(BString* name)
331 {
332 return SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_DRV_NAME, name);
333 }
334
335
336 // ---------------------------------------------------------------
337 // AddCurrentPrinter
338 //
339 // Add printer name to message.
340 //
341 // Parameters:
342 // msg - message.
343 // ---------------------------------------------------------------
344 void
AddCurrentPrinter(BMessage & message)345 Printer::AddCurrentPrinter(BMessage& message)
346 {
347 BString name;
348 GetName(name);
349
350 message.RemoveName(PSRV_FIELD_CURRENT_PRINTER);
351 message.AddString(PSRV_FIELD_CURRENT_PRINTER, name.String());
352 }
353
354
355 // ---------------------------------------------------------------
356 // GetName
357 //
358 // Get the name from spool directory.
359 //
360 // Parameters:
361 // name - the name of the printer.
362 // ---------------------------------------------------------------
363 void
GetName(BString & name)364 Printer::GetName(BString& name)
365 {
366 if (SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name) != B_OK)
367 name = "Unknown Printer";
368 }
369
370
371 // ---------------------------------------------------------------
372 // ResetJobStatus
373 //
374 // Reset status of "processing" jobs to "waiting" at print_server start.
375 // ---------------------------------------------------------------
376 void
ResetJobStatus()377 Printer::ResetJobStatus()
378 {
379 if (fPrinter.Lock()) {
380 const int32 n = fPrinter.CountJobs();
381 for (int32 i = 0; i < n; i ++) {
382 Job* job = fPrinter.JobAt(i);
383 if (job->Status() == kProcessing)
384 job->SetStatus(kWaiting);
385 }
386 fPrinter.Unlock();
387 }
388 }
389
390
391 // ---------------------------------------------------------------
392 // HasCurrentPrinter
393 //
394 // Try to read the printer name from job file.
395 //
396 // Parameters:
397 // name - the printer name.
398 //
399 // Returns:
400 // true if successful.
401 // ---------------------------------------------------------------
402 bool
HasCurrentPrinter(BString & name)403 Printer::HasCurrentPrinter(BString& name)
404 {
405 BMessage settings;
406 // read settings from spool file and get printer name
407 BFile jobFile(&fJob->EntryRef(), B_READ_WRITE);
408 return jobFile.InitCheck() == B_OK
409 && jobFile.Seek(sizeof(print_file_header), SEEK_SET) == sizeof(print_file_header)
410 && settings.Unflatten(&jobFile) == B_OK
411 && settings.FindString(PSRV_FIELD_CURRENT_PRINTER, &name) == B_OK;
412 }
413
414
415 // ---------------------------------------------------------------
416 // MoveJob
417 //
418 // Try to move job to another printer folder.
419 //
420 // Parameters:
421 // name - the printer folder name.
422 //
423 // Returns:
424 // true if successful.
425 // ---------------------------------------------------------------
426 bool
MoveJob(const BString & name)427 Printer::MoveJob(const BString& name)
428 {
429 BPath file(&fJob->EntryRef());
430 BPath path;
431 file.GetParent(&path);
432 path.Append("..");
433 path.Append(name.String());
434 BDirectory dir(path.Path());
435 BEntry entry(&fJob->EntryRef());
436 // try to move job file to proper directory
437 return entry.MoveTo(&dir) == B_OK;
438 }
439
440
441 // ---------------------------------------------------------------
442 // FindSpooledJob
443 //
444 // Looks if there is a job waiting to be processed and moves
445 // jobs to the proper printer folder.
446 //
447 // Note:
448 // Our implementation of BPrintJob moves jobs to the
449 // proper printer folder.
450 //
451 //
452 // Returns:
453 // true if there is a job present in fJob.
454 // ---------------------------------------------------------------
455 bool
FindSpooledJob()456 Printer::FindSpooledJob()
457 {
458 BString name2;
459 GetName(name2);
460 do {
461 fJob = fPrinter.GetNextJob();
462 if (fJob) {
463 BString name;
464 if (HasCurrentPrinter(name) && name != name2 && MoveJob(name)) {
465 // job in wrong printer folder moved to apropriate one
466 fJob->SetStatus(kUnknown, false); // so that fPrinter.GetNextJob skips it
467 fJob->Release();
468 } else {
469 // job found
470 fJob->SetPrinter(this);
471 return true;
472 }
473 }
474 } while (fJob != NULL);
475 return false;
476 }
477
478
479 // ---------------------------------------------------------------
480 // PrintSpooledJob
481 //
482 // Loads the printer add-on and calls its take_job function with
483 // the spool file as argument.
484 //
485 // Parameters:
486 // spoolFile - the path to the spool file.
487 //
488 // Returns:
489 // B_OK if successful.
490 // ---------------------------------------------------------------
491 status_t
PrintSpooledJob(const char * spoolFile)492 Printer::PrintSpooledJob(const char* spoolFile)
493 {
494 BString driver;
495 status_t result = GetDriverName(&driver);
496 if (result != B_OK)
497 return result;
498
499 PrintAddOnServer addOn(driver.String());
500 return addOn.TakeJob(spoolFile, SpoolDir());
501 }
502
503
504 // ---------------------------------------------------------------
505 // PrintThread
506 //
507 // Loads the printer add-on and calls its take_job function with
508 // the spool file as argument.
509 //
510 // Parameters:
511 // job - the spool job.
512 // ---------------------------------------------------------------
513 void
PrintThread(Job * job)514 Printer::PrintThread(Job* job)
515 {
516 // Wait until resource is available
517 fResource->Lock();
518 bool failed = true;
519 // Can we continue?
520 if (!fAbort) {
521 BPath path;
522 bool canOpenFile;
523 {
524 BEntry entry(&job->EntryRef());
525 path.SetTo(&entry);
526 BFile jobFile(path.Path(), B_READ_WRITE);
527 canOpenFile = jobFile.InitCheck() == B_OK;
528 }
529 // Tell the printer to print the spooled job
530 if (canOpenFile && PrintSpooledJob(path.Path()) == B_OK) {
531 // Remove spool file if printing was successful.
532 job->Remove(); failed = false;
533 }
534 }
535 // Set status of spooled job on error
536 if (failed)
537 job->SetStatus(kFailed);
538 fResource->Unlock();
539 job->Release();
540 atomic_add(&fProcessing, -1);
541 Release();
542 // Notify print_server to process next spooled job
543 be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB);
544 }
545
546
547 // ---------------------------------------------------------------
548 // print_thread
549 //
550 // Print thread entry, calls PrintThread with spool job.
551 //
552 // Parameters:
553 // data - spool job.
554 //
555 // Returns:
556 // 0 always.
557 // ---------------------------------------------------------------
558 status_t
print_thread(void * data)559 Printer::print_thread(void* data)
560 {
561 Job* job = static_cast<Job*>(data);
562 job->GetPrinter()->PrintThread(job);
563 return 0;
564 }
565
566
567 // ---------------------------------------------------------------
568 // StartPrintThread
569 //
570 // Sets the status of the current spool job to "processing" and
571 // starts the print_thread.
572 // ---------------------------------------------------------------
573 void
StartPrintThread()574 Printer::StartPrintThread()
575 {
576 Acquire();
577 thread_id tid = spawn_thread(print_thread, "print", B_NORMAL_PRIORITY, (void*)fJob);
578 if (tid > 0) {
579 fJob->SetStatus(kProcessing);
580 atomic_add(&fProcessing, 1);
581 resume_thread(tid);
582 } else {
583 fJob->Release();
584 Release();
585 }
586 }
587
588