1736 lines
47 KiB
C
1736 lines
47 KiB
C
/*
|
|
(c) Copyright 1996 Hewlett-Packard Company
|
|
(c) Copyright 1996 International Business Machines Corp.
|
|
(c) Copyright 1996 Sun Microsystems, Inc.
|
|
(c) Copyright 1996 Novell, Inc.
|
|
(c) Copyright 1996 Digital Equipment Corp.
|
|
(c) Copyright 1996 Fujitsu Limited
|
|
(c) Copyright 1996 Hitachi, Ltd.
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the names of the copyright holders shall
|
|
not be used in advertising or otherwise to promote the sale, use or other
|
|
dealings in this Software without prior written authorization from said
|
|
copyright holders.
|
|
*/
|
|
/*******************************************************************
|
|
**
|
|
** *********************************************************
|
|
** *
|
|
** * File: attributes.c
|
|
** *
|
|
** * Contents:
|
|
** * Implementation of the attribute store for Xp.
|
|
** *
|
|
** * Copyright: Copyright 1995 Hewlett-Packard Company
|
|
** *
|
|
** *********************************************************
|
|
**
|
|
********************************************************************/
|
|
|
|
#ifdef HAVE_DIX_CONFIG_H
|
|
#include <dix-config.h>
|
|
#endif
|
|
|
|
#include <X11/Xproto.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <sys/wait.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#if (defined(sun) && defined(SVR4)) || defined(__SCO__) || defined(__UNIXWARE__)
|
|
#include <wchar.h>
|
|
#endif
|
|
#include "scrnintstr.h"
|
|
|
|
#include <X11/extensions/Printstr.h>
|
|
|
|
#include "attributes.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xresource.h>
|
|
|
|
#include "spooler.h"
|
|
|
|
#ifndef MIN
|
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
|
#endif
|
|
#ifndef MAX
|
|
#define MAX(a,b) (((a)>(b))?(a):(b))
|
|
#endif
|
|
|
|
|
|
static XrmDatabase CopyDb(XrmDatabase inDb);
|
|
|
|
extern XrmDatabase XpSpoolerGetServerAttributes(void);
|
|
|
|
static int attrGeneration = 0;
|
|
|
|
typedef struct {
|
|
XrmDatabase *pDb;
|
|
char *qualifier;
|
|
char *modelId;
|
|
} DbEnumStruct;
|
|
|
|
typedef struct {
|
|
char *stringDb;
|
|
int nextPos;
|
|
int space;
|
|
} StringDbStruct;
|
|
|
|
typedef struct _printerAttrs {
|
|
struct _printerAttrs *next;
|
|
char *name;
|
|
char *qualifier;
|
|
XrmDatabase printerAttrs;
|
|
XrmDatabase docAttrs;
|
|
XrmDatabase jobAttrs;
|
|
} PrAttrs, *PrAttrPtr;
|
|
|
|
static PrAttrPtr attrList = (PrAttrPtr)NULL;
|
|
|
|
typedef struct _systemAttrs {
|
|
XrmDatabase doc;
|
|
XrmDatabase job;
|
|
XrmDatabase printers;
|
|
XrmDatabase server;
|
|
} SysAttrs, *SysAttrsPtr;
|
|
|
|
SysAttrs systemAttributes;
|
|
|
|
/*
|
|
* attrCtxtPrivIndex hold the attribute store's context private index.
|
|
* This index is allocated at the time the attribute store is initialized.
|
|
*/
|
|
static int attrCtxtPrivIndex;
|
|
|
|
/*
|
|
* The ContextAttrs structure descibes the context private space reserved
|
|
* by the attribute store.
|
|
*/
|
|
typedef struct _contextAttrs {
|
|
XrmDatabase printerAttrs;
|
|
XrmDatabase docAttrs;
|
|
XrmDatabase jobAttrs;
|
|
XrmDatabase pageAttrs;
|
|
} ContextAttrs, *ContextAttrPtr;
|
|
|
|
/*
|
|
* XPDIR is relative to (i.e. is a subdir of) XPRINTDIR/$LANG.
|
|
*/
|
|
static const char XPDIR[] = "/print";
|
|
/*
|
|
* The following files/directories define or are within subdirectories of the
|
|
* above-defined XPDIR.
|
|
*/
|
|
static const char XPPRINTERATTRFILE[] = "/attributes/printer";
|
|
static const char XPJOBATTRFILE[] = "/attributes/job";
|
|
static const char XPDOCATTRFILE[] = "/attributes/document";
|
|
static const char XPMODELDIR[] = "/models";
|
|
|
|
static char NULL_STRING[] = "\0";
|
|
|
|
/*
|
|
* XpGetConfigDirBase returns a string containing the path name of the base
|
|
* where the print server configuration directory is localed.
|
|
*/
|
|
static
|
|
char *XpGetConfigDirBase(void)
|
|
{
|
|
char *configDir;
|
|
|
|
/*
|
|
* If the XPCONFIGDIR environment variable is not set, then use the
|
|
* compile-time constant XPRINTDIR. XPRINTDIR is passed in on the
|
|
* compile command line, and is defined in $(TOP)/config/cf/Project.tmpl.
|
|
*/
|
|
if((configDir = getenv("XPCONFIGDIR")) == (char *)NULL)
|
|
configDir = XPRINTDIR;
|
|
|
|
return configDir;
|
|
}
|
|
|
|
/*
|
|
* XpGetConfigDir returns a string containing the path name of the print
|
|
* server configuration directory. If the useLocale parameter is False
|
|
* the it returns the path to the "/C" directory. If the useLocale
|
|
* parameter is True it returns the path to the directory associated with
|
|
* $LANG. It makes no attempt to ensure that the directory actually exists.
|
|
*/
|
|
char *
|
|
XpGetConfigDir(Bool useLocale)
|
|
{
|
|
char *dirName, *langName, *langDir, *configDir;
|
|
Bool freeLangDir = False;
|
|
|
|
if(useLocale == False) langDir = "/C";
|
|
else
|
|
{
|
|
langName = getenv("LC_ALL");
|
|
if (langName == NULL) {
|
|
langName = getenv("LANG");
|
|
}
|
|
|
|
if(langName == (char *)NULL)
|
|
return (char *)NULL;
|
|
else
|
|
{
|
|
if(strcmp(langName, "C") == 0)
|
|
return (char *)NULL;
|
|
langDir = (char *)xalloc(strlen(langName) + 2);
|
|
sprintf(langDir, "/%s", langName);
|
|
freeLangDir = True;
|
|
}
|
|
}
|
|
|
|
configDir = XpGetConfigDirBase();
|
|
|
|
dirName = (char *)xalloc(strlen(configDir) + strlen(XPDIR) +
|
|
strlen(langDir) + 1);
|
|
sprintf(dirName, "%s%s%s", configDir, langDir, XPDIR);
|
|
|
|
if(freeLangDir == True)
|
|
xfree(langDir);
|
|
|
|
return dirName;
|
|
}
|
|
|
|
/*
|
|
* GetMergedDatabase reads and merges xrmdb files from the top-level printer
|
|
* config directory, and from the directory associated with the current
|
|
* locale (if other than the top-level).
|
|
*/
|
|
static XrmDatabase
|
|
GetMergedDatabase(const char *attrName)
|
|
{
|
|
char *dirName, *fileName;
|
|
XrmDatabase db;
|
|
|
|
if((dirName = XpGetConfigDir(False)) == (char *)NULL)
|
|
return (XrmDatabase)NULL;
|
|
if((fileName = (char *)xalloc(strlen(dirName) + strlen(attrName) + 1)) ==
|
|
(char *)NULL)
|
|
return (XrmDatabase)NULL;
|
|
sprintf(fileName, "%s%s", dirName, attrName);
|
|
db = XrmGetFileDatabase(fileName);
|
|
xfree(fileName);
|
|
xfree(dirName);
|
|
|
|
if((dirName = XpGetConfigDir(True)) == (char *)NULL)
|
|
return db;
|
|
if((fileName = (char *)xalloc(strlen(dirName) + strlen(attrName) + 1)) ==
|
|
(char *)NULL)
|
|
return db;
|
|
sprintf(fileName, "%s%s", dirName, attrName);
|
|
(void)XrmCombineFileDatabase(fileName, &db, True);
|
|
xfree(fileName);
|
|
xfree(dirName);
|
|
|
|
return db;
|
|
}
|
|
|
|
/*
|
|
* BuildSystemAttributes reads the on-disk configuration files for printers,
|
|
* initial job, and initial document attributes. The resulting xrm
|
|
* databases are then dissected as needed for each printer.
|
|
* It also allocates a contextPrivate space for the attributes,
|
|
* reserving space to store pointers to the attribute stores for
|
|
* the context.
|
|
*/
|
|
static void
|
|
BuildSystemAttributes(void)
|
|
{
|
|
if(systemAttributes.printers != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(systemAttributes.printers);
|
|
systemAttributes.printers = GetMergedDatabase(XPPRINTERATTRFILE);
|
|
if(systemAttributes.job != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(systemAttributes.job);
|
|
systemAttributes.job = GetMergedDatabase(XPJOBATTRFILE);
|
|
if(systemAttributes.doc != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(systemAttributes.doc);
|
|
systemAttributes.doc = GetMergedDatabase(XPDOCATTRFILE);
|
|
if(systemAttributes.server != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(systemAttributes.server);
|
|
systemAttributes.server = XpSpoolerGetServerAttributes();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* AddDbEntry is called by XrmEnumerateDatabase, and adds the supplied
|
|
* database entry to the database pointed to within the "DbEnumStruct"
|
|
* passed as the client_data (aka "closure").
|
|
*/
|
|
static Bool
|
|
AddDbEntry(
|
|
XrmDatabase *sourceDB,
|
|
XrmBindingList bindings,
|
|
XrmQuarkList quarks,
|
|
XrmRepresentation *type,
|
|
XrmValue *value,
|
|
XPointer client_data)
|
|
{
|
|
DbEnumStruct *pEnumStruct = (DbEnumStruct *)client_data;
|
|
XrmName xrm_name[5];
|
|
XrmClass xrm_class[5];
|
|
XrmBinding xrm_bind[3];
|
|
XrmValue realVal;
|
|
XrmRepresentation rep_type;
|
|
|
|
xrm_name[0] = XrmStringToQuark (pEnumStruct->qualifier);
|
|
xrm_class[0] = XrmStringToQuark (pEnumStruct->modelId);
|
|
|
|
for(;*quarks; quarks++)
|
|
xrm_name[1] = xrm_class[1] = *quarks;
|
|
|
|
xrm_name[2] = (XrmQuark)NULL;
|
|
xrm_class[2] = (XrmQuark)NULL;
|
|
|
|
if(XrmQGetResource (*sourceDB, xrm_name, xrm_class, &rep_type, &realVal))
|
|
{
|
|
xrm_bind[0] = XrmBindLoosely;
|
|
|
|
xrm_name[0] = xrm_name[1];
|
|
xrm_name[1] = NULLQUARK;
|
|
|
|
XrmQPutStringResource(pEnumStruct->pDb, xrm_bind, xrm_name,
|
|
(char *)realVal.addr);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* BuildPrinterAttrs - builds and returns an XrmDatabase for the printer
|
|
* of the specified name/qualifier, if we have enough information.
|
|
* If we don't have a model-config
|
|
* file, then just enumerate the systemAttributes->printers database,
|
|
* otherwise read in the model-config database and merge into it the
|
|
* systemAttributes->printers database. This database is then enumerated
|
|
* with the printer qualifier (and the model name as class if we have it), and
|
|
* the resulting elements are stored into the database for this particular
|
|
* printer.
|
|
*/
|
|
static XrmDatabase
|
|
BuildPrinterAttrs(
|
|
char *printerName,
|
|
char *qualifierName)
|
|
{
|
|
XrmDatabase printerDB = (XrmDatabase)NULL;
|
|
|
|
if(systemAttributes.printers != (XrmDatabase)NULL)
|
|
{
|
|
char *fileName;
|
|
XrmDatabase modelDB = (XrmDatabase)NULL;
|
|
XrmName xrm_name[5], xrm_class[2];
|
|
XrmRepresentation rep_type;
|
|
XrmValue value;
|
|
DbEnumStruct enumStruct;
|
|
Bool freeModelDB = False;
|
|
/*
|
|
* Build the initial db based on the model-config files
|
|
*/
|
|
xrm_name[0] = XrmStringToQuark (qualifierName);
|
|
xrm_name[1] = XrmStringToQuark ("xp-model-identifier");
|
|
xrm_name[2] = (XrmQuark)NULL;
|
|
XrmQGetResource (systemAttributes.printers, xrm_name, xrm_name,
|
|
&rep_type, &value);
|
|
|
|
if(value.addr != (XPointer)NULL)
|
|
{
|
|
fileName = (char *)xalloc(strlen(XPMODELDIR) +
|
|
strlen((char *)value.addr) +
|
|
strlen("model-config") + 3);
|
|
sprintf(fileName, "%s/%s/%s", XPMODELDIR, value.addr,
|
|
"model-config");
|
|
modelDB = GetMergedDatabase(fileName);
|
|
xfree(fileName);
|
|
if(modelDB != (XrmDatabase)NULL)
|
|
{
|
|
XrmDatabase tempDB = (XrmDatabase)NULL;
|
|
/*
|
|
* have to make a temp copy because MergeDatabase destroys
|
|
* the "source" database. Merge in the printers DB
|
|
*/
|
|
tempDB = CopyDb(systemAttributes.printers);
|
|
XrmMergeDatabases(tempDB, &modelDB);
|
|
freeModelDB = True;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check to see if we knew the name AND found a database file
|
|
*/
|
|
if(modelDB == (XrmDatabase)NULL)
|
|
modelDB = systemAttributes.printers;
|
|
|
|
xrm_name[0] = XrmStringToQuark (qualifierName);
|
|
xrm_name[1] = (XrmQuark)NULL;
|
|
xrm_class[0] = XrmStringToQuark((char *)value.addr);
|
|
xrm_class[1] = (XrmQuark)NULL;
|
|
enumStruct.pDb = &printerDB;
|
|
enumStruct.qualifier = (char *)qualifierName;
|
|
enumStruct.modelId = (char *)value.addr;
|
|
XrmEnumerateDatabase(modelDB, xrm_name, xrm_class, XrmEnumAllLevels,
|
|
AddDbEntry, (XPointer) &enumStruct);
|
|
|
|
if(freeModelDB == True) XrmDestroyDatabase(modelDB);
|
|
}
|
|
XrmPutStringResource(&printerDB, "*printer-name", printerName);
|
|
XrmPutStringResource(&printerDB, "*qualifier", qualifierName);
|
|
return printerDB;
|
|
}
|
|
|
|
/*
|
|
* BuildABase - builds an XrmDatabase by enumerating the supplied sourceBase
|
|
* database for elements relevant for the printer named by printerName,
|
|
* and deriving a class for printerName from the model declared in the
|
|
* systemAttributes.printers database. If no model is defined for this
|
|
* printer then the printerName is used as the class as well.
|
|
*
|
|
* This is used to build the initial value document and initial value
|
|
* job attribute databases for each printer by searching the system
|
|
* level doc and job databases.
|
|
*/
|
|
static XrmDatabase
|
|
BuildABase(
|
|
char *printerName,
|
|
char *qualifierName,
|
|
XrmDatabase sourceBase)
|
|
{
|
|
XrmDatabase builtDB = (XrmDatabase)NULL;
|
|
|
|
if(sourceBase != (XrmDatabase)NULL)
|
|
{
|
|
XrmName xrm_name[5], xrm_class[2];
|
|
XrmRepresentation rep_type;
|
|
XrmValue value;
|
|
DbEnumStruct enumStruct;
|
|
|
|
/*
|
|
* Retrieve the model name for use as the class.
|
|
*/
|
|
xrm_name[0] = XrmStringToQuark (printerName);
|
|
xrm_name[1] = XrmStringToQuark ("xp-model-identifier");
|
|
xrm_name[2] = (XrmQuark)NULL;
|
|
XrmQGetResource (systemAttributes.printers, xrm_name, xrm_name,
|
|
&rep_type, &value);
|
|
/*
|
|
* if we have a model name then use it as the class, otherwise
|
|
* just use the printer name as the class as well as the name.
|
|
*/
|
|
if(value.addr != (XPointer)NULL)
|
|
xrm_class[0] = XrmStringToQuark((char *)value.addr);
|
|
else
|
|
xrm_class[0] = xrm_name[0];
|
|
xrm_class[1] = (XrmQuark)NULL;
|
|
|
|
xrm_name[1] = (XrmQuark)NULL;
|
|
|
|
enumStruct.pDb = &builtDB;
|
|
enumStruct.qualifier = (char *)qualifierName;
|
|
enumStruct.modelId = (char *)value.addr;
|
|
XrmEnumerateDatabase(sourceBase, xrm_name, xrm_class, XrmEnumAllLevels,
|
|
AddDbEntry, (XPointer) &enumStruct);
|
|
}
|
|
|
|
XrmPutStringResource(&builtDB, "*qualifier", qualifierName);
|
|
|
|
return builtDB;
|
|
}
|
|
|
|
/*
|
|
* FreeAttrList is called upon server recycle, and frees the printer
|
|
* databases stored in the global attrList.
|
|
*/
|
|
static void
|
|
FreeAttrList(void)
|
|
{
|
|
PrAttrPtr pAttr, pNext;
|
|
|
|
for(pAttr = attrList, pNext = attrList;
|
|
pAttr != (PrAttrPtr)NULL;
|
|
pAttr = pNext)
|
|
{
|
|
pNext = pAttr->next;
|
|
if(pAttr->printerAttrs != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(pAttr->printerAttrs);
|
|
if(pAttr->docAttrs != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(pAttr->docAttrs);
|
|
if(pAttr->jobAttrs != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(pAttr->jobAttrs);
|
|
xfree(pAttr->name);
|
|
xfree(pAttr->qualifier);
|
|
xfree(pAttr);
|
|
}
|
|
attrList = (PrAttrPtr)NULL;
|
|
}
|
|
|
|
/*
|
|
* XpRehashAttributes - frees the per-printer attribute list and
|
|
* calls BuildSystemAttributes to rebuild the overall attribute
|
|
* store. It is expected that a caller of this will follow it
|
|
* by calling XpBuildAttributeStore for a new list of printers.
|
|
*/
|
|
int
|
|
XpRehashAttributes(void)
|
|
{
|
|
if(attrList != (PrAttrPtr)NULL)
|
|
FreeAttrList();
|
|
BuildSystemAttributes();
|
|
return Success;
|
|
}
|
|
|
|
/*
|
|
* XpBuildAttributeStore - creates the attribute database associated
|
|
* with the specified printer. The first time this is called it
|
|
* calls BuildSystemAttributes to create the system-level databases.
|
|
*/
|
|
void
|
|
XpBuildAttributeStore(
|
|
char *printerName,
|
|
char *qualifierName)
|
|
{
|
|
PrAttrPtr pAttr;
|
|
|
|
if((pAttr = (PrAttrPtr)xalloc(sizeof(PrAttrs))) == (PrAttrPtr)NULL)
|
|
return;
|
|
|
|
if(attrGeneration != serverGeneration)
|
|
{
|
|
if(attrList != (PrAttrPtr)NULL)
|
|
FreeAttrList();
|
|
attrCtxtPrivIndex = XpAllocateContextPrivateIndex();
|
|
XpAllocateContextPrivate(attrCtxtPrivIndex, sizeof(ContextAttrs));
|
|
BuildSystemAttributes();
|
|
|
|
attrGeneration = serverGeneration;
|
|
}
|
|
|
|
if(attrList == (PrAttrPtr)NULL)
|
|
{
|
|
pAttr->next = (PrAttrPtr)NULL;
|
|
attrList = pAttr;
|
|
}
|
|
else
|
|
{
|
|
pAttr->next = attrList;
|
|
attrList = pAttr;
|
|
}
|
|
|
|
pAttr->name = strdup(printerName);
|
|
pAttr->qualifier = strdup(qualifierName);
|
|
pAttr->printerAttrs = BuildPrinterAttrs(printerName, qualifierName);
|
|
pAttr->docAttrs = BuildABase(printerName, qualifierName,
|
|
systemAttributes.doc);
|
|
pAttr->jobAttrs = BuildABase(printerName, qualifierName,
|
|
systemAttributes.job);
|
|
}
|
|
|
|
|
|
static Bool
|
|
StoreEntry(
|
|
XrmDatabase *sourceDB,
|
|
XrmBindingList bindings,
|
|
XrmQuarkList quarks,
|
|
XrmRepresentation *type,
|
|
XrmValue *value,
|
|
XPointer client_data)
|
|
{
|
|
XrmDatabase *outDb = (XrmDatabase *)client_data;
|
|
|
|
XrmQPutStringResource(outDb, bindings, quarks, (char *)value->addr);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* XpCopyDb - makes a copy of the specified XrmDatabase and returns
|
|
* the copy.
|
|
*/
|
|
static XrmDatabase
|
|
CopyDb(XrmDatabase inDb)
|
|
{
|
|
XrmDatabase outDb = (XrmDatabase)NULL;
|
|
XrmQuark empty = NULLQUARK;
|
|
|
|
(void)XrmEnumerateDatabase(inDb, &empty, &empty, XrmEnumAllLevels,
|
|
StoreEntry, (XPointer) &outDb);
|
|
return outDb;
|
|
}
|
|
|
|
/*
|
|
* XpInitAttributes - initializes the attribute store for the specified
|
|
* context. It does this by making copies of the printer, doc, and job
|
|
* attributes databases for the printer associated with the context.
|
|
*/
|
|
void
|
|
XpInitAttributes(XpContextPtr pContext)
|
|
{
|
|
ContextAttrPtr pCtxtAttrs;
|
|
PrAttrPtr pPrAttr = attrList;
|
|
|
|
/* Initialize all the pointers to NULL */
|
|
pCtxtAttrs = (ContextAttrPtr)pContext->devPrivates[attrCtxtPrivIndex].ptr;
|
|
(void)memset((void *)pCtxtAttrs, 0, (size_t) sizeof(ContextAttrs));
|
|
|
|
for(pPrAttr = attrList; pPrAttr != (PrAttrPtr)NULL; pPrAttr = pPrAttr->next)
|
|
if(!strcmp(pPrAttr->name, pContext->printerName)) break;
|
|
|
|
if(pPrAttr != (PrAttrPtr)NULL)
|
|
{
|
|
pCtxtAttrs->printerAttrs = CopyDb(pPrAttr->printerAttrs);
|
|
pCtxtAttrs->docAttrs = CopyDb(pPrAttr->docAttrs);
|
|
pCtxtAttrs->jobAttrs = CopyDb(pPrAttr->jobAttrs);
|
|
}
|
|
}
|
|
|
|
void
|
|
XpDestroyAttributes(
|
|
XpContextPtr pContext)
|
|
{
|
|
ContextAttrPtr pCtxtAttrs;
|
|
|
|
pCtxtAttrs = (ContextAttrPtr)pContext->devPrivates[attrCtxtPrivIndex].ptr;
|
|
|
|
if(pCtxtAttrs->printerAttrs != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(pCtxtAttrs->printerAttrs);
|
|
if(pCtxtAttrs->docAttrs != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(pCtxtAttrs->docAttrs);
|
|
if(pCtxtAttrs->jobAttrs != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(pCtxtAttrs->jobAttrs);
|
|
if(pCtxtAttrs->pageAttrs != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(pCtxtAttrs->pageAttrs);
|
|
}
|
|
|
|
/*
|
|
* XpGetOneAttribute returns the string value of the specified attribute
|
|
* in the specified class for the specified print context. If the attribute
|
|
* doesn't exist in the database for this context, or if the class database
|
|
* doesn't exist for this context, then NULL is returned. The caller must
|
|
* not free the returned string, as the returned pointer points into the
|
|
* database. This function can also return a value from the server attributes,
|
|
* in which case the pContext parameter is ignored.
|
|
*/
|
|
char *
|
|
XpGetOneAttribute(
|
|
XpContextPtr pContext,
|
|
XPAttributes class,
|
|
char *attributeName)
|
|
{
|
|
ContextAttrPtr pCtxtAttrs;
|
|
XrmDatabase db = (XrmDatabase)NULL;
|
|
XrmName xrm_name[3];
|
|
XrmRepresentation rep_type;
|
|
XrmValue value;
|
|
|
|
if(class == XPServerAttr)
|
|
{
|
|
if(systemAttributes.server == (XrmDatabase)NULL)
|
|
return NULL_STRING;
|
|
|
|
xrm_name[0] = XrmStringToQuark (attributeName);
|
|
xrm_name[1] = (XrmQuark)NULL;
|
|
XrmQGetResource(systemAttributes.server, xrm_name, xrm_name,
|
|
&rep_type, &value);
|
|
|
|
if(value.addr == (char *)NULL)
|
|
return NULL_STRING;
|
|
return (char *)value.addr;
|
|
}
|
|
else
|
|
{
|
|
pCtxtAttrs=(ContextAttrPtr)pContext->devPrivates[attrCtxtPrivIndex].ptr;
|
|
switch(class)
|
|
{
|
|
case XPPrinterAttr:
|
|
db = pCtxtAttrs->printerAttrs;
|
|
break;
|
|
case XPDocAttr:
|
|
db = pCtxtAttrs->docAttrs;
|
|
break;
|
|
case XPJobAttr:
|
|
db = pCtxtAttrs->jobAttrs;
|
|
break;
|
|
case XPPageAttr:
|
|
db = pCtxtAttrs->pageAttrs;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if(db == (XrmDatabase)NULL)
|
|
return NULL_STRING;
|
|
|
|
xrm_name[0] = XrmStringToQuark ("qualifier");
|
|
xrm_name[1] = (XrmQuark)NULL;
|
|
XrmQGetResource(db, xrm_name, xrm_name, &rep_type, &value);
|
|
|
|
xrm_name[0] = XrmStringToQuark (value.addr);
|
|
xrm_name[1] = XrmStringToQuark (attributeName);
|
|
xrm_name[2] = (XrmQuark)NULL;
|
|
if(XrmQGetResource(db, xrm_name, xrm_name, &rep_type, &value))
|
|
return (char *)value.addr;
|
|
else
|
|
return NULL_STRING;
|
|
}
|
|
|
|
/*
|
|
* XpPutOneAttribute updates one attribute for the specified
|
|
* context and class. This function is intended for use by the attribute
|
|
* validation module which updates the XrmDatabases directly. This
|
|
* function does not recognize XPServerAttr.
|
|
*/
|
|
void
|
|
XpPutOneAttribute(
|
|
XpContextPtr pContext,
|
|
XPAttributes class,
|
|
const char* attributeName,
|
|
const char* value)
|
|
{
|
|
ContextAttrPtr pCtxtAttrs;
|
|
XrmDatabase db;
|
|
XrmBinding bindings[1];
|
|
XrmQuark quarks[2];
|
|
|
|
pCtxtAttrs = (ContextAttrPtr)pContext->devPrivates[attrCtxtPrivIndex].ptr;
|
|
switch(class)
|
|
{
|
|
case XPPrinterAttr:
|
|
db = pCtxtAttrs->printerAttrs;
|
|
break;
|
|
case XPDocAttr:
|
|
db = pCtxtAttrs->docAttrs;
|
|
break;
|
|
case XPJobAttr:
|
|
db = pCtxtAttrs->jobAttrs;
|
|
break;
|
|
case XPPageAttr:
|
|
db = pCtxtAttrs->pageAttrs;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
bindings[0] = XrmBindLoosely;
|
|
quarks[0] = XrmStringToQuark(attributeName);
|
|
quarks[1] = (XrmQuark)NULL;
|
|
XrmQPutStringResource(&db, bindings, quarks, value ? value : "");
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* The following routines: ExpandSpace, PutString, PutByte, and AppendEntry
|
|
* form the functional core of the GetAttributes routine. Xrm does not
|
|
* supply a routine to form a string database from an XrmDatabase, except
|
|
* by writing the database to a file. This code avoids the file system
|
|
* overhead, but is a bit clunky in its memory management.
|
|
*
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* ExpandSpace expands the memory allocated for the string database in
|
|
* the StringDbStruct passed in, and updates the "space" field of the
|
|
* struct to indicate the new amount of space available.
|
|
*/
|
|
static Bool
|
|
ExpandSpace(
|
|
StringDbStruct *pStr)
|
|
{
|
|
char *newSpace;
|
|
|
|
if((newSpace = (char *)xrealloc(pStr->stringDb, pStr->nextPos + pStr->space
|
|
+ 1024)) == (char *)NULL)
|
|
return False;
|
|
pStr->space += 1024;
|
|
pStr->stringDb = newSpace;
|
|
return True;
|
|
}
|
|
|
|
/*
|
|
* PutString puts the contents of a null-terminated string into the string
|
|
* database in the StringDbStruct passed in. If there is insufficient room
|
|
* for the string, ExpandSpace is called, and the nextPos and space fields
|
|
* are updated.
|
|
*/
|
|
static void
|
|
PutString(
|
|
StringDbStruct *pStr,
|
|
char *pString)
|
|
{
|
|
int len = strlen(pString);
|
|
|
|
if(len >= pStr->space)
|
|
if(!ExpandSpace(pStr))
|
|
return;
|
|
strcpy(&pStr->stringDb[pStr->nextPos], pString);
|
|
pStr->nextPos += len;
|
|
pStr->space -= len;
|
|
}
|
|
|
|
/*
|
|
* PutByte puts a single byte value in to the string database in the passed-in
|
|
* StringDbStruct. ExpandSpace is called if there is insufficient room for
|
|
* the byte, and the nextPos and space fields are updated.
|
|
*/
|
|
static void
|
|
PutByte(
|
|
StringDbStruct *pStr,
|
|
char byte)
|
|
{
|
|
if(pStr->space <= 1)
|
|
if(!ExpandSpace(pStr))
|
|
return;
|
|
pStr->stringDb[pStr->nextPos] = byte;
|
|
pStr->nextPos++;
|
|
pStr->space--;
|
|
}
|
|
|
|
#define XrmQString XrmPermStringToQuark("String")
|
|
|
|
/*
|
|
* AppendEntry is called by XrmEnumerateDatabase, and serves to append
|
|
* a database entry onto a string database. The passed-in "closure"
|
|
* struct contains a pointer to the string, and a count of the remaining
|
|
* bytes. If there are insufficient remaining bytes then the struct
|
|
* is realloced, and the count of the space remaining is updated.
|
|
* Database elements of types other than String are ignored!
|
|
* This code is based directly on that in "DumpEntry" in Xrm.c.
|
|
*/
|
|
static Bool
|
|
AppendEntry(
|
|
XrmDatabase *db,
|
|
XrmBindingList bindings,
|
|
XrmQuarkList quarks,
|
|
XrmRepresentation *type,
|
|
XrmValuePtr value,
|
|
XPointer data)
|
|
{
|
|
StringDbStruct *pEnumStr = (StringDbStruct *)data;
|
|
Bool firstNameSeen;
|
|
unsigned int i;
|
|
char *s, c;
|
|
|
|
if (*type != XrmQString)
|
|
return False;
|
|
|
|
for (firstNameSeen = False; *quarks; bindings++, quarks++) {
|
|
if (*bindings == XrmBindLoosely) {
|
|
PutString(pEnumStr, "*");
|
|
} else if (firstNameSeen) {
|
|
PutString(pEnumStr, ".");
|
|
}
|
|
firstNameSeen = True;
|
|
PutString(pEnumStr, XrmQuarkToString(*quarks));
|
|
}
|
|
s = value->addr;
|
|
i = value->size;
|
|
PutString(pEnumStr, ":\t");
|
|
if(i) i--;
|
|
|
|
if (i && (*s == ' ' || *s == '\t'))
|
|
PutByte(pEnumStr, '\\'); /* preserve leading whitespace */
|
|
|
|
while (i--) {
|
|
c = *s++;
|
|
if (c == '\n') {
|
|
if (i)
|
|
PutString(pEnumStr, "\\n\\\n");
|
|
else
|
|
PutString(pEnumStr, "\\n");
|
|
} else if (c == '\\')
|
|
PutString(pEnumStr, "\\\\");
|
|
else if ((c < ' ' && c != '\t') ||
|
|
((unsigned char)c >= 0x7f && (unsigned char)c < 0xa0))
|
|
{
|
|
char temp[4];
|
|
(void) sprintf(temp, "\\%03o", (unsigned char)c);
|
|
PutString(pEnumStr, temp);
|
|
}
|
|
else
|
|
PutByte(pEnumStr, c);
|
|
}
|
|
PutByte(pEnumStr, '\n');
|
|
pEnumStr->stringDb[pEnumStr->nextPos] = (char)'\0';
|
|
return False;
|
|
}
|
|
|
|
/*
|
|
* XpGetAttributes returns a string database version of the Xrm database
|
|
* for the specified context and class. This function can also return the
|
|
* contents of the server attributes, in which case the pContext parameter
|
|
* is ignored.
|
|
*
|
|
* The caller is responsible for freeing the returned string,
|
|
* unlike XpGetOneAttribute, where the caller must not free the string.
|
|
*/
|
|
char *
|
|
XpGetAttributes(
|
|
XpContextPtr pContext,
|
|
XPAttributes class)
|
|
{
|
|
ContextAttrPtr pCtxtAttrs;
|
|
XrmDatabase db = (XrmDatabase)NULL;
|
|
StringDbStruct enumStruct;
|
|
XrmQuark empty = NULLQUARK;
|
|
|
|
if(class == XPServerAttr)
|
|
db = systemAttributes.server;
|
|
else
|
|
{
|
|
pCtxtAttrs=(ContextAttrPtr)pContext->devPrivates[attrCtxtPrivIndex].ptr;
|
|
switch(class)
|
|
{
|
|
case XPServerAttr:
|
|
db = systemAttributes.server;
|
|
break;
|
|
case XPPrinterAttr:
|
|
db = pCtxtAttrs->printerAttrs;
|
|
break;
|
|
case XPDocAttr:
|
|
db = pCtxtAttrs->docAttrs;
|
|
break;
|
|
case XPJobAttr:
|
|
db = pCtxtAttrs->jobAttrs;
|
|
break;
|
|
case XPPageAttr:
|
|
db = pCtxtAttrs->pageAttrs;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if(db == (XrmDatabase)NULL)
|
|
{
|
|
char *retval = (char *)xalloc(1);
|
|
retval[0] = (char)'\0';
|
|
return retval;
|
|
}
|
|
|
|
if((enumStruct.stringDb = (char *)xalloc(1024)) == (char *)NULL)
|
|
return (char *)NULL;
|
|
enumStruct.stringDb[0] = (char)'\0';
|
|
enumStruct.nextPos = 0;
|
|
enumStruct.space = 1024;
|
|
(void)XrmEnumerateDatabase(db, &empty, &empty, XrmEnumAllLevels,
|
|
AppendEntry, (XPointer) &enumStruct);
|
|
|
|
return enumStruct.stringDb;
|
|
}
|
|
|
|
int
|
|
XpAugmentAttributes(
|
|
XpContextPtr pContext,
|
|
XPAttributes class,
|
|
char *attributes)
|
|
{
|
|
XrmDatabase db;
|
|
ContextAttrPtr pCtxtAttrs;
|
|
|
|
db = XrmGetStringDatabase(attributes);
|
|
if(db == (XrmDatabase)NULL) return BadAlloc;
|
|
|
|
pCtxtAttrs = (ContextAttrPtr)pContext->devPrivates[attrCtxtPrivIndex].ptr;
|
|
switch(class)
|
|
{
|
|
case XPPrinterAttr:
|
|
XrmMergeDatabases(db, &pCtxtAttrs->printerAttrs);
|
|
break;
|
|
case XPDocAttr:
|
|
XrmMergeDatabases(db, &pCtxtAttrs->docAttrs);
|
|
break;
|
|
case XPJobAttr:
|
|
XrmMergeDatabases(db, &pCtxtAttrs->jobAttrs);
|
|
break;
|
|
case XPPageAttr:
|
|
XrmMergeDatabases(db, &pCtxtAttrs->pageAttrs);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return Success;
|
|
}
|
|
|
|
/*
|
|
* XpSetAttributes - sets the attribute stores for a specified context.
|
|
*/
|
|
int
|
|
XpSetAttributes(
|
|
XpContextPtr pContext,
|
|
XPAttributes class,
|
|
char *attributes)
|
|
{
|
|
XrmDatabase db;
|
|
ContextAttrPtr pCtxtAttrs;
|
|
|
|
db = XrmGetStringDatabase(attributes);
|
|
if(db == (XrmDatabase)NULL) return BadAlloc;
|
|
|
|
pCtxtAttrs=(ContextAttrPtr)pContext->devPrivates[attrCtxtPrivIndex].ptr;
|
|
switch(class)
|
|
{
|
|
case XPPrinterAttr:
|
|
if(pCtxtAttrs->printerAttrs != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(pCtxtAttrs->printerAttrs);
|
|
pCtxtAttrs->printerAttrs = db;
|
|
break;
|
|
case XPDocAttr:
|
|
if(pCtxtAttrs->docAttrs != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(pCtxtAttrs->docAttrs);
|
|
pCtxtAttrs->docAttrs = db;
|
|
break;
|
|
case XPJobAttr:
|
|
if(pCtxtAttrs->jobAttrs != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(pCtxtAttrs->jobAttrs);
|
|
pCtxtAttrs->jobAttrs = db;
|
|
break;
|
|
case XPPageAttr:
|
|
if(pCtxtAttrs->pageAttrs != (XrmDatabase)NULL)
|
|
XrmDestroyDatabase(pCtxtAttrs->pageAttrs);
|
|
pCtxtAttrs->pageAttrs = db;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return Success;
|
|
}
|
|
|
|
void
|
|
XpAddPrinterAttribute(
|
|
char *printerName,
|
|
char *printerQualifier,
|
|
char *attributeName,
|
|
char *attributeValue)
|
|
{
|
|
PrAttrPtr pAttr;
|
|
|
|
for(pAttr = attrList; pAttr != (PrAttrPtr)NULL; pAttr = pAttr->next)
|
|
{
|
|
if(!strcmp(printerQualifier, pAttr->qualifier))
|
|
{
|
|
XrmPutStringResource(&pAttr->printerAttrs, attributeName,
|
|
attributeValue);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const char *
|
|
XpGetPrinterAttribute(const char *printerName,
|
|
const char *attribute)
|
|
{
|
|
PrAttrPtr pAttr;
|
|
XrmValue value;
|
|
char *type;
|
|
|
|
for(pAttr = attrList; pAttr != (PrAttrPtr)NULL; pAttr = pAttr->next)
|
|
{
|
|
if(!strcmp(printerName, pAttr->qualifier))
|
|
{
|
|
char *attrStr;
|
|
|
|
attrStr = (char *)xalloc(strlen(printerName) + strlen(attribute) +
|
|
2);
|
|
sprintf(attrStr, "%s.%s", printerName, attribute);
|
|
XrmGetResource(pAttr->printerAttrs, attrStr, attrStr,
|
|
&type, &value);
|
|
xfree(attrStr);
|
|
break;
|
|
}
|
|
}
|
|
if(value.addr != (XPointer)NULL && strlen(value.addr) != 0)
|
|
return value.addr;
|
|
else
|
|
return "";
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* The following routines are not attribute routines, but are rather
|
|
* spooler interface functions. They should presumably move to
|
|
* a SpoolerIf.c of some similarly named file.
|
|
*
|
|
******************************************************************************/
|
|
#include <locale.h>
|
|
|
|
static char serverAttrStr[] = "*document-attributes-supported: copy-count\n\
|
|
*job-attributes-supported: job-name job-owner\
|
|
notification-profile xp-spooler-command-options\n\
|
|
*multiple-documents-supported: False";
|
|
|
|
XrmDatabase
|
|
XpSpoolerGetServerAttributes(void)
|
|
{
|
|
char *totalAttrs, *localeName;
|
|
XrmDatabase db;
|
|
|
|
localeName = setlocale(LC_CTYPE, (char *)NULL);
|
|
if(!localeName || strlen(localeName) == 0)
|
|
localeName = "C";
|
|
|
|
if((totalAttrs = (char *)xalloc(strlen(serverAttrStr) + strlen(localeName)
|
|
+ 11)) == (char *)NULL)
|
|
return (XrmDatabase)NULL;
|
|
sprintf(totalAttrs, "%s\n%s\t%s", serverAttrStr, "*locale:", localeName);
|
|
|
|
db = XrmGetStringDatabase(totalAttrs);
|
|
xfree(totalAttrs);
|
|
return db;
|
|
}
|
|
|
|
/*
|
|
* Tailf() works similar to "/bin/tail -f fd_in >fd_out" until
|
|
* the process |child| terminates (the child status is
|
|
* returned in |child_status|).
|
|
* This function is used to copy the stdout/stderr output of a
|
|
* child to fd_out until the child terminates.
|
|
*/
|
|
static
|
|
void Tailf(int fd_in, int fd_out, pid_t child, int *child_status)
|
|
{
|
|
char b[256];
|
|
ssize_t sz;
|
|
Bool childDone = FALSE;
|
|
struct timeval timeout;
|
|
long fpos = 0; /* XXX: this is not correct for largefile support */
|
|
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 100000;
|
|
|
|
for(;;)
|
|
{
|
|
/* Check whether the child is still alive or not */
|
|
if (waitpid(child, child_status, WNOHANG) == child)
|
|
childDone = TRUE;
|
|
|
|
/* Copy traffic from |fd_in| to |fd_out|
|
|
* (Note we have to use |pread()| here to avoid race conditions
|
|
* between a child process writing to the same file using the
|
|
* same file pointer (|dup(2)| and |fork(2)| just duplicate the
|
|
* file handle but not the pointer)).
|
|
*/
|
|
while ((sz = pread(fd_in, b, sizeof(b), fpos)) > 0)
|
|
{
|
|
fpos += sz;
|
|
write(fd_out, b, sz);
|
|
}
|
|
|
|
if (childDone)
|
|
break;
|
|
|
|
(void)select(0, NULL, NULL, NULL, &timeout);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SendFileToCommand takes three character pointers - the file name,
|
|
* the command to execute,
|
|
* and the "argv" style NULL-terminated vector of arguments for the command.
|
|
* The command is exec'd, and the file contents are sent to the command
|
|
* via stdin.
|
|
*
|
|
* WARNING: This function will try to adopt the userId of the supplied
|
|
* user name prior to exec'ing the supplied command.
|
|
*/
|
|
static void
|
|
SendFileToCommand(
|
|
XpContextPtr pContext,
|
|
char *fileName,
|
|
char *pCommand,
|
|
char **argVector,
|
|
char *userName)
|
|
{
|
|
pid_t childPid;
|
|
int pipefd[2];
|
|
int status;
|
|
struct stat statBuf;
|
|
FILE *fp, *outPipe;
|
|
FILE *resFp; /* output from launched command */
|
|
int resfd;
|
|
|
|
resFp = tmpfile();
|
|
if (resFp == NULL)
|
|
{
|
|
ErrorF("SendFileToCommand: Cannot open temporary file for command output\n");
|
|
return;
|
|
}
|
|
resfd = fileno(resFp);
|
|
|
|
if(pipe(pipefd))
|
|
{
|
|
ErrorF("SendFileToCommand: Cannot open pipe\n");
|
|
fclose(resFp);
|
|
return;
|
|
}
|
|
|
|
if(stat(fileName, &statBuf) < 0 || (int)statBuf.st_size == 0)
|
|
{
|
|
close(pipefd[0]);
|
|
close(pipefd[1]);
|
|
fclose(resFp);
|
|
return;
|
|
}
|
|
|
|
fp = fopen(fileName, "r");
|
|
if(fp == (FILE *)NULL)
|
|
{
|
|
ErrorF("SendFileToCommand: Cannot open scratch spool file '%s'\n", fileName);
|
|
close(pipefd[0]);
|
|
close(pipefd[1]);
|
|
fclose(resFp);
|
|
return;
|
|
}
|
|
|
|
if((childPid = fork()) == 0)
|
|
{
|
|
close(pipefd[1]);
|
|
|
|
/* Replace current stdin with input from the pipe */
|
|
close(STDIN_FILENO);
|
|
dup(pipefd[0]);
|
|
close(pipefd[0]);
|
|
|
|
/* Close current stdout and redirect it to resfd */
|
|
close(STDOUT_FILENO);
|
|
dup(resfd);
|
|
|
|
/* Close current stderr and redirect it to resfd
|
|
* (valgrind may not like that, in this case simply start it using
|
|
* % valgrind 50>/dev/tty --logfile-fd=50 <more-options> ./Xprt ... #)
|
|
*/
|
|
close(STDERR_FILENO);
|
|
dup(resfd);
|
|
|
|
fclose(resFp);
|
|
|
|
/*
|
|
* If a user name is specified, try to set our uid to match that
|
|
* user name. This is to allow e.g. a banner page to show the
|
|
* name of the printing user rather than the user who started
|
|
* the print server.
|
|
*/
|
|
if(userName)
|
|
{
|
|
uid_t myUid;
|
|
|
|
if((myUid = geteuid()) == (uid_t)0)
|
|
{
|
|
struct passwd *pPasswd;
|
|
|
|
if((pPasswd = getpwnam(userName)))
|
|
{
|
|
if (setgid((gid_t)pPasswd->pw_gid) != 0)
|
|
perror("SendFileToCommand: setgid() failure.");
|
|
|
|
if (initgroups(userName, (gid_t)pPasswd->pw_gid) != 0)
|
|
perror("SendFileToCommand: initgroups() failure.");
|
|
|
|
if (setuid((uid_t)pPasswd->pw_uid) != 0)
|
|
perror("SendFileToCommand: setuid() failure.");
|
|
}
|
|
}
|
|
}
|
|
/* return BadAlloc? */
|
|
if (execv(pCommand, argVector) == -1) {
|
|
FatalError("unable to exec '%s'", pCommand);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
(void) close(pipefd[0]);
|
|
|
|
outPipe = fdopen(pipefd[1], "w");
|
|
(void) TransferBytes(fp, outPipe, (int)statBuf.st_size);
|
|
|
|
(void) fclose(outPipe);
|
|
(void) fclose(fp);
|
|
|
|
/* Wait for spooler child (and send all it's output to stderr) */
|
|
Tailf(resfd, STDERR_FILENO, childPid, &status);
|
|
|
|
if (status != EXIT_SUCCESS)
|
|
{
|
|
ErrorF("SendFileToCommand: spooler command returned non-zero status %d.\n", status);
|
|
}
|
|
|
|
/* Store "xp-spooler-command-results" XPJobAttr that the
|
|
* client can fetch it on demand */
|
|
if ((fstat(resfd, &statBuf) >= 0) && (statBuf.st_size >= 0))
|
|
{
|
|
long bufSize;
|
|
char *buf;
|
|
|
|
bufSize = statBuf.st_size;
|
|
|
|
/* Clamp buffer size to 4MB to prevent that we allocate giant
|
|
* buffers if the spooler goes mad and spams it's stdout/stderr
|
|
* channel. */
|
|
bufSize = MIN(bufSize, 4*1024*1024);
|
|
|
|
buf = xalloc(bufSize+1);
|
|
if (buf != NULL)
|
|
{
|
|
bufSize = pread(resfd, buf, bufSize, 0);
|
|
buf[bufSize]='\0';
|
|
|
|
/* XXX: This should be converted from local multibyte encoding to
|
|
* Compound Text encoding first */
|
|
XpPutOneAttribute(pContext, XPJobAttr, "xp-spooler-command-results", buf);
|
|
|
|
xfree(buf);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrorF("SendFileToCommand: fstat() failed.\n");
|
|
}
|
|
|
|
fclose(resFp);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* ReplaceAllKeywords causes all the predefined keywords (e.g. %options%)
|
|
* to be replaced with the appropriate values derived from the attribute
|
|
* store for the supplied print context. The ReplaceAnyString utility
|
|
* routine is used to perform the actual replacements.
|
|
*/
|
|
|
|
static char *
|
|
ReplaceAllKeywords(
|
|
XpContextPtr pContext,
|
|
char *command)
|
|
{
|
|
char *cmdOpt;
|
|
|
|
cmdOpt = XpGetOneAttribute(pContext, XPPrinterAttr,
|
|
"xp-spooler-printer-name");
|
|
if(cmdOpt != (char *)NULL && strlen(cmdOpt) != 0)
|
|
command = ReplaceAnyString(command, "%printer-name%", cmdOpt);
|
|
else
|
|
command = ReplaceAnyString(command, "%printer-name%",
|
|
pContext->printerName);
|
|
|
|
cmdOpt = XpGetOneAttribute(pContext, XPDocAttr, "copy-count");
|
|
if(cmdOpt != (char *)NULL && strlen(cmdOpt) != 0)
|
|
command = ReplaceAnyString(command, "%copy-count%", cmdOpt);
|
|
else
|
|
command = ReplaceAnyString(command, "%copy-count%", "1");
|
|
|
|
cmdOpt = XpGetOneAttribute(pContext, XPJobAttr, "job-name");
|
|
if(cmdOpt != (char *)NULL && strlen(cmdOpt) != 0)
|
|
command = ReplaceAnyString(command, "%job-name%", cmdOpt);
|
|
else
|
|
command = ReplaceAnyString(command, "%job-name%", "");
|
|
|
|
cmdOpt = XpGetOneAttribute(pContext, XPJobAttr, "job-owner");
|
|
if(cmdOpt != (char *)NULL && strlen(cmdOpt) != 0)
|
|
command = ReplaceAnyString(command, "%job-owner%", cmdOpt);
|
|
else
|
|
command = ReplaceAnyString(command, "%job-owner%", "");
|
|
|
|
cmdOpt = XpGetOneAttribute(pContext, XPJobAttr,
|
|
"xp-spooler-command-options");
|
|
if(cmdOpt != (char *)NULL && strlen(cmdOpt) != 0)
|
|
command = ReplaceAnyString(command, "%options%", cmdOpt);
|
|
else
|
|
command = ReplaceAnyString(command, "%options%", "");
|
|
|
|
/* New in xprint.mozdev.org release 007 - replace "%xpconfigdir%" with
|
|
* location of $XPCONFIGDIR */
|
|
command = ReplaceAnyString(command, "%xpconfigdir%", XpGetConfigDirBase());
|
|
|
|
return command;
|
|
}
|
|
|
|
#ifdef __QNX__
|
|
#define toascii( c ) ((unsigned)(c) & 0x007f)
|
|
#endif
|
|
|
|
#if defined(CSRG_BASED) || \
|
|
defined(linux) || \
|
|
defined(__CYGWIN__) || \
|
|
(defined(sun) && !defined(SVR4)) || \
|
|
(defined(SVR4) && !defined(sun) && !defined(__UNIXWARE__)) || \
|
|
defined(ISC) || \
|
|
defined(Lynx) || \
|
|
defined(__QNX__) || \
|
|
defined(__DARWIN__)
|
|
#define iswspace(c) (isascii(c) && isspace(toascii(c)))
|
|
#endif
|
|
|
|
/*
|
|
* GetToken - takes in a string and returns a malloc'd copy of the
|
|
* first non-white-space sequence of characters in the string.
|
|
* It returns the number of _bytes_ (NOT characters) parsed through
|
|
* the inStr to get to the end of the returned token.
|
|
*/
|
|
static int
|
|
GetToken(
|
|
char *inStr,
|
|
char **outStr)
|
|
{
|
|
size_t mbCurMax = MB_CUR_MAX;
|
|
wchar_t curChar;
|
|
int i, numBytes, byteLen = strlen(inStr);
|
|
char *tok;
|
|
|
|
/*
|
|
* read through any leading white space.
|
|
*/
|
|
for(i = 0, numBytes = 0; i < byteLen; i += numBytes)
|
|
{
|
|
numBytes = mbtowc(&curChar, &inStr[i], mbCurMax);
|
|
if(!iswspace(curChar))
|
|
break;
|
|
}
|
|
tok = inStr + i;
|
|
|
|
/*
|
|
* find the end of the token.
|
|
*/
|
|
byteLen = strlen(tok);
|
|
for(i = 0, numBytes = 0; i < byteLen; i += numBytes)
|
|
{
|
|
numBytes = mbtowc(&curChar, &tok[i], mbCurMax);
|
|
if(iswspace(curChar))
|
|
break;
|
|
}
|
|
|
|
if((*outStr = (char *)xalloc(i + 1)) == (char *)NULL)
|
|
return 0;
|
|
strncpy(*outStr, tok, i);
|
|
(*outStr)[i] = (char)'\0';
|
|
return (tok + i) - inStr;
|
|
}
|
|
|
|
static void
|
|
FreeVector(
|
|
char **vector)
|
|
{
|
|
int i;
|
|
|
|
if(vector == (char **)NULL) return;
|
|
|
|
for(i = 0; vector[i] != (char *)NULL; i++)
|
|
xfree(vector[i]);
|
|
xfree(vector);
|
|
}
|
|
|
|
|
|
/*
|
|
* AddVector appends the pAddition arg vector to the pTarget arg vector.
|
|
* If the pTarget cannot be realloc'd, then pTarget is set to NULL.
|
|
*/
|
|
static void
|
|
AddVector(
|
|
char ***pTarget,
|
|
char **pAddition)
|
|
{
|
|
int numTarget, numAdd, i;
|
|
|
|
for(numTarget = 0; (*pTarget)[numTarget] != (char *)NULL; numTarget++)
|
|
;
|
|
for(numAdd = 0; pAddition[numAdd] != (char *)NULL; numAdd++)
|
|
;
|
|
|
|
*pTarget = (char **)xrealloc((void *)*pTarget, (numTarget + numAdd + 1) *
|
|
sizeof(char *));
|
|
if(*pTarget == (char **)NULL)
|
|
return;
|
|
for(i = 0; i < numAdd; i++)
|
|
(*pTarget)[numTarget + i] = pAddition[i];
|
|
|
|
(*pTarget)[numTarget + numAdd] = (char *)NULL;
|
|
}
|
|
|
|
static char **
|
|
BuildArgVector(
|
|
char *argString,
|
|
XpContextPtr pContext)
|
|
{
|
|
char **pVector;
|
|
char *curTok;
|
|
int numChars, i;
|
|
static int beenHere = 0; /* prevent recursion on embedded %options%
|
|
*/
|
|
|
|
pVector = (char **)xalloc(sizeof(char *));
|
|
pVector[0] = (char *)NULL;
|
|
for(i = 0; (numChars = GetToken(argString, &curTok)) != 0;
|
|
i++, argString += numChars)
|
|
{
|
|
if(beenHere || strcmp(curTok, "%options%"))
|
|
{
|
|
if(curTok[0] == (char)'\0')
|
|
{
|
|
xfree(curTok);
|
|
}
|
|
else
|
|
{
|
|
pVector = (char **)xrealloc((void *)pVector,
|
|
(i + 2)*sizeof(char *));
|
|
if(pVector == (char **)NULL)
|
|
return (char **)NULL;
|
|
pVector[i] = curTok;
|
|
pVector[i + 1] = (char *)NULL;
|
|
}
|
|
}
|
|
else if(!beenHere)
|
|
{
|
|
char **optionsVec;
|
|
|
|
curTok = ReplaceAllKeywords(pContext, curTok);
|
|
beenHere = 1;
|
|
optionsVec = BuildArgVector(curTok, pContext);
|
|
xfree(curTok);
|
|
beenHere = 0;
|
|
AddVector(&pVector, optionsVec);
|
|
xfree(optionsVec);
|
|
}
|
|
}
|
|
if(numChars == 0 && curTok != (char *)NULL)
|
|
xfree(curTok);
|
|
return pVector;
|
|
}
|
|
|
|
/*
|
|
* VectorizeCommand takes a string and breaks it into a command name and
|
|
* an array of character pointers suitable for handing to execv. The
|
|
* array is NULL-terminated.
|
|
* The returned char * is the command name, and should be freed when no
|
|
* longer needed. The array elements returned in the pVector parameter
|
|
* should be individually freed, and the array itself should also be
|
|
* freed when no longer needed.
|
|
*/
|
|
static char *
|
|
VectorizeCommand(
|
|
char *command,
|
|
char ***pVector,
|
|
XpContextPtr pContext)
|
|
{
|
|
char *cmdName;
|
|
int numChars;
|
|
|
|
if(command == (char *)NULL)
|
|
return (char *)NULL;
|
|
|
|
numChars = GetToken(command, &cmdName);
|
|
|
|
if(cmdName == (char *)NULL)
|
|
return (char *)NULL;
|
|
|
|
/* Mangle the command name, too... */
|
|
cmdName = ReplaceAllKeywords(pContext, cmdName);
|
|
|
|
if(cmdName == (char *)NULL)
|
|
return (char *)NULL;
|
|
|
|
*pVector = BuildArgVector(command, pContext);
|
|
|
|
return cmdName;
|
|
}
|
|
|
|
int
|
|
XpSubmitJob(fileName, pContext)
|
|
char *fileName;
|
|
XpContextPtr pContext;
|
|
{
|
|
char **vector, *cmdNam, *command, *userName;
|
|
int i;
|
|
|
|
command = XpGetOneAttribute(pContext, XPPrinterAttr, "xp-spooler-command");
|
|
if(command == (char *)NULL || strlen(command) == 0)
|
|
{
|
|
if( spooler_type )
|
|
{
|
|
command = strdup(spooler_type->spool_command);
|
|
}
|
|
else
|
|
{
|
|
ErrorF("XpSubmitJob: No default spool command defined.\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
command = strdup(command);
|
|
}
|
|
if(command == (char *)NULL)
|
|
{
|
|
ErrorF("XpSubmitJob: No spooler command found, cannot submit job.\n");
|
|
return BadAlloc;
|
|
}
|
|
|
|
cmdNam = VectorizeCommand(command, &vector, pContext);
|
|
xfree(command);
|
|
|
|
if(cmdNam == (char *)NULL)
|
|
return BadAlloc;
|
|
|
|
for(i = 0; vector[i] != (char *)NULL; i++)
|
|
{
|
|
vector[i] = ReplaceAllKeywords(pContext, vector[i]);
|
|
if(vector[i] == (char *)NULL)
|
|
{
|
|
xfree(cmdNam);
|
|
for(i = 0; vector[i] != (char *)NULL; i++)
|
|
xfree(vector[i]);
|
|
xfree(vector);
|
|
return BadAlloc;
|
|
}
|
|
}
|
|
|
|
userName = XpGetOneAttribute(pContext, XPJobAttr, "job-owner");
|
|
if(userName != (char *)NULL && strlen(userName) == 0)
|
|
userName = (char *)NULL;
|
|
|
|
SendFileToCommand(pContext, fileName, cmdNam, vector, userName);
|
|
|
|
FreeVector(vector);
|
|
xfree(cmdNam);
|
|
|
|
return Success;
|
|
}
|
|
|
|
/*
|
|
* SearchInputTrays()
|
|
*
|
|
* Given a tray, return the medium in the tray. Conversely, given a
|
|
* medium, return a tray in which it can be found. In either case,
|
|
* return NULL if the given tray or medium cannot be found.
|
|
*/
|
|
#define TRAY 0
|
|
#define MEDIUM 1
|
|
|
|
static char *
|
|
SearchInputTrays(XpContextPtr pCon,
|
|
int which,
|
|
char *val)
|
|
{
|
|
char *inputTraysMedium, tray[80], medium[80], *copy;
|
|
char *pS, *pE, *pLast;
|
|
|
|
inputTraysMedium = XpGetOneAttribute( pCon, XPPrinterAttr,
|
|
"input-trays-medium" );
|
|
|
|
copy = strdup( inputTraysMedium );
|
|
pS = copy;
|
|
pLast = copy + strlen( copy );
|
|
|
|
while( pS < pLast )
|
|
{
|
|
while( *pS && *pS != '{' )
|
|
pS++;
|
|
|
|
pE = ++pS;
|
|
while( *pE && *pE != '}' )
|
|
pE++;
|
|
*pE = '\0';
|
|
|
|
sscanf( pS, "%s %s", tray, medium );
|
|
|
|
if( which == MEDIUM && !strcmp( val, medium ) )
|
|
{
|
|
xfree( copy );
|
|
return strdup( tray );
|
|
}
|
|
|
|
if( which == TRAY && !strcmp( val, tray ) )
|
|
{
|
|
xfree( copy );
|
|
return strdup( medium );
|
|
}
|
|
|
|
pS = pE + 1;
|
|
}
|
|
|
|
xfree( copy );
|
|
return strdup( NULL_STRING );
|
|
}
|
|
|
|
/*
|
|
* XpGetTrayMediumFromContext()
|
|
*
|
|
* Given a print context, hit the input-trays-medium,
|
|
* default-input-tray and default-medium attributes to find the
|
|
* appropriate tray to use, and the medium in that tray.
|
|
*/
|
|
void
|
|
XpGetTrayMediumFromContext(XpContextPtr pCon,
|
|
char **medium,
|
|
char **tray)
|
|
{
|
|
char *defMedium, *defTray;
|
|
char *t, *m;
|
|
|
|
defMedium = XpGetOneAttribute( pCon, XPPageAttr,
|
|
"default-medium" );
|
|
if( *defMedium == '\0' )
|
|
defMedium = XpGetOneAttribute( pCon, XPDocAttr,
|
|
"default-medium" );
|
|
|
|
defTray = XpGetOneAttribute( pCon, XPPageAttr,
|
|
"default-input-tray" );
|
|
if( *defTray == '\0' )
|
|
defTray = XpGetOneAttribute( pCon, XPDocAttr,
|
|
"default-input-tray" );
|
|
|
|
/*
|
|
* First, check to see if the default tray has the default medium
|
|
* installed. This is the ideal case.
|
|
*/
|
|
m = SearchInputTrays( pCon, TRAY, defTray );
|
|
if( !strcmp( m, defMedium ) )
|
|
{
|
|
xfree( m );
|
|
*tray = strdup( defTray );
|
|
*medium = strdup( defMedium );
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If the default tray doesn't have the default medium, search for
|
|
* a tray which has the default medium.
|
|
*/
|
|
t = SearchInputTrays( pCon, MEDIUM, defMedium );
|
|
if( t )
|
|
{
|
|
*tray = t;
|
|
*medium = strdup( defMedium );
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If all else fails, just return the default tray, and whatever
|
|
* medium happens to be there. Note that we simply return
|
|
* whatever is in the attribute store. Any further correction is
|
|
* left up to the DDX driver.
|
|
*/
|
|
*tray = strdup( defTray );
|
|
*medium = m;
|
|
xfree( t );
|
|
}
|