1 /*
   2  * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include <jni.h>
  27 #include <jni_util.h>
  28 #include <jvm_md.h>
  29 #include <dlfcn.h>
  30 #include <cups/cups.h>
  31 #include <cups/ppd.h>
  32 
  33 //#define CUPS_DEBUG
  34 
  35 #ifdef CUPS_DEBUG
  36 #define DPRINTF(x, y) fprintf(stderr, x, y);
  37 #else
  38 #define DPRINTF(x, y)
  39 #endif
  40 
  41 typedef const char* (*fn_cupsServer)(void);
  42 typedef int (*fn_ippPort)(void);
  43 typedef http_t* (*fn_httpConnect)(const char *, int);
  44 typedef void (*fn_httpClose)(http_t *);
  45 typedef char* (*fn_cupsGetPPD)(const char *);
  46 typedef ppd_file_t* (*fn_ppdOpenFile)(const char *);
  47 typedef void (*fn_ppdClose)(ppd_file_t *);
  48 typedef ppd_option_t* (*fn_ppdFindOption)(ppd_file_t *, const char *);
  49 typedef ppd_size_t* (*fn_ppdPageSize)(ppd_file_t *, char *);
  50 
  51 fn_cupsServer j2d_cupsServer;
  52 fn_ippPort j2d_ippPort;
  53 fn_httpConnect j2d_httpConnect;
  54 fn_httpClose j2d_httpClose;
  55 fn_cupsGetPPD j2d_cupsGetPPD;
  56 fn_ppdOpenFile j2d_ppdOpenFile;
  57 fn_ppdClose j2d_ppdClose;
  58 fn_ppdFindOption j2d_ppdFindOption;
  59 fn_ppdPageSize j2d_ppdPageSize;
  60 
  61 
  62 /*
  63  * Initialize library functions.
  64  * // REMIND : move tab , add dlClose before return
  65  */
  66 JNIEXPORT jboolean JNICALL
  67 Java_sun_print_CUPSPrinter_initIDs(JNIEnv *env,
  68                                          jobject printObj) {
  69   void *handle = dlopen(VERSIONED_JNI_LIB_NAME("cups", "2"),
  70                         RTLD_LAZY | RTLD_GLOBAL);
  71 
  72   if (handle == NULL) {
  73     handle = dlopen(JNI_LIB_NAME("cups"), RTLD_LAZY | RTLD_GLOBAL);
  74     if (handle == NULL) {
  75       return JNI_FALSE;
  76     }
  77   }
  78 
  79   j2d_cupsServer = (fn_cupsServer)dlsym(handle, "cupsServer");
  80   if (j2d_cupsServer == NULL) {
  81     dlclose(handle);
  82     return JNI_FALSE;
  83   }
  84 
  85   j2d_ippPort = (fn_ippPort)dlsym(handle, "ippPort");
  86   if (j2d_ippPort == NULL) {
  87     dlclose(handle);
  88     return JNI_FALSE;
  89   }
  90 
  91   j2d_httpConnect = (fn_httpConnect)dlsym(handle, "httpConnect");
  92   if (j2d_httpConnect == NULL) {
  93     dlclose(handle);
  94     return JNI_FALSE;
  95   }
  96 
  97   j2d_httpClose = (fn_httpClose)dlsym(handle, "httpClose");
  98   if (j2d_httpClose == NULL) {
  99     dlclose(handle);
 100     return JNI_FALSE;
 101   }
 102 
 103   j2d_cupsGetPPD = (fn_cupsGetPPD)dlsym(handle, "cupsGetPPD");
 104   if (j2d_cupsGetPPD == NULL) {
 105     dlclose(handle);
 106     return JNI_FALSE;
 107   }
 108 
 109   j2d_ppdOpenFile = (fn_ppdOpenFile)dlsym(handle, "ppdOpenFile");
 110   if (j2d_ppdOpenFile == NULL) {
 111     dlclose(handle);
 112     return JNI_FALSE;
 113 
 114   }
 115 
 116   j2d_ppdClose = (fn_ppdClose)dlsym(handle, "ppdClose");
 117   if (j2d_ppdClose == NULL) {
 118     dlclose(handle);
 119     return JNI_FALSE;
 120 
 121   }
 122 
 123   j2d_ppdFindOption = (fn_ppdFindOption)dlsym(handle, "ppdFindOption");
 124   if (j2d_ppdFindOption == NULL) {
 125     dlclose(handle);
 126     return JNI_FALSE;
 127   }
 128 
 129   j2d_ppdPageSize = (fn_ppdPageSize)dlsym(handle, "ppdPageSize");
 130   if (j2d_ppdPageSize == NULL) {
 131     dlclose(handle);
 132     return JNI_FALSE;
 133   }
 134 
 135   return JNI_TRUE;
 136 }
 137 
 138 /*
 139  * Gets CUPS server name.
 140  *
 141  */
 142 JNIEXPORT jstring JNICALL
 143 Java_sun_print_CUPSPrinter_getCupsServer(JNIEnv *env,
 144                                          jobject printObj)
 145 {
 146     jstring cServer = NULL;
 147     const char* server = j2d_cupsServer();
 148     if (server != NULL) {
 149         // Is this a local domain socket?
 150         if (strncmp(server, "/", 1) == 0) {
 151             cServer = JNU_NewStringPlatform(env, "localhost");
 152         } else {
 153             cServer = JNU_NewStringPlatform(env, server);
 154         }
 155     }
 156     return cServer;
 157 }
 158 
 159 /*
 160  * Gets CUPS port name.
 161  *
 162  */
 163 JNIEXPORT jint JNICALL
 164 Java_sun_print_CUPSPrinter_getCupsPort(JNIEnv *env,
 165                                          jobject printObj)
 166 {
 167     int port = j2d_ippPort();
 168     return (jint) port;
 169 }
 170 
 171 
 172 /*
 173  * Checks if connection can be made to the server.
 174  *
 175  */
 176 JNIEXPORT jboolean JNICALL
 177 Java_sun_print_CUPSPrinter_canConnect(JNIEnv *env,
 178                                       jobject printObj,
 179                                       jstring server,
 180                                       jint port)
 181 {
 182     const char *serverName;
 183     serverName = (*env)->GetStringUTFChars(env, server, NULL);
 184     if (serverName != NULL) {
 185         http_t *http = j2d_httpConnect(serverName, (int)port);
 186         (*env)->ReleaseStringUTFChars(env, server, serverName);
 187         if (http != NULL) {
 188             j2d_httpClose(http);
 189             return JNI_TRUE;
 190         }
 191     }
 192     return JNI_FALSE;
 193 }
 194 
 195 
 196 /*
 197  * Returns list of media: pages + trays
 198  */
 199 JNIEXPORT jobjectArray JNICALL
 200 Java_sun_print_CUPSPrinter_getMedia(JNIEnv *env,
 201                                          jobject printObj,
 202                                          jstring printer)
 203 {
 204     ppd_file_t *ppd;
 205     ppd_option_t *optionTray, *optionPage;
 206     ppd_choice_t *choice;
 207     const char *name;
 208     const char *filename;
 209     int i, nTrays=0, nPages=0, nTotal=0;
 210     jstring utf_str;
 211     jclass cls;
 212     jobjectArray nameArray = NULL;
 213 
 214     name = (*env)->GetStringUTFChars(env, printer, NULL);
 215     if (name == NULL) {
 216         (*env)->ExceptionClear(env);
 217         JNU_ThrowOutOfMemoryError(env, "Could not create printer name");
 218         return NULL;
 219     }
 220 
 221     // NOTE: cupsGetPPD returns a pointer to a filename of a temporary file.
 222     // unlink() must be caled to remove the file when finished using it.
 223     filename = j2d_cupsGetPPD(name);
 224     (*env)->ReleaseStringUTFChars(env, printer, name);
 225     CHECK_NULL_RETURN(filename, NULL);
 226 
 227     cls = (*env)->FindClass(env, "java/lang/String");
 228     CHECK_NULL_RETURN(cls, NULL);
 229 
 230     if ((ppd = j2d_ppdOpenFile(filename)) == NULL) {
 231         unlink(filename);
 232         DPRINTF("CUPSfuncs::unable to open PPD  %s\n", filename);
 233         return NULL;
 234     }
 235 
 236     optionPage = j2d_ppdFindOption(ppd, "PageSize");
 237     if (optionPage != NULL) {
 238         nPages = optionPage->num_choices;
 239     }
 240 
 241     optionTray = j2d_ppdFindOption(ppd, "InputSlot");
 242     if (optionTray != NULL) {
 243         nTrays = optionTray->num_choices;
 244     }
 245 
 246     if ((nTotal = (nPages+nTrays) *2) > 0) {
 247         nameArray = (*env)->NewObjectArray(env, nTotal, cls, NULL);
 248         if (nameArray == NULL) {
 249             unlink(filename);
 250             j2d_ppdClose(ppd);
 251             DPRINTF("CUPSfuncs::bad alloc new array\n", "")
 252             (*env)->ExceptionClear(env);
 253             JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
 254             return NULL;
 255         }
 256 
 257         for (i = 0; optionPage!=NULL && i<nPages; i++) {
 258             choice = (optionPage->choices)+i;
 259             utf_str = JNU_NewStringPlatform(env, choice->text);
 260             if (utf_str == NULL) {
 261                 unlink(filename);
 262                 j2d_ppdClose(ppd);
 263                 DPRINTF("CUPSfuncs::bad alloc new string ->text\n", "")
 264                 JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
 265                 return NULL;
 266             }
 267             (*env)->SetObjectArrayElement(env, nameArray, i*2, utf_str);
 268             (*env)->DeleteLocalRef(env, utf_str);
 269             utf_str = JNU_NewStringPlatform(env, choice->choice);
 270             if (utf_str == NULL) {
 271                 unlink(filename);
 272                 j2d_ppdClose(ppd);
 273                 DPRINTF("CUPSfuncs::bad alloc new string ->choice\n", "")
 274                 JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
 275                 return NULL;
 276             }
 277             (*env)->SetObjectArrayElement(env, nameArray, i*2+1, utf_str);
 278             (*env)->DeleteLocalRef(env, utf_str);
 279         }
 280 
 281         for (i = 0; optionTray!=NULL && i<nTrays; i++) {
 282             choice = (optionTray->choices)+i;
 283             utf_str = JNU_NewStringPlatform(env, choice->text);
 284             if (utf_str == NULL) {
 285                 unlink(filename);
 286                 j2d_ppdClose(ppd);
 287                 DPRINTF("CUPSfuncs::bad alloc new string text\n", "")
 288                 JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
 289                 return NULL;
 290             }
 291             (*env)->SetObjectArrayElement(env, nameArray,
 292                                           (nPages+i)*2, utf_str);
 293             (*env)->DeleteLocalRef(env, utf_str);
 294             utf_str = JNU_NewStringPlatform(env, choice->choice);
 295             if (utf_str == NULL) {
 296                 unlink(filename);
 297                 j2d_ppdClose(ppd);
 298                 DPRINTF("CUPSfuncs::bad alloc new string choice\n", "")
 299                 JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
 300                 return NULL;
 301             }
 302             (*env)->SetObjectArrayElement(env, nameArray,
 303                                           (nPages+i)*2+1, utf_str);
 304             (*env)->DeleteLocalRef(env, utf_str);
 305         }
 306     }
 307     j2d_ppdClose(ppd);
 308     unlink(filename);
 309     return nameArray;
 310 }
 311 
 312 
 313 /*
 314  * Returns list of page sizes and imageable area.
 315  */
 316 JNIEXPORT jfloatArray JNICALL
 317 Java_sun_print_CUPSPrinter_getPageSizes(JNIEnv *env,
 318                                          jobject printObj,
 319                                          jstring printer)
 320 {
 321     ppd_file_t *ppd;
 322     ppd_option_t *option;
 323     ppd_choice_t *choice;
 324     ppd_size_t *size;
 325 
 326     const char *name = (*env)->GetStringUTFChars(env, printer, NULL);
 327     if (name == NULL) {
 328         (*env)->ExceptionClear(env);
 329         JNU_ThrowOutOfMemoryError(env, "Could not create printer name");
 330         return NULL;
 331     }
 332     const char *filename;
 333     int i;
 334     jobjectArray sizeArray = NULL;
 335     jfloat *dims;
 336 
 337     // NOTE: cupsGetPPD returns a pointer to a filename of a temporary file.
 338     // unlink() must be called to remove the file after using it.
 339     filename = j2d_cupsGetPPD(name);
 340     (*env)->ReleaseStringUTFChars(env, printer, name);
 341     CHECK_NULL_RETURN(filename, NULL);
 342     if ((ppd = j2d_ppdOpenFile(filename)) == NULL) {
 343         unlink(filename);
 344         DPRINTF("unable to open PPD  %s\n", filename)
 345         return NULL;
 346     }
 347     option = j2d_ppdFindOption(ppd, "PageSize");
 348     if (option != NULL && option->num_choices > 0) {
 349         // create array of dimensions - (num_choices * 6)
 350         //to cover length & height
 351         DPRINTF( "CUPSfuncs::option->num_choices %d\n", option->num_choices)
 352         // +1 is for storing the default media index
 353         sizeArray = (*env)->NewFloatArray(env, option->num_choices*6+1);
 354         if (sizeArray == NULL) {
 355             unlink(filename);
 356             j2d_ppdClose(ppd);
 357             DPRINTF("CUPSfuncs::bad alloc new float array\n", "")
 358             (*env)->ExceptionClear(env);
 359             JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
 360             return NULL;
 361         }
 362 
 363         dims = (*env)->GetFloatArrayElements(env, sizeArray, NULL);
 364         if (dims == NULL) {
 365             unlink(filename);
 366             j2d_ppdClose(ppd);
 367             (*env)->ExceptionClear(env);
 368             JNU_ThrowOutOfMemoryError(env, "Could not create printer name");
 369             return NULL;
 370         }
 371         for (i = 0; i<option->num_choices; i++) {
 372             choice = (option->choices)+i;
 373             // get the index of the default page
 374             if (!strcmp(choice->choice, option->defchoice)) {
 375                 dims[option->num_choices*6] = (float)i;
 376             }
 377             size = j2d_ppdPageSize(ppd, choice->choice);
 378             if (size != NULL) {
 379                 // paper width and height
 380                 dims[i*6] = size->width;
 381                 dims[(i*6)+1] = size->length;
 382                 // paper printable area
 383                 dims[(i*6)+2] = size->left;
 384                 dims[(i*6)+3] = size->top;
 385                 dims[(i*6)+4] = size->right;
 386                 dims[(i*6)+5] = size->bottom;
 387             }
 388         }
 389 
 390         (*env)->ReleaseFloatArrayElements(env, sizeArray, dims, 0);
 391     }
 392 
 393     j2d_ppdClose(ppd);
 394     unlink(filename);
 395     return sizeArray;
 396 }