Back to index

lightning-sunbird  0.9+nobinonly
nsPrintJobPS.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ex: set tabstop=8 softtabstop=4 shiftwidth=4 expandtab: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Ken Herron <kherron@fastmail.us>.
00020  * Portions created by the Initial Developer are Copyright (C) 2004
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 
00040 #include "nscore.h"
00041 #include "nsIDeviceContext.h"   // NS_ERROR_GFX_*
00042 #include "nsIDeviceContextPS.h" // NS_POSTSCRIPT_DRIVER_NAME_LEN
00043 #include "nsIDeviceContextSpecPS.h"
00044 #include "nsPrintJobPS.h"
00045 #include "nsPSPrinters.h"
00046 #include "nsReadableUtils.h"
00047 
00048 #include "prenv.h"
00049 #include "prinit.h"
00050 #include "prlock.h"
00051 #include "prprf.h"
00052 
00053 #include <stdlib.h>
00054 #include <sys/wait.h>
00055 #include <unistd.h>
00056 
00057 
00058 /* Routines to set environment variables. These are defined toward
00059  * the end of this file.
00060  */
00061 static PRStatus EnvLock();
00062 static PRStatus EnvSetPrinter(nsCString&);
00063 static void EnvClear();
00064 
00065 
00066 /* ~nsIPrintJobPS() is virtual, so must implement a destructor. */
00067 nsIPrintJobPS::~nsIPrintJobPS() {}
00068 
00069 /**** nsPrintJobPreviewPS - Stub class for print preview ****/
00070 nsresult
00071 nsPrintJobPreviewPS::Init(nsIDeviceContextSpecPS *aSpec)
00072 {
00073     return NS_OK;
00074 }
00075 
00076 
00077 /**** nsPrintJobFilePS - Print-to-file support ****/
00078 
00079 /* Print-to-file constructor */
00080 nsPrintJobFilePS::nsPrintJobFilePS() : mDestHandle(nsnull) { }
00081 
00082 /* Print-to-file destructor */
00083 nsPrintJobFilePS::~nsPrintJobFilePS()
00084 {
00085     if (mDestHandle)
00086         fclose(mDestHandle);
00087 }
00088 
00093 nsresult
00094 nsPrintJobFilePS::Init(nsIDeviceContextSpecPS *aSpec)
00095 {
00096     NS_PRECONDITION(aSpec, "aSpec must not be NULL");
00097 #ifdef DEBUG
00098     PRBool toPrinter;
00099     aSpec->GetToPrinter(toPrinter);
00100     NS_PRECONDITION(!toPrinter, "This print job is to a printer");
00101 #endif
00102     const char *path;
00103     aSpec->GetPath(&path);
00104     mDestination = path;
00105     return NS_OK;
00106 }
00107 
00108 
00113 nsresult
00114 nsPrintJobFilePS::StartSubmission(FILE **aHandle)
00115 {
00116     NS_PRECONDITION(aHandle, "aHandle is NULL");
00117     NS_PRECONDITION(!mDestination.IsEmpty(), "No destination");
00118     NS_PRECONDITION(!mDestHandle, "Already have a destination handle");
00119 
00120     nsCOMPtr<nsILocalFile> destFile;
00121     nsresult rv = NS_NewNativeLocalFile(GetDestination(),
00122             PR_FALSE, getter_AddRefs(destFile));
00123     if (NS_SUCCEEDED(rv))
00124         rv = destFile->OpenANSIFileDesc("w", &mDestHandle);
00125     NS_ENSURE_SUCCESS(rv, NS_ERROR_GFX_PRINTER_COULD_NOT_OPEN_FILE);
00126     NS_POSTCONDITION(mDestHandle,
00127             "OpenANSIFileDesc succeeded but no file handle");
00128     *aHandle = mDestHandle;
00129     return rv;
00130 }
00131 
00132 
00136 nsresult
00137 nsPrintJobFilePS::FinishSubmission()
00138 {
00139     NS_PRECONDITION(mDestHandle, "No destination file handle");
00140 
00141     fclose(mDestHandle);
00142     mDestHandle = nsnull;
00143     return NS_OK;
00144 }
00145 
00146 
00147 #ifdef VMS
00148 
00149 /**** Print-to-command on VMS. ****/
00150 
00151 /* This implementation writes the print job to a temporary file, then runs
00152  * the print command with the name of that file appended.
00153  */
00154 
00155 
00160 nsresult
00161 nsPrintJobVMSCmdPS::Init(nsIDeviceContextSpecPS *aSpec)
00162 {
00163     NS_PRECONDITION(aSpec, "argument must not be NULL");
00164 #ifdef DEBUG
00165     PRBool toPrinter;
00166     aSpec->GetToPrinter(toPrinter);
00167     NS_PRECONDITION(toPrinter, "This print job is not to a printer");
00168 #endif
00169 
00170     /* Print command. This is stored as the destination string. */
00171     const char *command;
00172     aSpec->GetCommand(&command);
00173     SetDestination(command);
00174 
00175     /* Printer name */
00176     const char *printerName;
00177     aSpec->GetPrinterName(&printerName);
00178     if (printerName) {
00179         const char *slash = strchr(printerName, '/');
00180         if (slash)
00181             printerName = slash + 1;
00182         if (0 != strcmp(printerName, "default"))
00183             mPrinterName = printerName;
00184     }
00185     return NS_OK;
00186 }
00187 
00188 
00194 nsresult
00195 nsPrintJobVMSCmdPS::StartSubmission(FILE **aHandle)
00196 {
00197     NS_PRECONDITION(aHandle, "aHandle is NULL");
00198     NS_PRECONDITION(!GetDestination().IsEmpty(), "No destination");
00199     NS_PRECONDITION(!GetDestHandle(), "Already have a destination handle");
00200 
00201     /* Create the final output file */
00202     FILE *printHandle = nsnull;
00203     nsresult rv = mTempFactory.CreateTempFile(
00204             getter_AddRefs(mTempFile), &printHandle, "w+");
00205     if (NS_SUCCEEDED(rv)) {
00206         SetDestHandle(printHandle);
00207         *aHandle = printHandle;
00208     }
00209     return rv;
00210 }
00211 
00212 nsresult
00213 nsPrintJobVMSCmdPS::FinishSubmission()
00214 {
00215     NS_PRECONDITION(GetDestHandle(), "No destination file handle");
00216     NS_PRECONDITION(!GetDestination().IsEmpty(), "No destination");
00217 
00218     /* Close the temporary file handle */
00219     fclose(GetDestHandle());
00220     SetDestHandle(nsnull);
00221 
00222     /* construct the print command */
00223     nsCAutoString printFileName;
00224     nsresult rv = mTempFile->GetNativePath(printFileName);
00225     if (NS_SUCCEEDED(rv)) {
00226         nsCAutoString cmd(GetDestination());
00227         cmd += " "; cmd += printFileName; cmd += ".";
00228 
00229         /* Set up the environment. */
00230         if (PR_SUCCESS != EnvLock())
00231             return NS_ERROR_OUT_OF_MEMORY;
00232         if (!mPrinterName.IsEmpty())
00233             EnvSetPrinter(mPrinterName);
00234 
00235         /* Run the print command */
00236         int presult = system(cmd.get());
00237 
00238         /* Clean up */
00239         EnvClear();
00240         mTempFile->Remove(PR_FALSE);
00241 
00242         rv = (!WIFEXITED(presult) || (EXIT_SUCCESS != WEXITSTATUS(presult)))
00243             ? NS_ERROR_GFX_PRINTER_CMD_FAILURE : NS_OK;
00244     }
00245     return rv;
00246 }
00247 
00248 
00249 #else   /* NOT VMS */
00250 
00251 /**** Print-to-Pipe for unix and unix-like systems ****/
00252 
00253 /* This launches a command using popen(); the print job is then written
00254  * to the pipe.
00255  */
00256 
00257 /* Destructor. We must override the print-to-file destructor in order
00258  * to pclose() any open file handle.
00259  */
00260 nsPrintJobPipePS::~nsPrintJobPipePS()
00261 {
00262     if (GetDestHandle()) {
00263         pclose(GetDestHandle());
00264         SetDestHandle(nsnull);
00265     }
00266 }
00267 
00268 
00273 nsresult
00274 nsPrintJobPipePS::Init(nsIDeviceContextSpecPS *aSpec)
00275 {
00276     NS_PRECONDITION(aSpec, "argument must not be NULL");
00277 #ifdef DEBUG
00278     PRBool toPrinter;
00279     aSpec->GetToPrinter(toPrinter);
00280     NS_PRECONDITION(toPrinter, "Wrong class for this print job");
00281 #endif
00282 
00283     /* Print command. This is stored as the destination string. */
00284     const char *command;
00285     aSpec->GetCommand(&command);
00286     SetDestination(command);
00287 
00288     /* Printer name */
00289     const char *printerName;
00290     aSpec->GetPrinterName(&printerName);
00291     if (printerName) {
00292         const char *slash = strchr(printerName, '/');
00293         if (slash)
00294             printerName = slash + 1;
00295         if (0 != strcmp(printerName, "default"))
00296             mPrinterName = printerName;
00297     }
00298     return NS_OK;
00299 }
00300 
00301 
00306 nsresult
00307 nsPrintJobPipePS::StartSubmission(FILE **aHandle)
00308 {
00309     NS_PRECONDITION(aHandle, "aHandle is NULL");
00310     NS_PRECONDITION(!GetDestination().IsEmpty(), "No destination");
00311     NS_PRECONDITION(!GetDestHandle(), "Already have a destination handle");
00312 
00313     if (PR_SUCCESS != EnvLock())
00314         return NS_ERROR_OUT_OF_MEMORY;  // Couldn't allocate the object?
00315     if (!mPrinterName.IsEmpty())
00316         EnvSetPrinter(mPrinterName);
00317 
00318     FILE *destPipe = popen(GetDestination().get(), "w");
00319     EnvClear();
00320     if (!destPipe)
00321         return NS_ERROR_GFX_PRINTER_CMD_FAILURE;
00322     SetDestHandle(destPipe);
00323     *aHandle = destPipe;
00324     return NS_OK;
00325 }
00326 
00327 nsresult
00328 nsPrintJobPipePS::FinishSubmission()
00329 {
00330     NS_PRECONDITION(GetDestHandle(), "No destination file handle");
00331     NS_PRECONDITION(!GetDestination().IsEmpty(), "No destination");
00332 
00333     int presult = pclose(GetDestHandle());
00334     SetDestHandle(nsnull);
00335     if (!WIFEXITED(presult) || (EXIT_SUCCESS != WEXITSTATUS(presult)))
00336         return NS_ERROR_GFX_PRINTER_CMD_FAILURE;
00337     return NS_OK;
00338 }
00339 
00340 
00341 /**** Print via CUPS ****/
00342 
00347 nsresult
00348 nsPrintJobCUPS::Init(nsIDeviceContextSpecPS *aSpec)
00349 {
00350     NS_PRECONDITION(aSpec, "argument must not be NULL");
00351 #ifdef DEBUG
00352     PRBool toPrinter;
00353     aSpec->GetToPrinter(toPrinter);
00354     NS_PRECONDITION(toPrinter, "Wrong class for this print job");
00355 #endif
00356 
00357     NS_ENSURE_TRUE(mCups.Init(), NS_ERROR_NOT_INITIALIZED);
00358 
00359     /* Printer name */
00360     const char *printerName = nsnull;
00361     aSpec->GetPrinterName(&printerName);
00362     NS_ENSURE_TRUE(printerName, NS_ERROR_GFX_PRINTER_NAME_NOT_FOUND);
00363 
00364     const char *slash = strchr(printerName, '/');
00365     mPrinterName = slash ? slash + 1 : printerName;
00366     mJobTitle.SetIsVoid(PR_TRUE);
00367     return NS_OK;
00368 }
00369 
00370 nsresult
00371 nsPrintJobCUPS::SetNumCopies(int aNumCopies)
00372 {
00373     mNumCopies.Truncate();
00374     if (aNumCopies > 1)
00375         mNumCopies.AppendInt(aNumCopies);
00376     return NS_OK;
00377 }
00378 
00379 
00380 /* According to the cups.development forum, only plain ASCII may be
00381  * reliably used for CUPS print job titles. See
00382  * <http://www.cups.org/newsgroups.php?s523+gcups.development+v530+T0>.
00383  */
00384 void
00385 nsPrintJobCUPS::SetJobTitle(const PRUnichar *aTitle)
00386 {
00387     if (aTitle) {
00388         LossyCopyUTF16toASCII(aTitle, mJobTitle);
00389     }
00390 }
00391 
00392 
00393 nsresult
00394 nsPrintJobCUPS::StartSubmission(FILE **aHandle)
00395 {
00396     NS_ENSURE_TRUE(mCups.IsInitialized(), NS_ERROR_NOT_INITIALIZED);
00397 
00398     int fd;
00399     char buf[FILENAME_MAX];
00400 
00401     fd = (mCups.mCupsTempFd)(buf, sizeof buf);
00402     // The CUPS manual doesn't describe what cupsTempFd() returns to
00403     // indicate failure. -1 is a likely value.
00404     NS_ENSURE_TRUE(fd > 0, NS_ERROR_GFX_PRINTER_COULD_NOT_OPEN_FILE);
00405 
00406     SetDestHandle(fdopen(fd, "r+"));
00407     if (!GetDestHandle()) {
00408         close(fd);
00409         return NS_ERROR_GFX_PRINTER_COULD_NOT_OPEN_FILE;
00410     }
00411     SetDestination(buf);
00412     *aHandle = GetDestHandle();
00413     return NS_OK;
00414 }
00415 
00416 
00417 nsresult
00418 nsPrintJobCUPS::FinishSubmission()
00419 {
00420     NS_ENSURE_TRUE(mCups.IsInitialized(), NS_ERROR_NOT_INITIALIZED);
00421     NS_PRECONDITION(GetDestHandle(), "No destination file handle");
00422     NS_PRECONDITION(!GetDestination().IsEmpty(), "No destination");
00423 
00424     fclose(GetDestHandle());
00425     SetDestHandle(nsnull);
00426 
00427     nsCStringArray printer(3);
00428     printer.ParseString(mPrinterName.get(),"/");
00429 
00430     cups_dest_t *dests, *dest;
00431     int num_dests = (mCups.mCupsGetDests)(&dests);
00432     
00433     if (printer.Count() == 1) {
00434         dest = (mCups.mCupsGetDest)(printer.CStringAt(0)->get(), NULL, num_dests, dests);
00435     } else {
00436         dest = (mCups.mCupsGetDest)(printer.CStringAt(0)->get(), 
00437                                     printer.CStringAt(1)->get(), num_dests, dests);
00438     }
00439 
00440     // Setting result just to get rid of compilation warning
00441     int result=0;
00442     if (dest != NULL) {
00443         if (!mNumCopies.IsEmpty())
00444             dest->num_options = (mCups.mCupsAddOption)("copies",
00445                                                        mNumCopies.get(),
00446                                                        dest->num_options,
00447                                                        &dest->options);
00448         const char *title = mJobTitle.IsVoid() ?
00449             "Untitled Document" : mJobTitle.get();
00450         result = (mCups.mCupsPrintFile)(printer.CStringAt(0)->get(),
00451                                             GetDestination().get(), title, 
00452                                             dest->num_options, dest->options);
00453     }
00454     (mCups.mCupsFreeDests)(num_dests, dests);
00455     unlink(GetDestination().get());
00456 
00457     // cupsPrintFile() result codes below 0x0300 indicate success.
00458     // Individual success codes are defined in the cups headers, but
00459     // we're not including those.
00460     if (dest == NULL)
00461         return NS_ERROR_GFX_PRINTER_NAME_NOT_FOUND;
00462     else
00463         return (result < 0x0300) ? NS_OK : NS_ERROR_GFX_PRINTER_CMD_FAILURE;
00464 }
00465 
00466 
00467 #endif  /* VMS */
00468 
00469 
00470 /* Routines to set the MOZ_PRINTER_NAME environment variable and to
00471  * single-thread print jobs while the variable is set.
00472  */
00473 
00474 static PRLock *EnvLockObj;
00475 static PRCallOnceType EnvLockOnce;
00476 
00477 /* EnvLock callback function */
00478 static PRStatus
00479 EnvLockInit()
00480 {
00481     EnvLockObj = PR_NewLock();
00482     return EnvLockObj ? PR_SUCCESS : PR_FAILURE;
00483 }
00484 
00485 
00493 static PRStatus
00494 EnvLock()
00495 {
00496     if (PR_FAILURE == PR_CallOnce(&EnvLockOnce, EnvLockInit))
00497         return PR_FAILURE;
00498     PR_Lock(EnvLockObj);
00499     return PR_SUCCESS;
00500 }
00501 
00502 
00503 static char *EnvPrinterString;
00504 static const char EnvPrinterName[] = { "MOZ_PRINTER_NAME" };
00505 
00506 
00513 static PRStatus
00514 EnvSetPrinter(nsCString& aPrinter)
00515 {
00516     /* Construct the new environment string */
00517     char *newVar = PR_smprintf("%s=%s", EnvPrinterName, aPrinter.get());
00518     if (!newVar)
00519         return PR_FAILURE;
00520 
00521     /* Add it to the environment and dispose of any old string */
00522     PR_SetEnv(newVar);
00523     if (EnvPrinterString)
00524         PR_smprintf_free(EnvPrinterString);
00525     EnvPrinterString = newVar;
00526 
00527     return PR_SUCCESS;
00528 }
00529 
00530 
00534 static void
00535 EnvClear()
00536 {
00537     if (EnvPrinterString) {
00538         /* On some systems, setenv("FOO") will remove FOO
00539          * from the environment.
00540          */
00541         PR_SetEnv(EnvPrinterName);
00542         if (!PR_GetEnv(EnvPrinterName)) {
00543             /* It must have worked */
00544             PR_smprintf_free(EnvPrinterString);
00545             EnvPrinterString = nsnull;
00546         }
00547     }
00548     PR_Unlock(EnvLockObj);
00549 }