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