* 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;
@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;
}
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");
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();
BMimeType type(PSRV_PRINTER_FILETYPE);
type.GetIcon(&fSelectedIcon, &fIconSize);
PostMessage(PSRV_PRINT_SPOOLED_JOB);
}
PrintServerApp::~PrintServerApp()
{
SaveSettings();
delete fSettings;
}
bool
PrintServerApp::QuitRequested()
{
BMessage* m = CurrentMessage();
bool shortcut;
if (m != NULL && m->FindBool("shortcut", &shortcut) == B_OK && shortcut)
return false;
if (!Inherited::QuitRequested())
return false;
delete fFolder; fFolder = NULL;
Printer* printer;
while ((printer = Printer::At(0)) != NULL) {
printer->AbortPrintThread();
UnregisterPrinter(printer);
}
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()) {
node_ref node;
if (printer->GetNodeRef(&node) != B_OK)
return;
if (Printer::Find(&node) != NULL)
return;
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);
}
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()
{
BPath path;
status_t status = find_directory(B_USER_PRINTERS_DIRECTORY, &path);
if (status != B_OK)
return status;
mode_t mode = 0777;
create_directory(path.Path(), mode);
BDirectory dir(path.Path());
status = dir.InitCheck();
if (status != B_OK)
return status;
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);
}
}
fFolder = new FolderWatcher(this, dir, true);
fFolder->SetListener(this);
return B_OK;
}
@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);
}
}
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)
{
BPath path;
status_t status = find_directory(B_USER_PRINTERS_DIRECTORY, &path, true,
NULL);
if (status != B_OK)
return status;
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) {
if (strcmp(fDefaultPrinter->Name(), printerName) != 0)
status = B_OK;
return status;
}
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;
}
BNodeInfo info(&printer);
info.SetType(PSRV_PRINTER_FILETYPE);
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) {
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;
}
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)
{
BNode node;
if (fDefaultPrinter != NULL
&& FindPrinterNode(fDefaultPrinter->Name(), node) == B_OK) {
BNodeInfo info(&node);
info.SetIcon(NULL, B_MINI_ICON);
info.SetIcon(NULL, B_LARGE_ICON);
}
status_t status = FindPrinterNode(printerName, node);
if (status == B_OK) {
BNodeInfo info(&node);
info.SetIcon(fSelectedIcon, fIconSize);
}
fDefaultPrinter = Printer::Find(printerName);
StoreDefaultPrinter();
be_roster->Broadcast(new BMessage(B_PRINTER_CHANGED));
return status;
}
void
PrintServerApp::HandleSpooledJobs()
{
const int n = Printer::CountPrinters();
for (int i = 0; i < n; i ++) {
Printer* printer = Printer::At(i);
printer->HandleSpooledJob();
}
}
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;
}
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;
}
*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)
{
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);
}
}