haiku/src/servers/print/PrintServerApp.cpp

580 lines
13 KiB
C++

/*
* Copyright 2001-2015, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ithamar R. Adema
* Michael Pfeiffer
*/
#include "PrintServerApp.h"
#include <stdio.h>
#include <unistd.h>
#include <Alert.h>
#include <Autolock.h>
#include <Catalog.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <image.h>
#include <Locale.h>
#include <Mime.h>
#include <NodeInfo.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <Roster.h>
#include <PrintJob.h>
#include <String.h>
#include "BeUtils.h"
#include "Printer.h"
#include "pr_server.h"
#include "Transport.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "PrintServerApp"
typedef struct _printer_data {
char defaultPrinterName[256];
} printer_data_t;
static const char* kSettingsName = "print_server_settings";
BLocker *gLock = NULL;
/*! Main entry point of print_server.
@returns B_OK if application was started, or an errorcode if
the application failed to start.
*/
int
main()
{
gLock = new BLocker();
status_t status = B_OK;
PrintServerApp printServer(&status);
if (status == B_OK)
printServer.Run();
delete gLock;
return status;
}
/*! Constructor for print_server's application class. Retrieves the
name of the default printer from storage, caches the icons for
a selected printer.
@param err Pointer to status_t for storing result of application
initialisation.
@see BApplication
*/
PrintServerApp::PrintServerApp(status_t* err)
:
Inherited(PSRV_SIGNATURE_TYPE, true, err),
fDefaultPrinter(NULL),
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
fIconSize(0),
fSelectedIcon(NULL),
#else
fSelectedIconMini(NULL),
fSelectedIconLarge(NULL),
#endif
fReferences(0),
fHasReferences(0),
fUseConfigWindow(true),
fFolder(NULL)
{
fSettings = Settings::GetSettings();
LoadSettings();
if (*err != B_OK)
return;
fHasReferences = create_sem(1, "has_references");
// Build list of transport addons
Transport::Scan(B_USER_NONPACKAGED_ADDONS_DIRECTORY);
Transport::Scan(B_USER_ADDONS_DIRECTORY);
Transport::Scan(B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY);
Transport::Scan(B_SYSTEM_ADDONS_DIRECTORY);
SetupPrinterList();
RetrieveDefaultPrinter();
// Cache icons for selected printer
BMimeType type(PSRV_PRINTER_FILETYPE);
type.GetIcon(&fSelectedIcon, &fIconSize);
PostMessage(PSRV_PRINT_SPOOLED_JOB);
// Start handling of spooled files
}
PrintServerApp::~PrintServerApp()
{
SaveSettings();
delete fSettings;
}
bool
PrintServerApp::QuitRequested()
{
// don't quit when user types Command+Q!
BMessage* m = CurrentMessage();
bool shortcut;
if (m != NULL && m->FindBool("shortcut", &shortcut) == B_OK && shortcut)
return false;
if (!Inherited::QuitRequested())
return false;
// Stop watching the folder
delete fFolder; fFolder = NULL;
// Release all printers
Printer* printer;
while ((printer = Printer::At(0)) != NULL) {
printer->AbortPrintThread();
UnregisterPrinter(printer);
}
// Wait for printers
if (fHasReferences > 0) {
acquire_sem(fHasReferences);
delete_sem(fHasReferences);
fHasReferences = 0;
}
delete [] fSelectedIcon;
fSelectedIcon = NULL;
return true;
}
void
PrintServerApp::Acquire()
{
if (atomic_add(&fReferences, 1) == 0)
acquire_sem(fHasReferences);
}
void
PrintServerApp::Release()
{
if (atomic_add(&fReferences, -1) == 1)
release_sem(fHasReferences);
}
void
PrintServerApp::RegisterPrinter(BDirectory* printer)
{
BString transport, address, connection, state;
if (printer->ReadAttrString(PSRV_PRINTER_ATTR_TRANSPORT, &transport) == B_OK
&& printer->ReadAttrString(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, &address)
== B_OK
&& printer->ReadAttrString(PSRV_PRINTER_ATTR_CNX, &connection) == B_OK
&& printer->ReadAttrString(PSRV_PRINTER_ATTR_STATE, &state) == B_OK
&& state == "free") {
BAutolock lock(gLock);
if (lock.IsLocked()) {
// check if printer is already registered
node_ref node;
if (printer->GetNodeRef(&node) != B_OK)
return;
if (Printer::Find(&node) != NULL)
return;
// register new printer
Resource* resource = fResourceManager.Allocate(transport.String(),
address.String(), connection.String());
AddHandler(new Printer(printer, resource));
Acquire();
}
}
}
void
PrintServerApp::UnregisterPrinter(Printer* printer)
{
RemoveHandler(printer);
Printer::Remove(printer);
printer->Release();
}
void
PrintServerApp::NotifyPrinterDeletion(Printer* printer)
{
BAutolock lock(gLock);
if (lock.IsLocked()) {
fResourceManager.Free(printer->GetResource());
Release();
}
}
void
PrintServerApp::EntryCreated(node_ref* node, entry_ref* entry)
{
BEntry printer(entry);
if (printer.InitCheck() == B_OK && printer.IsDirectory()) {
BDirectory dir(&printer);
if (dir.InitCheck() == B_OK) RegisterPrinter(&dir);
}
}
void
PrintServerApp::EntryRemoved(node_ref* node)
{
Printer* printer = Printer::Find(node);
if (printer) {
if (printer == fDefaultPrinter)
fDefaultPrinter = NULL;
UnregisterPrinter(printer);
}
}
void
PrintServerApp::AttributeChanged(node_ref* node)
{
BDirectory printer(node);
if (printer.InitCheck() == B_OK)
RegisterPrinter(&printer);
}
/*! This method builds the internal list of printers from disk. It
also installs a node monitor to be sure that the list keeps
updated with the definitions on disk.
@return B_OK if successful, or an errorcode if failed.
*/
status_t
PrintServerApp::SetupPrinterList()
{
// Find directory containing printer definition nodes
BPath path;
status_t status = find_directory(B_USER_PRINTERS_DIRECTORY, &path);
if (status != B_OK)
return status;
// Directory has to exist in order to watch it
mode_t mode = 0777;
create_directory(path.Path(), mode);
BDirectory dir(path.Path());
status = dir.InitCheck();
if (status != B_OK)
return status;
// Register printer definition nodes
BEntry entry;
while(dir.GetNextEntry(&entry) == B_OK) {
if (!entry.IsDirectory())
continue;
BDirectory node(&entry);
BNodeInfo info(&node);
char buffer[256];
if (info.GetType(buffer) == B_OK
&& strcmp(buffer, PSRV_PRINTER_FILETYPE) == 0) {
RegisterPrinter(&node);
}
}
// Now we are ready to start node watching
fFolder = new FolderWatcher(this, dir, true);
fFolder->SetListener(this);
return B_OK;
}
/*! Message handling method for print_server application class.
@param msg Actual message sent to application class.
*/
void
PrintServerApp::MessageReceived(BMessage* msg)
{
switch(msg->what) {
case PSRV_GET_ACTIVE_PRINTER:
case PSRV_MAKE_PRINTER_ACTIVE_QUIETLY:
case PSRV_MAKE_PRINTER_ACTIVE:
case PSRV_MAKE_PRINTER:
case PSRV_SHOW_PAGE_SETUP:
case PSRV_SHOW_PRINT_SETUP:
case PSRV_PRINT_SPOOLED_JOB:
case PSRV_GET_DEFAULT_SETTINGS:
Handle_BeOSR5_Message(msg);
break;
case B_GET_PROPERTY:
case B_SET_PROPERTY:
case B_CREATE_PROPERTY:
case B_DELETE_PROPERTY:
case B_COUNT_PROPERTIES:
case B_EXECUTE_PROPERTY:
HandleScriptingCommand(msg);
break;
default:
Inherited::MessageReceived(msg);
}
}
/*! Creates printer definition/spool directory. It sets the
attributes of the directory to the values passed and calls
the driver's add_printer method to handle any configuration
needed.
@param printerName Name of printer to create.
@param driverName Name of driver to use for this printer.
@param connection "Local" or "Network".
@param transportName Name of transport driver to use.
@param transportPath Configuration data for transport driver.
*/
status_t
PrintServerApp::CreatePrinter(const char* printerName, const char* driverName,
const char* connection, const char* transportName,
const char* transportPath)
{
// Find directory containing printer definitions
BPath path;
status_t status = find_directory(B_USER_PRINTERS_DIRECTORY, &path, true,
NULL);
if (status != B_OK)
return status;
// Create our printer definition/spool directory
BDirectory printersDir(path.Path());
BDirectory printer;
status = printersDir.CreateDirectory(printerName, &printer);
if (status == B_FILE_EXISTS) {
printer.SetTo(&printersDir, printerName);
BString info;
char type[B_MIME_TYPE_LENGTH];
BNodeInfo(&printer).GetType(type);
if (strcmp(PSRV_PRINTER_FILETYPE, type) == 0) {
BPath path;
if (Printer::FindPathToDriver(printerName, &path) == B_OK) {
if (fDefaultPrinter) {
// the printer exists, but is not the default printer
if (strcmp(fDefaultPrinter->Name(), printerName) != 0)
status = B_OK;
return status;
}
// the printer exists, but no default at all
return B_OK;
} else {
info.SetTo(B_TRANSLATE(
"A printer with that name already exists, "
"but its driver could not be found! Replace?"));
}
} else {
info.SetTo(B_TRANSLATE(
"A printer with that name already exists, "
"but it's not usable at all! Replace?"));
}
if (info.Length() != 0) {
BAlert *alert = new BAlert("Info", info.String(),
B_TRANSLATE("Cancel"), B_TRANSLATE("OK"));
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 0)
return status;
}
} else if (status != B_OK) {
return status;
}
// Set its type to a printer
BNodeInfo info(&printer);
info.SetType(PSRV_PRINTER_FILETYPE);
// Store the settings in its attributes
printer.WriteAttr(PSRV_PRINTER_ATTR_PRT_NAME, B_STRING_TYPE, 0, printerName,
::strlen(printerName) + 1);
printer.WriteAttr(PSRV_PRINTER_ATTR_DRV_NAME, B_STRING_TYPE, 0, driverName,
::strlen(driverName) + 1);
printer.WriteAttr(PSRV_PRINTER_ATTR_TRANSPORT, B_STRING_TYPE, 0,
transportName, ::strlen(transportName) + 1);
printer.WriteAttr(PSRV_PRINTER_ATTR_TRANSPORT_ADDR, B_STRING_TYPE, 0,
transportPath, ::strlen(transportPath) + 1);
printer.WriteAttr(PSRV_PRINTER_ATTR_CNX, B_STRING_TYPE, 0, connection,
::strlen(connection) + 1);
status = Printer::ConfigurePrinter(driverName, printerName);
if (status == B_OK) {
// Notify printer driver that a new printer definition node
// has been created.
printer.WriteAttr(PSRV_PRINTER_ATTR_STATE, B_STRING_TYPE, 0, "free",
::strlen("free")+1);
}
if (status != B_OK) {
BEntry entry;
if (printer.GetEntry(&entry) == B_OK)
entry.Remove();
}
return status;
}
/*! Makes a new printer the active printer. This is done simply
by changing our class attribute fDefaultPrinter, and changing
the icon of the BNode for the printer. Ofcourse, we need to
change the icon of the "old" default printer first back to a
"non-active" printer icon first.
@param printerName Name of the new active printer.
@return B_OK on success, or error code otherwise.
*/
status_t
PrintServerApp::SelectPrinter(const char* printerName)
{
// Find the node of the "old" default printer
BNode node;
if (fDefaultPrinter != NULL
&& FindPrinterNode(fDefaultPrinter->Name(), node) == B_OK) {
// and remove the custom icon
BNodeInfo info(&node);
info.SetIcon(NULL, B_MINI_ICON);
info.SetIcon(NULL, B_LARGE_ICON);
}
// Find the node for the new default printer
status_t status = FindPrinterNode(printerName, node);
if (status == B_OK) {
// and add the custom icon
BNodeInfo info(&node);
info.SetIcon(fSelectedIcon, fIconSize);
}
fDefaultPrinter = Printer::Find(printerName);
StoreDefaultPrinter();
// update our pref file
be_roster->Broadcast(new BMessage(B_PRINTER_CHANGED));
return status;
}
//! Handles calling the printer drivers for printing a spooled job.
void
PrintServerApp::HandleSpooledJobs()
{
const int n = Printer::CountPrinters();
for (int i = 0; i < n; i ++) {
Printer* printer = Printer::At(i);
printer->HandleSpooledJob();
}
}
/*! Loads the currently selected printer from a private settings
file.
@return Error code on failore, or B_OK if all went fine.
*/
status_t
PrintServerApp::RetrieveDefaultPrinter()
{
fDefaultPrinter = Printer::Find(fSettings->DefaultPrinter());
return B_OK;
}
/*! Stores the currently selected printer in a private settings
file.
@return Error code on failore, or B_OK if all went fine.
*/
status_t
PrintServerApp::StoreDefaultPrinter()
{
if (fDefaultPrinter)
fSettings->SetDefaultPrinter(fDefaultPrinter->Name());
else
fSettings->SetDefaultPrinter("");
return B_OK;
}
/*! Find the BNode representing the specified printer. It searches
*only* in the users printer definitions.
@param name Name of the printer to look for.
@param node BNode to set to the printer definition node.
@return B_OK if found, an error code otherwise.
*/
status_t
PrintServerApp::FindPrinterNode(const char* name, BNode& node)
{
// Find directory containing printer definitions
BPath path;
status_t status = find_directory(B_USER_PRINTERS_DIRECTORY, &path, true,
NULL);
if (status != B_OK)
return status;
path.Append(name);
return node.SetTo(path.Path());
}
bool
PrintServerApp::OpenSettings(BFile& file, const char* name, bool forReading)
{
BPath path;
uint32 openMode = forReading ? B_READ_ONLY : B_CREATE_FILE | B_ERASE_FILE
| B_WRITE_ONLY;
return find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK
&& path.Append(name) == B_OK
&& file.SetTo(path.Path(), openMode) == B_OK;
}
void
PrintServerApp::LoadSettings()
{
BFile file;
if (OpenSettings(file, kSettingsName, true)) {
fSettings->Load(&file);
fUseConfigWindow = fSettings->UseConfigWindow();
}
}
void
PrintServerApp::SaveSettings()
{
BFile file;
if (OpenSettings(file, kSettingsName, false)) {
fSettings->SetUseConfigWindow(fUseConfigWindow);
fSettings->Save(&file);
}
}