Files
haiku-beta6/src/servers/print/PrintServerApp.cpp
T
DarkWyrm 314167cd80 Header updates
Tweaked a couple BAlerts to be a bit more standardized


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19593 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-12-21 15:18:09 +00:00

594 lines
16 KiB
C++

/*
* Copyright 2002-2006, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ithamar R. Adema
* Michael Pfeiffer
*/
#include "PrintServerApp.h"
#include "BeUtils.h"
#include "Printer.h"
#include "pr_server.h"
// BeOS API
#include <Alert.h>
#include <Autolock.h>
#include <Directory.h>
#include <File.h>
#include <image.h>
#include <FindDirectory.h>
#include <Mime.h>
#include <NodeInfo.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <Roster.h>
#include <PrintJob.h>
#include <String.h>
// ANSI C
#include <stdio.h> //for printf
#include <unistd.h> //for unlink
BLocker *gLock = NULL;
// ---------------------------------------------------------------
typedef struct _printer_data {
char defaultPrinterName[256];
} printer_data_t;
typedef BMessage* (*config_func_t)(BNode*, const BMessage*);
typedef BMessage* (*take_job_func_t)(BFile*, BNode*, const BMessage*);
typedef char* (*add_printer_func_t)(const char* printer_name);
/**
* Main entry point of print_server.
*
* @returns B_OK if application was started, or an errorcode if
* application failed to start.
*
* @see your favorite C/C++ textbook :P
*/
int
main()
{
status_t rc = B_OK;
gLock = new BLocker();
// Create our application object
PrintServerApp print_server(&rc);
// If all went fine, let's start it
if (rc == B_OK) {
print_server.Run();
}
delete gLock;
return rc;
}
/**
* 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, err),
fSelectedIconMini(NULL),
fSelectedIconLarge(NULL),
fReferences(0),
fHasReferences(0),
fUseConfigWindow(true),
fFolder(NULL)
{
fSettings = Settings::GetSettings();
LoadSettings();
// If our superclass initialized ok
if (*err == B_OK) {
fHasReferences = create_sem(1, "has_references");
// let us try as well
SetupPrinterList();
RetrieveDefaultPrinter();
fSelectedIconMini = new BBitmap(BRect(0,0,B_MINI_ICON-1,B_MINI_ICON-1), B_CMAP8);
fSelectedIconLarge = new BBitmap(BRect(0,0,B_LARGE_ICON-1,B_LARGE_ICON-1), B_CMAP8);
// Cache icons for selected printer
BMimeType type(PRNT_SIGNATURE_TYPE);
type.GetIcon(fSelectedIconMini, B_MINI_ICON);
type.GetIcon(fSelectedIconLarge, B_LARGE_ICON);
// Start handling of spooled files
PostMessage(PSRV_PRINT_SPOOLED_JOB);
}
}
PrintServerApp::~PrintServerApp() {
SaveSettings();
delete fSettings;
}
bool PrintServerApp::QuitRequested()
{
BMessage* m = CurrentMessage();
bool shortcut;
// don't quit when user types Command+Q!
if (m && m->FindBool("shortcut", &shortcut) == B_OK && shortcut) return false;
bool rc = Inherited::QuitRequested();
if (rc) {
delete fFolder; fFolder = NULL;
#if 0
// Find directory containing printer definition nodes
BPath path;
if (::find_directory(B_USER_PRINTERS_DIRECTORY, &path) == B_OK) {
BEntry entry(path.Path());
node_ref nref;
if (entry.InitCheck() == B_OK &&
entry.GetNodeRef(&nref) == B_OK) {
// Stop watching the printer directory
::watch_node(&nref, B_STOP_WATCHING, be_app_messenger);
}
}
#endif
// 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;
}
ASSERT(fSelectedIconMini != NULL);
delete fSelectedIconMini; fSelectedIconMini = NULL;
delete fSelectedIconLarge; fSelectedIconLarge = NULL;
}
return rc;
}
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;
Printer* p;
if (printer->GetNodeRef(&node) != B_OK) return;
p = Printer::Find(&node);
if (p != NULL) return;
// register new printer
Resource* r = fResourceManager.Allocate(transport.String(), address.String(), connection.String());
p = new Printer(printer, r);
AddHandler(p);
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);
}
}
// ---------------------------------------------------------------
// SetupPrinterList
//
// 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.
//
// Parameters:
// none.
//
// Returns:
// B_OK if successful, or an errorcode if failed.
// ---------------------------------------------------------------
status_t PrintServerApp::SetupPrinterList()
{
BEntry entry;
status_t rc;
BPath path;
// Find directory containing printer definition nodes
if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY, &path)) == B_OK) {
BDirectory dir(path.Path());
// Can we reach the directory?
if ((rc=dir.InitCheck()) == B_OK) {
// Yes, so loop over it's entries
while((rc=dir.GetNextEntry(&entry)) == B_OK) {
// If the entry is a directory
if (entry.IsDirectory()) {
// Check it's Mime type for a spool director
BDirectory node(&entry);
BNodeInfo info(&node);
char buffer[256];
if (info.GetType(buffer) == B_OK &&
strcmp(buffer, PSRV_PRINTER_FILETYPE) == 0) {
// Yes, it is a printer definition node
RegisterPrinter(&node);
}
}
}
fFolder = new FolderWatcher(this, dir, true);
fFolder->SetListener(this);
#if 0
// If we scanned all entries successfully
node_ref nref;
if (rc == B_ENTRY_NOT_FOUND &&
(rc=dir.GetNodeRef(&nref)) == B_OK) {
// Get node to watch
rc = ::watch_node(&nref, B_WATCH_DIRECTORY, be_app_messenger);
}
#endif
}
}
return rc;
}
// ---------------------------------------------------------------
// void MessageReceived(BMessage* msg)
//
// Message handling method for print_server application class.
//
// Parameters:
// msg - Actual message sent to application class.
//
// Returns:
// void.
// ---------------------------------------------------------------
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);
}
}
// ---------------------------------------------------------------
// CreatePrinter(const char* printerName, const char* driverName,
// const char* connection, const char* transportName,
// const char* transportPath)
//
// 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.
//
// Parameters:
// printerName - Name of printer to create.
// driverName - Name of driver to use for this printer.
// connection - "Local" or "Network".
// transportName - Name of transport driver to use.
// transportPath - Configuration data for transport driver.
//
// Returns:
// ---------------------------------------------------------------
status_t
PrintServerApp::CreatePrinter(const char* printerName, const char* driverName,
const char* connection, const char* transportName, const char* transportPath)
{
status_t rc;
// Find directory containing printer definitions
BPath path;
if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY,&path,true,NULL)) == B_OK) {
// Create our printer definition/spool directory
BDirectory printersDir(path.Path());
BDirectory printer;
if ((rc=printersDir.CreateDirectory(printerName,&printer)) == B_OK) {
// 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);
// Find printer driver
if (FindPrinterDriver(driverName, path) == B_OK) {
// Try and load the addon
image_id id = ::load_add_on(path.Path());
if (id > 0) {
// Addon was loaded, so try and get the add_printer symbol
add_printer_func_t func;
if (get_image_symbol(id, "add_printer", B_SYMBOL_TYPE_TEXT, (void**)&func) == B_OK) {
// call the function and check its result
if ((*func)(printerName) == NULL) {
BEntry entry;
if (printer.GetEntry(&entry) == B_OK) {
// Delete the printer if function failed
entry.Remove();
}
} else {
printer.WriteAttr(PSRV_PRINTER_ATTR_STATE, B_STRING_TYPE, 0, "free", ::strlen("free")+1);
}
}
::unload_add_on(id);
}
}
}
}
return rc;
}
// ---------------------------------------------------------------
// SelectPrinter(const char* printerName)
//
// 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.
//
// Parameters:
// printerName - Name of the new active printer.
//
// Returns:
// B_OK on success, or error code otherwise.
// ---------------------------------------------------------------
status_t
PrintServerApp::SelectPrinter(const char* printerName)
{
status_t rc;
BNode node;
// Find the node of the "old" default printer
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
if ((rc=FindPrinterNode(printerName, node)) == B_OK) {
// and add the custom icon
BNodeInfo info(&node);
info.SetIcon(fSelectedIconMini, B_MINI_ICON);
info.SetIcon(fSelectedIconLarge, B_LARGE_ICON);
}
fDefaultPrinter = Printer::Find(printerName);
StoreDefaultPrinter(); // update our pref file
be_roster->Broadcast(new BMessage(B_PRINTER_CHANGED));
return rc;
}
// ---------------------------------------------------------------
// HandleSpooledJobs()
//
// 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();
}
}
// static const char* kPrinterData = "printer_data";
// ---------------------------------------------------------------
// RetrieveDefaultPrinter()
//
// Loads the currently selected printer from a private settings
// file.
//
// Parameters:
// none.
//
// Returns:
// Error code on failore, or B_OK if all went fine.
// ---------------------------------------------------------------
status_t
PrintServerApp::RetrieveDefaultPrinter()
{
fDefaultPrinter = Printer::Find(fSettings->DefaultPrinter());
return B_OK;
}
// ---------------------------------------------------------------
// StoreDefaultPrinter()
//
// Stores the currently selected printer in a private settings
// file.
//
// Parameters:
// none.
//
// Returns:
// 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;
}
// ---------------------------------------------------------------
// FindPrinterNode(const char* name, BNode& node)
//
// Find the BNode representing the specified printer. It searches
// *only* in the users printer definitions.
//
// Parameters:
// name - Name of the printer to look for.
// node - BNode to set to the printer definition node.
//
// Returns:
// B_OK if found, an error code otherwise.
// ---------------------------------------------------------------
status_t
PrintServerApp::FindPrinterNode(const char* name, BNode& node)
{
status_t rc;
// Find directory containing printer definitions
BPath path;
if ((rc=::find_directory(B_USER_PRINTERS_DIRECTORY,&path,true,NULL)) == B_OK)
{
path.Append(name);
rc = node.SetTo(path.Path());
}
return rc;
}
// ---------------------------------------------------------------
// FindPrinterDriver(const char* name, BPath& outPath)
//
// Finds the path to a specific printer driver. It searches all 3
// places add-ons can be stored: the user's private directory, the
// directory common to all users, and the system directory, in that
// order.
//
// Parameters:
// name - Name of the printer driver to look for.
// outPath - BPath to store the path to the driver in.
//
// Returns:
// B_OK if the driver was found, otherwise an error code.
// ---------------------------------------------------------------
status_t
PrintServerApp::FindPrinterDriver(const char* name, BPath& outPath)
{
// try to locate the driver
if (::TestForAddonExistence(name, B_USER_ADDONS_DIRECTORY, "Print", outPath) != B_OK)
if (::TestForAddonExistence(name, B_COMMON_ADDONS_DIRECTORY, "Print", outPath) != B_OK)
return ::TestForAddonExistence(name, B_BEOS_ADDONS_DIRECTORY, "Print", outPath);
return B_OK;
}
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;
}
static const char* kSettingsName = "print_server_settings";
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);
}
}