xref: /haiku/src/servers/print/PrintServerApp.cpp (revision 5e96d7d537fbec23bad4ae9b4c8e7b02e769f0c6)
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 "PrintServerApp.h"
10 
11 #include "BeUtils.h"
12 #include "Printer.h"
13 #include "pr_server.h"
14 #include "Transport.h"
15 
16 // BeOS API
17 #include <Alert.h>
18 #include <Autolock.h>
19 #include <Catalog.h>
20 #include <Directory.h>
21 #include <File.h>
22 #include <FindDirectory.h>
23 #include <image.h>
24 #include <Locale.h>
25 #include <Mime.h>
26 #include <NodeInfo.h>
27 #include <NodeMonitor.h>
28 #include <Path.h>
29 #include <Roster.h>
30 #include <PrintJob.h>
31 #include <String.h>
32 
33 // ANSI C
34 #include <stdio.h>
35 	// for printf
36 #include <unistd.h>
37 	// for unlink
38 
39 
40 #undef B_TRANSLATION_CONTEXT
41 #define B_TRANSLATION_CONTEXT "PrintServerApp"
42 
43 
44 typedef struct _printer_data {
45 	char defaultPrinterName[256];
46 } printer_data_t;
47 
48 
49 
50 static const char* kSettingsName = "print_server_settings";
51 
52 
53 BLocker *gLock = NULL;
54 
55 
56 /**
57  * Main entry point of print_server.
58  *
59  * @returns B_OK if application was started, or an errorcode if
60  *          application failed to start.
61  */
62 int
63 main()
64 {
65 	status_t rc = B_OK;
66 	gLock = new BLocker();
67 	PrintServerApp print_server(&rc);
68 	if (rc == B_OK) {
69 		print_server.Run();
70 	}
71 	delete gLock;
72 	return rc;
73 }
74 
75 
76 /**
77  * Constructor for print_server's application class. Retrieves the
78  * name of the default printer from storage, caches the icons for
79  * a selected printer.
80  *
81  * @param err Pointer to status_t for storing result of application
82  *          initialisation.
83  *
84  * @see BApplication
85  */
86 PrintServerApp::PrintServerApp(status_t* err)
87 	: Inherited(PSRV_SIGNATURE_TYPE, err),
88 	fDefaultPrinter(NULL),
89 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
90 	fIconSize(0),
91 	fSelectedIcon(NULL),
92 #else
93 	fSelectedIconMini(NULL),
94 	fSelectedIconLarge(NULL),
95 #endif
96 	fReferences(0),
97 	fHasReferences(0),
98 	fUseConfigWindow(true),
99 	fFolder(NULL)
100 {
101 	fSettings = Settings::GetSettings();
102 	LoadSettings();
103 
104 	if (*err != B_OK)
105 		return;
106 
107 	fHasReferences = create_sem(1, "has_references");
108 
109 	// Build list of transport addons
110 	Transport::Scan(B_USER_NONPACKAGED_ADDONS_DIRECTORY);
111 	Transport::Scan(B_USER_ADDONS_DIRECTORY);
112 	Transport::Scan(B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY);
113 	Transport::Scan(B_SYSTEM_ADDONS_DIRECTORY);
114 
115 	SetupPrinterList();
116 	RetrieveDefaultPrinter();
117 
118 	// Cache icons for selected printer
119 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
120 	BMimeType type(PSRV_PRINTER_FILETYPE);
121 	type.GetIcon(&fSelectedIcon, &fIconSize);
122 #else
123 	fSelectedIconMini = new BBitmap(BRect(0,0,B_MINI_ICON-1,B_MINI_ICON-1), B_CMAP8);
124 	fSelectedIconLarge = new BBitmap(BRect(0,0,B_LARGE_ICON-1,B_LARGE_ICON-1), B_CMAP8);
125 	BMimeType type(PRNT_SIGNATURE_TYPE);
126 	type.GetIcon(fSelectedIconMini, B_MINI_ICON);
127 	type.GetIcon(fSelectedIconLarge, B_LARGE_ICON);
128 #endif
129 
130 	PostMessage(PSRV_PRINT_SPOOLED_JOB);
131 		// Start handling of spooled files
132 }
133 
134 
135 PrintServerApp::~PrintServerApp()
136 {
137 	SaveSettings();
138 	delete fSettings;
139 }
140 
141 
142 bool
143 PrintServerApp::QuitRequested()
144 {
145 	// don't quit when user types Command+Q!
146 	BMessage* m = CurrentMessage();
147 	bool shortcut;
148 	if (m != NULL && m->FindBool("shortcut", &shortcut) == B_OK && shortcut)
149 		return false;
150 
151 	if (!Inherited::QuitRequested())
152 		return false;
153 
154 	// Stop watching the folder
155 	delete fFolder; fFolder = NULL;
156 
157 	// Release all printers
158 	Printer* printer;
159 	while ((printer = Printer::At(0)) != NULL) {
160 		printer->AbortPrintThread();
161 		UnregisterPrinter(printer);
162 	}
163 
164 	// Wait for printers
165 	if (fHasReferences > 0) {
166 		acquire_sem(fHasReferences);
167 		delete_sem(fHasReferences);
168 		fHasReferences = 0;
169 	}
170 
171 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
172 	delete [] fSelectedIcon;
173 	fSelectedIcon = NULL;
174 #else
175 	delete fSelectedIconMini;
176 	fSelectedIconMini = NULL;
177 	delete fSelectedIconLarge;
178 	fSelectedIconLarge = NULL;
179 #endif
180 
181 	return true;
182 }
183 
184 
185 void
186 PrintServerApp::Acquire()
187 {
188 	if (atomic_add(&fReferences, 1) == 0)
189 		acquire_sem(fHasReferences);
190 }
191 
192 
193 void
194 PrintServerApp::Release()
195 {
196 	if (atomic_add(&fReferences, -1) == 1)
197 		release_sem(fHasReferences);
198 }
199 
200 
201 void
202 PrintServerApp::RegisterPrinter(BDirectory* printer)
203 {
204 	BString transport, address, connection, state;
205 
206 	if (printer->ReadAttrString(PSRV_PRINTER_ATTR_TRANSPORT, &transport) == B_OK
207 		&& printer->ReadAttrString(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, &address)
208 			== B_OK
209 		&& printer->ReadAttrString(PSRV_PRINTER_ATTR_CNX, &connection) == B_OK
210 		&& printer->ReadAttrString(PSRV_PRINTER_ATTR_STATE, &state) == B_OK
211 		&& state == "free") {
212 
213  		BAutolock lock(gLock);
214 		if (lock.IsLocked()) {
215 			// check if printer is already registered
216 			node_ref node;
217 			if (printer->GetNodeRef(&node) != B_OK)
218 				return;
219 
220 			if (Printer::Find(&node) != NULL)
221 				return;
222 
223 			// register new printer
224 			Resource* r = fResourceManager.Allocate(transport.String(),
225 				address.String(), connection.String());
226 		 	AddHandler(new Printer(printer, r));
227 		 	Acquire();
228 		}
229 	}
230 }
231 
232 
233 void
234 PrintServerApp::UnregisterPrinter(Printer* printer)
235 {
236 	RemoveHandler(printer);
237 	Printer::Remove(printer);
238 	printer->Release();
239 }
240 
241 
242 void
243 PrintServerApp::NotifyPrinterDeletion(Printer* printer)
244 {
245 	BAutolock lock(gLock);
246 	if (lock.IsLocked()) {
247 		fResourceManager.Free(printer->GetResource());
248 		Release();
249 	}
250 }
251 
252 
253 void
254 PrintServerApp::EntryCreated(node_ref* node, entry_ref* entry)
255 {
256 	BEntry printer(entry);
257 	if (printer.InitCheck() == B_OK && printer.IsDirectory()) {
258 		BDirectory dir(&printer);
259 		if (dir.InitCheck() == B_OK) RegisterPrinter(&dir);
260 	}
261 }
262 
263 
264 void
265 PrintServerApp::EntryRemoved(node_ref* node)
266 {
267 	Printer* printer = Printer::Find(node);
268 	if (printer) {
269 		if (printer == fDefaultPrinter) fDefaultPrinter = NULL;
270 		UnregisterPrinter(printer);
271 	}
272 }
273 
274 
275 void
276 PrintServerApp::AttributeChanged(node_ref* node)
277 {
278 	BDirectory printer(node);
279 	if (printer.InitCheck() == B_OK) {
280 		RegisterPrinter(&printer);
281 	}
282 }
283 
284 
285 // ---------------------------------------------------------------
286 // SetupPrinterList
287 //
288 // This method builds the internal list of printers from disk. It
289 // also installs a node monitor to be sure that the list keeps
290 // updated with the definitions on disk.
291 //
292 // Parameters:
293 //    none.
294 //
295 // Returns:
296 //    B_OK if successful, or an errorcode if failed.
297 // ---------------------------------------------------------------
298 status_t
299 PrintServerApp::SetupPrinterList()
300 {
301 	status_t rc;
302 
303 	// Find directory containing printer definition nodes
304 	BPath path;
305 	rc = ::find_directory(B_USER_PRINTERS_DIRECTORY, &path);
306 	if (rc != B_OK)
307 		return rc;
308 
309 	// Directory has to exist in order to watch it
310 	mode_t mode = 0777;
311 	create_directory(path.Path(), mode);
312 
313 	BDirectory dir(path.Path());
314 	rc = dir.InitCheck();
315 	if (rc != B_OK)
316 		return rc;
317 
318 	// Register printer definition nodes
319 	BEntry entry;
320 	while(dir.GetNextEntry(&entry) == B_OK) {
321 		if (!entry.IsDirectory())
322 			continue;
323 
324 		BDirectory node(&entry);
325 		BNodeInfo info(&node);
326 		char buffer[256];
327 		if (info.GetType(buffer) == B_OK
328 			&& strcmp(buffer, PSRV_PRINTER_FILETYPE) == 0) {
329 			RegisterPrinter(&node);
330 		}
331 	}
332 
333 	// Now we are ready to start node watching
334 	fFolder = new FolderWatcher(this, dir, true);
335 	fFolder->SetListener(this);
336 
337 	return B_OK;
338 }
339 
340 // ---------------------------------------------------------------
341 // void MessageReceived(BMessage* msg)
342 //
343 // Message handling method for print_server application class.
344 //
345 // Parameters:
346 //      msg - Actual message sent to application class.
347 //
348 // Returns:
349 //      void.
350 // ---------------------------------------------------------------
351 void
352 PrintServerApp::MessageReceived(BMessage* msg)
353 {
354 	switch(msg->what) {
355 		case PSRV_GET_ACTIVE_PRINTER:
356 		case PSRV_MAKE_PRINTER_ACTIVE_QUIETLY:
357 		case PSRV_MAKE_PRINTER_ACTIVE:
358 		case PSRV_MAKE_PRINTER:
359 		case PSRV_SHOW_PAGE_SETUP:
360 		case PSRV_SHOW_PRINT_SETUP:
361 		case PSRV_PRINT_SPOOLED_JOB:
362 		case PSRV_GET_DEFAULT_SETTINGS:
363 			Handle_BeOSR5_Message(msg);
364 			break;
365 
366 		case B_GET_PROPERTY:
367 		case B_SET_PROPERTY:
368 		case B_CREATE_PROPERTY:
369 		case B_DELETE_PROPERTY:
370 		case B_COUNT_PROPERTIES:
371 		case B_EXECUTE_PROPERTY:
372 			HandleScriptingCommand(msg);
373 			break;
374 
375 		default:
376 			Inherited::MessageReceived(msg);
377 	}
378 }
379 
380 
381 // ---------------------------------------------------------------
382 // CreatePrinter(const char* printerName, const char* driverName,
383 //               const char* connection, const char* transportName,
384 //               const char* transportPath)
385 //
386 // Creates printer definition/spool directory. It sets the
387 // attributes of the directory to the values passed and calls
388 // the driver's add_printer method to handle any configuration
389 // needed.
390 //
391 // Parameters:
392 //    printerName - Name of printer to create.
393 //    driverName - Name of driver to use for this printer.
394 //    connection - "Local" or "Network".
395 //    transportName - Name of transport driver to use.
396 //    transportPath  - Configuration data for transport driver.
397 //
398 // Returns:
399 // ---------------------------------------------------------------
400 status_t
401 PrintServerApp::CreatePrinter(const char* printerName, const char* driverName,
402 	const char* connection, const char* transportName,
403 	const char* transportPath)
404 {
405 	status_t rc;
406 
407 	// Find directory containing printer definitions
408 	BPath path;
409 	rc = ::find_directory(B_USER_PRINTERS_DIRECTORY,&path,true,NULL);
410 	if (rc != B_OK)
411 		return rc;
412 
413 	// Create our printer definition/spool directory
414 	BDirectory printersDir(path.Path());
415 	BDirectory printer;
416 
417 	rc = printersDir.CreateDirectory(printerName, &printer);
418 	if (rc == B_FILE_EXISTS) {
419 		printer.SetTo(&printersDir, printerName);
420 
421 		BString info;
422 		char type[B_MIME_TYPE_LENGTH];
423 		BNodeInfo(&printer).GetType(type);
424 		if (strcmp(PSRV_PRINTER_FILETYPE, type) == 0) {
425 			BPath path;
426 			if (Printer::FindPathToDriver(printerName, &path) == B_OK) {
427 				if (fDefaultPrinter) {
428 					// the printer exists, but is not the default printer
429 					if (strcmp(fDefaultPrinter->Name(), printerName) != 0)
430 						rc = B_OK;
431 					return rc;
432 				}
433 				// the printer exists, but no default at all
434 				return B_OK;
435 			} else {
436 				info.SetTo(B_TRANSLATE(
437 					"A printer with that name already exists, "
438 					"but its driver could not be found! Replace?"));
439 			}
440 		} else {
441 			info.SetTo(B_TRANSLATE(
442 				"A printer with that name already exists, "
443 				"but it's not usable at all! Replace?"));
444 		}
445 
446 		if (info.Length() != 0) {
447 			BAlert *alert = new BAlert("Info", info.String(),
448 				B_TRANSLATE("Cancel"), B_TRANSLATE("OK"));
449 			alert->SetShortcut(0, B_ESCAPE);
450 			if (alert->Go() == 0)
451 				return rc;
452 		}
453 	} else if (rc != B_OK) {
454 		return rc;
455 	}
456 
457 	// Set its type to a printer
458 	BNodeInfo info(&printer);
459 	info.SetType(PSRV_PRINTER_FILETYPE);
460 
461 	// Store the settings in its attributes
462 	printer.WriteAttr(PSRV_PRINTER_ATTR_PRT_NAME, B_STRING_TYPE, 0, printerName,
463 		::strlen(printerName) + 1);
464 	printer.WriteAttr(PSRV_PRINTER_ATTR_DRV_NAME, B_STRING_TYPE, 0, driverName,
465 		::strlen(driverName) + 1);
466 	printer.WriteAttr(PSRV_PRINTER_ATTR_TRANSPORT, B_STRING_TYPE, 0,
467 		transportName, ::strlen(transportName) + 1);
468 	printer.WriteAttr(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, B_STRING_TYPE, 0,
469 		transportPath, ::strlen(transportPath) + 1);
470 	printer.WriteAttr(PSRV_PRINTER_ATTR_CNX, B_STRING_TYPE, 0, connection,
471 		::strlen(connection) + 1);
472 
473 	rc = Printer::ConfigurePrinter(driverName, printerName);
474 	if (rc == B_OK) {
475 		// Notify printer driver that a new printer definition node
476 		// has been created.
477 		printer.WriteAttr(PSRV_PRINTER_ATTR_STATE, B_STRING_TYPE, 0, "free",
478 			::strlen("free")+1);
479 	}
480 
481 	if (rc != B_OK) {
482 		BEntry entry;
483 		if (printer.GetEntry(&entry) == B_OK)
484 			entry.Remove();
485 	}
486 
487 	return rc;
488 }
489 
490 
491 // ---------------------------------------------------------------
492 // SelectPrinter(const char* printerName)
493 //
494 // Makes a new printer the active printer. This is done simply
495 // by changing our class attribute fDefaultPrinter, and changing
496 // the icon of the BNode for the printer. Ofcourse, we need to
497 // change the icon of the "old" default printer first back to a
498 // "non-active" printer icon first.
499 //
500 // Parameters:
501 //		printerName - Name of the new active printer.
502 //
503 // Returns:
504 //      B_OK on success, or error code otherwise.
505 // ---------------------------------------------------------------
506 status_t
507 PrintServerApp::SelectPrinter(const char* printerName)
508 {
509 	status_t rc;
510 	BNode node;
511 
512 	// Find the node of the "old" default printer
513 	if (fDefaultPrinter != NULL
514 		&& FindPrinterNode(fDefaultPrinter->Name(), node) == B_OK) {
515 		// and remove the custom icon
516 		BNodeInfo info(&node);
517 		info.SetIcon(NULL, B_MINI_ICON);
518 		info.SetIcon(NULL, B_LARGE_ICON);
519 	}
520 
521 	// Find the node for the new default printer
522 	rc=FindPrinterNode(printerName, node);
523 	if (rc == B_OK) {
524 		// and add the custom icon
525 		BNodeInfo info(&node);
526 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
527 		info.SetIcon(fSelectedIcon, fIconSize);
528 #else
529 		info.SetIcon(fSelectedIconMini, B_MINI_ICON);
530 		info.SetIcon(fSelectedIconLarge, B_LARGE_ICON);
531 #endif
532 	}
533 
534 	fDefaultPrinter = Printer::Find(printerName);
535 	StoreDefaultPrinter();
536 		// update our pref file
537 	be_roster->Broadcast(new BMessage(B_PRINTER_CHANGED));
538 
539 	return rc;
540 }
541 
542 
543 // ---------------------------------------------------------------
544 // HandleSpooledJobs()
545 //
546 // Handles calling the printer drivers for printing a spooled job.
547 //
548 // ---------------------------------------------------------------
549 void
550 PrintServerApp::HandleSpooledJobs()
551 {
552 	const int n = Printer::CountPrinters();
553 	for (int i = 0; i < n; i ++) {
554 		Printer* printer = Printer::At(i);
555 		printer->HandleSpooledJob();
556 	}
557 }
558 
559 
560 // ---------------------------------------------------------------
561 // RetrieveDefaultPrinter()
562 //
563 // Loads the currently selected printer from a private settings
564 // file.
565 //
566 // Parameters:
567 //     none.
568 //
569 // Returns:
570 //     Error code on failore, or B_OK if all went fine.
571 // ---------------------------------------------------------------
572 status_t
573 PrintServerApp::RetrieveDefaultPrinter()
574 {
575 	fDefaultPrinter = Printer::Find(fSettings->DefaultPrinter());
576 	return B_OK;
577 }
578 
579 
580 // ---------------------------------------------------------------
581 // StoreDefaultPrinter()
582 //
583 // Stores the currently selected printer in a private settings
584 // file.
585 //
586 // Parameters:
587 //     none.
588 //
589 // Returns:
590 //     Error code on failore, or B_OK if all went fine.
591 // ---------------------------------------------------------------
592 status_t
593 PrintServerApp::StoreDefaultPrinter()
594 {
595 	if (fDefaultPrinter)
596 		fSettings->SetDefaultPrinter(fDefaultPrinter->Name());
597 	else
598 		fSettings->SetDefaultPrinter("");
599 	return B_OK;
600 }
601 
602 
603 // ---------------------------------------------------------------
604 // FindPrinterNode(const char* name, BNode& node)
605 //
606 // Find the BNode representing the specified printer. It searches
607 // *only* in the users printer definitions.
608 //
609 // Parameters:
610 //     name - Name of the printer to look for.
611 //     node - BNode to set to the printer definition node.
612 //
613 // Returns:
614 //     B_OK if found, an error code otherwise.
615 // ---------------------------------------------------------------
616 status_t
617 PrintServerApp::FindPrinterNode(const char* name, BNode& node)
618 {
619 	// Find directory containing printer definitions
620 	BPath path;
621 	status_t rc = ::find_directory(B_USER_PRINTERS_DIRECTORY, &path, true, NULL);
622 	if (rc != B_OK)
623 		return rc;
624 
625 	path.Append(name);
626 	return node.SetTo(path.Path());
627 }
628 
629 
630 bool
631 PrintServerApp::OpenSettings(BFile& file, const char* name,
632 	bool forReading)
633 {
634 	BPath path;
635 	uint32 openMode = forReading ? B_READ_ONLY : B_CREATE_FILE | B_ERASE_FILE
636 		| B_WRITE_ONLY;
637 	return find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK
638 		&& path.Append(name) == B_OK
639 		&& file.SetTo(path.Path(), openMode) == B_OK;
640 }
641 
642 
643 void
644 PrintServerApp::LoadSettings() {
645 	BFile file;
646 	if (OpenSettings(file, kSettingsName, true)) {
647 		fSettings->Load(&file);
648 		fUseConfigWindow = fSettings->UseConfigWindow();
649 	}
650 }
651 
652 
653 void
654 PrintServerApp::SaveSettings() {
655 	BFile file;
656 	if (OpenSettings(file, kSettingsName, false)) {
657 		fSettings->SetUseConfigWindow(fUseConfigWindow);
658 		fSettings->Save(&file);
659 	}
660 }
661