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