580 lines
13 KiB
C++
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);
|
|
}
|
|
}
|