xref: /haiku/src/servers/print/PrintServerApp.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 /*****************************************************************************/
2 // print_server Background Application.
3 //
4 // Version: 1.0.0d1
5 //
6 // The print_server manages the communication between applications and the
7 // printer and transport drivers.
8 //
9 // Authors
10 //   Ithamar R. Adema
11 //   Michael Pfeiffer
12 //
13 // This application and all source files used in its construction, except
14 // where noted, are licensed under the MIT License, and have been written
15 // and are:
16 //
17 // Copyright (c) 2001, 2002 OpenBeOS Project
18 //
19 // Permission is hereby granted, free of charge, to any person obtaining a
20 // copy of this software and associated documentation files (the "Software"),
21 // to deal in the Software without restriction, including without limitation
22 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
23 // and/or sell copies of the Software, and to permit persons to whom the
24 // Software is furnished to do so, subject to the following conditions:
25 //
26 // The above copyright notice and this permission notice shall be included
27 // in all copies or substantial portions of the Software.
28 //
29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
30 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
32 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
34 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
35 // DEALINGS IN THE SOFTWARE.
36 /*****************************************************************************/
37 
38 #include "PrintServerApp.h"
39 
40 #include "BeUtils.h"
41 #include "Printer.h"
42 #include "pr_server.h"
43 
44 	// BeOS API
45 #include <Alert.h>
46 #include <Autolock.h>
47 #include <Directory.h>
48 #include <File.h>
49 #include <image.h>
50 #include <FindDirectory.h>
51 #include <Mime.h>
52 #include <NodeInfo.h>
53 #include <NodeMonitor.h>
54 #include <Path.h>
55 #include <Roster.h>
56 #include <PrintJob.h>
57 #include <String.h>
58 
59 	// ANSI C
60 #include <stdio.h>	//for printf
61 #include <unistd.h> //for unlink
62 
63 BLocker *gLock = NULL;
64 
65 // ---------------------------------------------------------------
66 typedef struct _printer_data {
67 	char defaultPrinterName[256];
68 } printer_data_t;
69 
70 typedef BMessage* (*config_func_t)(BNode*, const BMessage*);
71 typedef BMessage* (*take_job_func_t)(BFile*, BNode*, const BMessage*);
72 typedef char* (*add_printer_func_t)(const char* printer_name);
73 
74 /**
75  * Main entry point of print_server.
76  *
77  * @returns B_OK if application was started, or an errorcode if
78  *          application failed to start.
79  *
80  * @see your favorite C/C++ textbook :P
81  */
82 
83 int
84 main()
85 {
86 	status_t rc = B_OK;
87 	gLock = new BLocker();
88 		// Create our application object
89 	PrintServerApp print_server(&rc);
90 		// If all went fine, let's start it
91 	if (rc == B_OK) {
92 		print_server.Run();
93 	}
94 	delete gLock;
95 	return rc;
96 }
97 
98 /**
99  * Constructor for print_server's application class. Retrieves the
100  * name of the default printer from storage, caches the icons for
101  * a selected printer.
102  *
103  * @param err Pointer to status_t for storing result of application
104  *          initialisation.
105  *
106  * @see BApplication
107  */
108 
109 PrintServerApp::PrintServerApp(status_t* err)
110 	: Inherited(PSRV_SIGNATURE_TYPE, err),
111 	fSelectedIconMini(NULL),
112 	fSelectedIconLarge(NULL),
113 	fReferences(0),
114 	fHasReferences(0),
115 	fUseConfigWindow(true),
116 	fFolder(NULL)
117 {
118 	fSettings = Settings::GetSettings();
119 	LoadSettings();
120 		// If our superclass initialized ok
121 	if (*err == B_OK) {
122 		fHasReferences = create_sem(1, "has_references");
123 
124 			// let us try as well
125 		SetupPrinterList();
126 		RetrieveDefaultPrinter();
127 
128 		fSelectedIconMini = new BBitmap(BRect(0,0,B_MINI_ICON-1,B_MINI_ICON-1), B_CMAP8);
129 		fSelectedIconLarge = new BBitmap(BRect(0,0,B_LARGE_ICON-1,B_LARGE_ICON-1), B_CMAP8);
130 
131 			// Cache icons for selected printer
132 		BMimeType type(PRNT_SIGNATURE_TYPE);
133 		type.GetIcon(fSelectedIconMini, B_MINI_ICON);
134 		type.GetIcon(fSelectedIconLarge, B_LARGE_ICON);
135 
136 			// Start handling of spooled files
137 		PostMessage(PSRV_PRINT_SPOOLED_JOB);
138 	}
139 }
140 
141 PrintServerApp::~PrintServerApp() {
142 	SaveSettings();
143 	delete fSettings;
144 }
145 
146 bool PrintServerApp::QuitRequested()
147 {
148 	BMessage* m = CurrentMessage();
149 	bool shortcut;
150 		// don't quit when user types Command+Q!
151 	if (m && m->FindBool("shortcut", &shortcut) == B_OK && shortcut) return false;
152 
153 	bool rc = Inherited::QuitRequested();
154 	if (rc) {
155 		delete fFolder; fFolder = NULL;
156 #if 0
157 			// Find directory containing printer definition nodes
158 		BPath path;
159 		if (::find_directory(B_USER_PRINTERS_DIRECTORY, &path) == B_OK) {
160 			BEntry entry(path.Path());
161 			node_ref nref;
162 			if (entry.InitCheck() == B_OK &&
163 				entry.GetNodeRef(&nref) == B_OK) {
164 					// Stop watching the printer directory
165 				::watch_node(&nref, B_STOP_WATCHING, be_app_messenger);
166 			}
167 		}
168 #endif
169 
170 			// Release all printers
171 		Printer* printer;
172 		while ((printer = Printer::At(0)) != NULL) {
173 			printer->AbortPrintThread();
174 			UnregisterPrinter(printer);
175 		}
176 
177 			// Wait for printers
178 		if (fHasReferences > 0) {
179 			acquire_sem(fHasReferences);
180 			delete_sem(fHasReferences);
181 			fHasReferences = 0;
182 		}
183 
184 		ASSERT(fSelectedIconMini != NULL);
185 
186 		delete fSelectedIconMini; fSelectedIconMini = NULL;
187 		delete fSelectedIconLarge; fSelectedIconLarge = NULL;
188 	}
189 
190 	return rc;
191 }
192 
193 void PrintServerApp::Acquire() {
194 		if (atomic_add(&fReferences, 1) == 0) acquire_sem(fHasReferences);
195 }
196 
197 void PrintServerApp::Release() {
198 		if (atomic_add(&fReferences, -1) == 1) release_sem(fHasReferences);
199 }
200 
201 void PrintServerApp::RegisterPrinter(BDirectory* printer) {
202 	BString transport, address, connection, state;
203 
204 	if (printer->ReadAttrString(PSRV_PRINTER_ATTR_TRANSPORT, &transport) == B_OK &&
205 		printer->ReadAttrString(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, &address) == B_OK &&
206 		printer->ReadAttrString(PSRV_PRINTER_ATTR_CNX, &connection) == B_OK &&
207 		printer->ReadAttrString(PSRV_PRINTER_ATTR_STATE, &state) == B_OK &&
208 		state == "free") {
209 
210  		BAutolock lock(gLock);
211 		if (lock.IsLocked()) {
212 				// check if printer is already registered
213 			node_ref node;
214 			Printer* p;
215 			if (printer->GetNodeRef(&node) != B_OK) return;
216 			p = Printer::Find(&node);
217 			if (p != NULL) return;
218 				// register new printer
219 			Resource* r = fResourceManager.Allocate(transport.String(), address.String(), connection.String());
220 		 	p = new Printer(printer, r);
221 		 	AddHandler(p);
222 		 	Acquire();
223 		}
224 	}
225 }
226 
227 void PrintServerApp::UnregisterPrinter(Printer* printer) {
228 	RemoveHandler(printer);
229 	Printer::Remove(printer);
230 	printer->Release();
231 }
232 
233 void PrintServerApp::NotifyPrinterDeletion(Printer* printer) {
234 	BAutolock lock(gLock);
235 	if (lock.IsLocked()) {
236 		fResourceManager.Free(printer->GetResource());
237 		Release();
238 	}
239 }
240 
241 
242 void PrintServerApp::EntryCreated(node_ref* node, entry_ref* entry) {
243 	BEntry printer(entry);
244 	if (printer.InitCheck() == B_OK && printer.IsDirectory()) {
245 		BDirectory dir(&printer);
246 		if (dir.InitCheck() == B_OK) RegisterPrinter(&dir);
247 	}
248 }
249 
250 void PrintServerApp::EntryRemoved(node_ref* node) {
251 	Printer* printer = Printer::Find(node);
252 	if (printer) {
253 		if (printer == fDefaultPrinter) fDefaultPrinter = NULL;
254 		UnregisterPrinter(printer);
255 	}
256 }
257 
258 void PrintServerApp::AttributeChanged(node_ref* node) {
259 	BDirectory printer(node);
260 	if (printer.InitCheck() == B_OK) {
261 		RegisterPrinter(&printer);
262 	}
263 }
264 
265 // ---------------------------------------------------------------
266 // SetupPrinterList
267 //
268 // This method builds the internal list of printers from disk. It
269 // also installs a node monitor to be sure that the list keeps
270 // updated with the definitions on disk.
271 //
272 // Parameters:
273 //    none.
274 //
275 // Returns:
276 //    B_OK if successful, or an errorcode if failed.
277 // ---------------------------------------------------------------
278 status_t PrintServerApp::SetupPrinterList()
279 {
280 	BEntry entry;
281 	status_t rc;
282 	BPath path;
283 
284 		// Find directory containing printer definition nodes
285 	if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY, &path)) == B_OK) {
286 		BDirectory dir(path.Path());
287 
288 			// Can we reach the directory?
289 		if ((rc=dir.InitCheck()) == B_OK) {
290 				// Yes, so loop over it's entries
291 			while((rc=dir.GetNextEntry(&entry)) == B_OK) {
292 					// If the entry is a directory
293 				if (entry.IsDirectory()) {
294 						// Check it's Mime type for a spool director
295 					BDirectory node(&entry);
296 					BNodeInfo info(&node);
297 					char buffer[256];
298 
299 					if (info.GetType(buffer) == B_OK &&
300 						strcmp(buffer, PSRV_PRINTER_FILETYPE) == 0) {
301 							// Yes, it is a printer definition node
302 						RegisterPrinter(&node);
303 					}
304 				}
305 			}
306 
307 
308 			fFolder = new FolderWatcher(this, dir, true);
309 			fFolder->SetListener(this);
310 #if 0
311 				// If we scanned all entries successfully
312 			node_ref nref;
313 			if (rc == B_ENTRY_NOT_FOUND &&
314 				(rc=dir.GetNodeRef(&nref)) == B_OK) {
315 					// Get node to watch
316 				rc = ::watch_node(&nref, B_WATCH_DIRECTORY, be_app_messenger);
317 			}
318 #endif
319 		}
320 	}
321 
322 	return rc;
323 }
324 
325 // ---------------------------------------------------------------
326 // void MessageReceived(BMessage* msg)
327 //
328 // Message handling method for print_server application class.
329 //
330 // Parameters:
331 //      msg - Actual message sent to application class.
332 //
333 // Returns:
334 //      void.
335 // ---------------------------------------------------------------
336 void
337 PrintServerApp::MessageReceived(BMessage* msg)
338 {
339 	switch(msg->what) {
340 		case PSRV_GET_ACTIVE_PRINTER:
341 		case PSRV_MAKE_PRINTER_ACTIVE_QUIETLY:
342 		case PSRV_MAKE_PRINTER_ACTIVE:
343 		case PSRV_MAKE_PRINTER:
344 		case PSRV_SHOW_PAGE_SETUP:
345 		case PSRV_SHOW_PRINT_SETUP:
346 		case PSRV_PRINT_SPOOLED_JOB:
347 		case PSRV_GET_DEFAULT_SETTINGS:
348 			Handle_BeOSR5_Message(msg);
349 			break;
350 
351 		case B_GET_PROPERTY:
352 		case B_SET_PROPERTY:
353 		case B_CREATE_PROPERTY:
354 		case B_DELETE_PROPERTY:
355 		case B_COUNT_PROPERTIES:
356 		case B_EXECUTE_PROPERTY:
357 			HandleScriptingCommand(msg);
358 			break;
359 
360 		default:
361 			Inherited::MessageReceived(msg);
362 	}
363 }
364 
365 // ---------------------------------------------------------------
366 // CreatePrinter(const char* printerName, const char* driverName,
367 //               const char* connection, const char* transportName,
368 //               const char* transportPath)
369 //
370 // Creates printer definition/spool directory. It sets the
371 // attributes of the directory to the values passed and calls
372 // the driver's add_printer method to handle any configuration
373 // needed.
374 //
375 // Parameters:
376 //    printerName - Name of printer to create.
377 //    driverName - Name of driver to use for this printer.
378 //    connection - "Local" or "Network".
379 //    transportName - Name of transport driver to use.
380 //    transportPath  - Configuration data for transport driver.
381 //
382 // Returns:
383 // ---------------------------------------------------------------
384 status_t
385 PrintServerApp::CreatePrinter(const char* printerName, const char* driverName,
386 	const char* connection, const char* transportName, const char* transportPath)
387 {
388 	status_t rc;
389 
390 		// Find directory containing printer definitions
391 	BPath path;
392 	if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY,&path,true,NULL)) == B_OK) {
393 
394 			// Create our printer definition/spool directory
395 		BDirectory printersDir(path.Path());
396 		BDirectory printer;
397 		if ((rc=printersDir.CreateDirectory(printerName,&printer)) == B_OK) {
398 				// Set its type to a printer
399 			BNodeInfo info(&printer);
400 			info.SetType(PSRV_PRINTER_FILETYPE);
401 
402 				// Store the settings in its attributes
403 			printer.WriteAttr(PSRV_PRINTER_ATTR_PRT_NAME, B_STRING_TYPE, 0, printerName, 	::strlen(printerName)+1);
404 			printer.WriteAttr(PSRV_PRINTER_ATTR_DRV_NAME, B_STRING_TYPE, 0, driverName,	::strlen(driverName)+1);
405 			printer.WriteAttr(PSRV_PRINTER_ATTR_TRANSPORT, B_STRING_TYPE, 0, transportName,::strlen(transportName)+1);
406 			printer.WriteAttr(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, B_STRING_TYPE, 0, transportPath,::strlen(transportPath)+1);
407 			printer.WriteAttr(PSRV_PRINTER_ATTR_CNX, B_STRING_TYPE, 0, connection,	::strlen(connection)+1);
408 
409 				// Find printer driver
410 			if (FindPrinterDriver(driverName, path) == B_OK) {
411 					// Try and load the addon
412 				image_id id = ::load_add_on(path.Path());
413 				if (id > 0) {
414 						// Addon was loaded, so try and get the add_printer symbol
415 					add_printer_func_t func;
416 					if (get_image_symbol(id, "add_printer", B_SYMBOL_TYPE_TEXT, (void**)&func) == B_OK) {
417 							// call the function and check its result
418 						if ((*func)(printerName) == NULL) {
419 							BEntry entry;
420 							if (printer.GetEntry(&entry) == B_OK) {
421 									// Delete the printer if function failed
422 								entry.Remove();
423 							}
424 						} else {
425 							printer.WriteAttr(PSRV_PRINTER_ATTR_STATE, B_STRING_TYPE, 0, "free", 		::strlen("free")+1);
426 						}
427 					}
428 
429 					::unload_add_on(id);
430 				}
431 			}
432 		}
433 	}
434 
435 	return rc;
436 }
437 
438 // ---------------------------------------------------------------
439 // SelectPrinter(const char* printerName)
440 //
441 // Makes a new printer the active printer. This is done simply
442 // by changing our class attribute fDefaultPrinter, and changing
443 // the icon of the BNode for the printer. Ofcourse, we need to
444 // change the icon of the "old" default printer first back to a
445 // "non-active" printer icon first.
446 //
447 // Parameters:
448 //		printerName - Name of the new active printer.
449 //
450 // Returns:
451 //      B_OK on success, or error code otherwise.
452 // ---------------------------------------------------------------
453 status_t
454 PrintServerApp::SelectPrinter(const char* printerName)
455 {
456 	status_t rc;
457 	BNode node;
458 
459 		// Find the node of the "old" default printer
460 	if (fDefaultPrinter != NULL &&
461 		FindPrinterNode(fDefaultPrinter->Name(), node) == B_OK) {
462 			// and remove the custom icon
463 		BNodeInfo info(&node);
464 		info.SetIcon(NULL, B_MINI_ICON);
465 		info.SetIcon(NULL, B_LARGE_ICON);
466 	}
467 
468 		// Find the node for the new default printer
469 	if ((rc=FindPrinterNode(printerName, node)) == B_OK) {
470 			// and add the custom icon
471 		BNodeInfo info(&node);
472 		info.SetIcon(fSelectedIconMini, B_MINI_ICON);
473 		info.SetIcon(fSelectedIconLarge, B_LARGE_ICON);
474 	}
475 
476 	fDefaultPrinter = Printer::Find(printerName);
477 	StoreDefaultPrinter(); // update our pref file
478 	be_roster->Broadcast(new BMessage(B_PRINTER_CHANGED));
479 
480 	return rc;
481 }
482 
483 // ---------------------------------------------------------------
484 // HandleSpooledJobs()
485 //
486 // Handles calling the printer drivers for printing a spooled job.
487 //
488 // ---------------------------------------------------------------
489 void
490 PrintServerApp::HandleSpooledJobs()
491 {
492 	const int n = Printer::CountPrinters();
493 	for (int i = 0; i < n; i ++) {
494 		Printer* printer = Printer::At(i);
495 		printer->HandleSpooledJob();
496 	}
497 }
498 
499 // static const char* kPrinterData = "printer_data";
500 
501 // ---------------------------------------------------------------
502 // RetrieveDefaultPrinter()
503 //
504 // Loads the currently selected printer from a private settings
505 // file.
506 //
507 // Parameters:
508 //     none.
509 //
510 // Returns:
511 //     Error code on failore, or B_OK if all went fine.
512 // ---------------------------------------------------------------
513 status_t
514 PrintServerApp::RetrieveDefaultPrinter()
515 {
516 	fDefaultPrinter = Printer::Find(fSettings->DefaultPrinter());
517 	return B_OK;
518 }
519 
520 // ---------------------------------------------------------------
521 // StoreDefaultPrinter()
522 //
523 // Stores the currently selected printer in a private settings
524 // file.
525 //
526 // Parameters:
527 //     none.
528 //
529 // Returns:
530 //     Error code on failore, or B_OK if all went fine.
531 // ---------------------------------------------------------------
532 status_t
533 PrintServerApp::StoreDefaultPrinter()
534 {
535 	if (fDefaultPrinter)
536 		fSettings->SetDefaultPrinter(fDefaultPrinter->Name());
537 	else
538 		fSettings->SetDefaultPrinter("");
539 	return B_OK;
540 }
541 
542 // ---------------------------------------------------------------
543 // FindPrinterNode(const char* name, BNode& node)
544 //
545 // Find the BNode representing the specified printer. It searches
546 // *only* in the users printer definitions.
547 //
548 // Parameters:
549 //     name - Name of the printer to look for.
550 //     node - BNode to set to the printer definition node.
551 //
552 // Returns:
553 //     B_OK if found, an error code otherwise.
554 // ---------------------------------------------------------------
555 status_t
556 PrintServerApp::FindPrinterNode(const char* name, BNode& node)
557 {
558 	status_t rc;
559 
560 		// Find directory containing printer definitions
561 	BPath path;
562 	if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY,&path,true,NULL)) == B_OK)
563 	{
564 		path.Append(name);
565 		rc = node.SetTo(path.Path());
566 	}
567 
568 	return rc;
569 }
570 
571 // ---------------------------------------------------------------
572 // FindPrinterDriver(const char* name, BPath& outPath)
573 //
574 // Finds the path to a specific printer driver. It searches all 3
575 // places add-ons can be stored: the user's private directory, the
576 // directory common to all users, and the system directory, in that
577 // order.
578 //
579 // Parameters:
580 //     name - Name of the printer driver to look for.
581 //     outPath - BPath to store the path to the driver in.
582 //
583 // Returns:
584 //      B_OK if the driver was found, otherwise an error code.
585 // ---------------------------------------------------------------
586 status_t
587 PrintServerApp::FindPrinterDriver(const char* name, BPath& outPath)
588 {
589 		// try to locate the driver
590 	if (::TestForAddonExistence(name, B_USER_ADDONS_DIRECTORY, "Print", outPath) != B_OK)
591 		if (::TestForAddonExistence(name, B_COMMON_ADDONS_DIRECTORY, "Print", outPath) != B_OK)
592 			return ::TestForAddonExistence(name, B_BEOS_ADDONS_DIRECTORY, "Print", outPath);
593 
594 	return B_OK;
595 }
596 
597 
598 bool PrintServerApp::OpenSettings(BFile& file, const char* name, bool forReading) {
599 	BPath path;
600 	uint32 openMode = forReading ? B_READ_ONLY : B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY;
601 	return find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK &&
602 		path.Append(name) == B_OK &&
603 		file.SetTo(path.Path(), openMode) == B_OK;
604 }
605 
606 static const char* kSettingsName = "print_server_settings";
607 
608 void PrintServerApp::LoadSettings() {
609 	BFile file;
610 	if (OpenSettings(file, kSettingsName, true)) {
611 		fSettings->Load(&file);
612 		fUseConfigWindow = fSettings->UseConfigWindow();
613 	}
614 }
615 
616 void PrintServerApp::SaveSettings() {
617 	BFile file;
618 	if (OpenSettings(file, kSettingsName, false)) {
619 		fSettings->SetUseConfigWindow(fUseConfigWindow);
620 		fSettings->Save(&file);
621 	}
622 }
623