/* * "$Id: phpcups.c,v 1.1.1.1 2003/04/11 21:07:50 jlovell Exp $" * * PHP module for the Common UNIX Printing System (CUPS). * * Copyright 1997-2003 by Easy Software Products. * * These coded instructions, statements, and computer programs are the * property of Easy Software Products and are protected by Federal * copyright law. Distribution and use rights are outlined in the file * "LICENSE.txt" which should have been included with this file. If this * file is missing or damaged please contact Easy Software Products * at: * * Attn: CUPS Licensing Information * Easy Software Products * 44141 Airport View Drive, Suite 204 * Hollywood, Maryland 20636-3111 USA * * Voice: (301) 373-9603 * EMail: cups-info@cups.org * WWW: http://www.cups.org * * Contents: * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "php_phpcups.h" /* * Include necessary headers... */ #include #include #include #include #include #include #include #include #include #include #if defined(WIN32) || defined(__EMX__) # include #else # include #endif /* WIN32 || __EMX__ */ /* * Local structures and types... */ typedef struct printer_attrs_str { char *name; /* Name of attribute */ char *value; /* Value of attribute */ } printer_attrs_t; /* * Local globals... */ #if 0 static int le_result, /* */ le_link, /* */ le_plink; /* */ #endif /* 0 */ static http_t *cups_server = NULL; /* Current server connection */ static ipp_status_t last_error = IPP_OK; /* Last IPP error */ static char authstring[HTTP_MAX_VALUE] = ""; /* Authorization string */ static char pwdstring[33] = ""; /* Last password string */ static int num_attrs = 0; /* Number of attributes */ static printer_attrs_t *printer_attrs = NULL; /* Attributes */ /* * Local functions... */ void _phpcups_free_attrs_list(void); int _phpcups_get_printer_status(char *name); /*static void free_attrs_list(void);*/ static char *cups_connect(const char *name, char *printer, char *hostname); static int cups_local_auth(http_t *http); /* * ********************************************************************* */ /* If you declare any globals in php_phpcups.h uncomment this: ZEND_DECLARE_MODULE_GLOBALS(phpcups) */ /* True global resources - no need for thread safety here */ /*static int le_phpcups;*/ /* * Every user visible function must have an entry in phpcups_functions[]. */ function_entry phpcups_functions[] = { PHP_FE(confirm_phpcups_compiled,NULL) PHP_FE(cups_get_dest_list, NULL) PHP_FE(cups_get_dest_options, NULL) PHP_FE(cups_get_jobs, NULL) PHP_FE(cups_cancel_job, NULL) PHP_FE(cups_last_error, NULL) PHP_FE(cups_print_file, NULL) PHP_FE(cups_get_printer_attributes, NULL) {NULL, NULL, NULL} }; zend_module_entry phpcups_module_entry = { STANDARD_MODULE_HEADER, "phpcups", phpcups_functions, PHP_MINIT(phpcups), PHP_MSHUTDOWN(phpcups), PHP_RINIT(phpcups), /* Replace with NULL if there's nothing to do at request start */ PHP_RSHUTDOWN(phpcups), /* Replace with NULL if there's nothing to do at request end */ PHP_MINFO(phpcups), "0.1", /* Replace with version number for your extension */ STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_PHPCUPS ZEND_GET_MODULE(phpcups) #endif /* Remove comments and fill if you need to have entries in php.ini PHP_INI_BEGIN() STD_PHP_INI_ENTRY("phpcups.value", "42", PHP_INI_ALL, OnUpdateInt, global_value, zend_phpcups_globals, phpcups_globals) STD_PHP_INI_ENTRY("phpcups.string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_phpcups_globals, phpcups_globals) PHP_INI_END() */ /* Uncomment this function if you have INI entries static void php_phpcups_init_globals(zend_phpcups_globals *phpcups_globals) { phpcups_globals->value = 0; phpcups_globals->string = NULL; } */ PHP_MINIT_FUNCTION(phpcups) { /* If you have INI entries, uncomment these lines ZEND_INIT_MODULE_GLOBALS(phpcups, php_phpcups_init_globals, NULL); REGISTER_INI_ENTRIES(); */ return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(phpcups) { /* uncomment this line if you have INI entries UNREGISTER_INI_ENTRIES(); */ return SUCCESS; } /* Remove if there's nothing to do at request start */ PHP_RINIT_FUNCTION(phpcups) { return SUCCESS; } /* Remove if there's nothing to do at request end */ PHP_RSHUTDOWN_FUNCTION(phpcups) { return SUCCESS; } PHP_MINFO_FUNCTION(phpcups) { php_info_print_table_start(); php_info_print_table_header(2, "phpcups support", "enabled"); php_info_print_table_end(); /* Remove comments if you have entries in php.ini DISPLAY_INI_ENTRIES(); */ } /* Remove the following function when you have succesfully modified config.m4 so that your module can be compiled into PHP, it exists only for testing purposes. */ /* Every user-visible function in PHP should document itself in the source */ PHP_FUNCTION(confirm_phpcups_compiled) { char *arg = NULL; int arg_len, len; char string[256]; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { return; } len = sprintf(string, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "phpcups", arg); RETURN_STRINGL(string, len, 1); } /* The previous line is meant for vim and emacs, so it can correctly fold and unfold functions in source code. See the corresponding marks just before function definition, where the functions purpose is also documented. Please follow this convention for the convenience of others editing your code. */ /* * Function: cups_get_dest_options * * Date: 8 Nov 2002 - TDB * * Parameters: name - String - Name of destination. * instance - String - Name of instance on destination. * * Returns: Array of option "objects", with each object * containing the members: * * name - String - Option name * value - String - Option value * * Comments: * */ PHP_FUNCTION(cups_get_dest_options) { /* char *arg = NULL; int arg_len, len;*/ zval *new_object; zval **d_server, **d_name, **d_instance; char c_server[256], c_name[256], c_instance[256]; char /*l_server[256],*/ l_name[256], l_instance[256]; cups_dest_t *dests, *dptr; int num_dests; int i, j; array_init(return_value); if ((ZEND_NUM_ARGS() != 3) || (zend_get_parameters_ex(3,&d_server,&d_name,&d_instance) != SUCCESS)) { WRONG_PARAM_COUNT; } convert_to_string_ex( d_server ); convert_to_string_ex( d_name ); convert_to_string_ex( d_instance ); /* * Find the dest/instance we want options for. */ bzero( c_server, 256 ); bzero( c_name, 256 ); bzero( c_instance, 256 ); if ( (char *)(*d_server)->value.str.val != NULL ) strcpy( c_server,(char *)(*d_server)->value.str.val ); if ( (char *)(*d_name)->value.str.val != NULL ) strcpy( c_name,(char *)(*d_name)->value.str.val ); if ( (char *)(*d_instance)->value.str.val != NULL ) strcpy( c_instance,(char *)(*d_instance)->value.str.val ); if (strlen(c_server)) cupsSetServer(c_server); num_dests = cupsGetDests(&dests); for (i=0, j = -1; (i < num_dests) && (j < 0); i++) { dptr = &dests[i]; if (dptr->name == NULL) strcpy( l_name, "" ); else strcpy( l_name, dptr->name ); if (dptr->instance == NULL) strcpy( l_instance, "" ); else strcpy( l_instance, dptr->instance ); if ((!strcmp( l_name, c_name )) && (!strcmp( l_instance, c_instance ))) { for (j=0; j < dptr->num_options; j++ ) { if ((dptr->options[j].name != NULL) && (dptr->options[j].value != NULL)) { MAKE_STD_ZVAL(new_object); if (object_init(new_object) == SUCCESS) { add_property_string(new_object,"name",dptr->options[j].name, 1 ); add_property_string(new_object,"value",dptr->options[j].value,1); add_next_index_zval( return_value, new_object ); } } } } } cupsFreeDests(num_dests,dests); } /* * Function: cups_get_dest_list * * Date: 8 Nov 2002 - TDB * * Parameters: cups server (optional) * * Returns: Array of destination "objects", with each object * containing the members: * * name - String - Name of destination. * instance - String - Name of instance on destination. * is_default - Long - 1 if default printer. * num_options - Long - Number of options for destination. * * Comments: * */ PHP_FUNCTION(cups_get_dest_list) { /* char *arg = NULL; int arg_len, len;*/ zval **z_server; zval *new_object; char c_server[256]; char string[2560]; /* char temp[256];*/ cups_dest_t *dests, *dptr; int num_dests; int i; /* * Initialize the return array. */ array_init(return_value); switch (ZEND_NUM_ARGS()) { /* * Change servers if passed. */ case 1: if (zend_get_parameters_ex(1,&z_server) != SUCCESS) { WRONG_PARAM_COUNT; } convert_to_string_ex( z_server ); if ( (char *)(*z_server)->value.str.val != NULL ) { strcpy( c_server,(char *)(*z_server)->value.str.val ); cupsSetServer( c_server ); } break; } /* * First get the destination list from the cups server. */ num_dests = cupsGetDests(&dests); /* * Loop through the list, create and fill in the objects, and * add them to the array. */ string[0] = '\0'; for (i=0; i < num_dests; i++) { dptr = &dests[i]; MAKE_STD_ZVAL(new_object); if (object_init(new_object) == SUCCESS) { if (strlen(c_server)) add_property_string( new_object, "server", c_server, 1 ); else add_property_string( new_object, "server", "", 1 ); if (dptr->name != NULL) add_property_string( new_object, "name", dptr->name, 1 ); else add_property_string( new_object, "name", "", 1 ); if (dptr->instance != NULL) add_property_string( new_object, "instance", dptr->instance, 1 ); else add_property_string( new_object, "instance", "", 1 ); add_property_long( new_object, "is_default", dptr->is_default ); add_property_long( new_object, "num_options", dptr->num_options ); add_next_index_zval( return_value, new_object ); } } /* * free the list ..... */ cupsFreeDests(num_dests,dests); } /* * Function: cups_get_jobs * * Date: 8 Nov 2002 - TDB * * Parameters: server - String - Name or IP of cups server. Blank * for localhost. * * name - String - Name of destination to query. * * user - String - Username to get job list for. * * Optional, default all users * * my_jobs - Long - Show only my jobs * * Optional, default FALSE * * completed - Long - Show completed jobs * * Optional, default FALSE * * Returns: Array of print job "objects", with each object * containing the members: * * id - Long - Job id * dest - String - Name of destination. * title - String - Title of document. * user - String - User who submitted job. * format - String - Document format (MIME type) * state - Long - Current state of the job. * size - Long - Size in bytes of the job. * priority - Long - Job priority. * completed_time - Long - Time job completed (UNIX time). * creation_time - Long - Time job created (UNIX time). * processing_time- Long - Processing time in seconds? * * Comments: * */ PHP_FUNCTION( cups_get_jobs ) { /* char *arg = NULL; int arg_len, len; */ zval *new_object; zval **z_server, **z_name, **z_user, **z_myjobs, **z_completed; char p_server[256], p_name[256], p_user[256]; int p_myjobs, p_completed; cups_job_t *jobs, *jptr; int num_jobs; int i; bzero( p_server, 256 ); bzero( p_name, 256 ); bzero( p_user, 256 ); p_myjobs = 0; p_completed = 0; /* * Initialize return value. */ array_init(return_value); /* * Parse params. */ switch(ZEND_NUM_ARGS()) { /* * server, destination only provided. */ case 2: if (zend_get_parameters_ex( 2, &z_server,&z_name ) == SUCCESS) { convert_to_string_ex( z_server); if ( (char *)(*z_server)->value.str.val != NULL ) strcpy( p_server,(char *)(*z_server)->value.str.val ); convert_to_string_ex( z_name ); if ( (char *)(*z_name)->value.str.val != NULL ) strcpy( p_name,(char *)(*z_name)->value.str.val ); } break; /* * server, destination and user */ case 3: if (zend_get_parameters_ex( 3, &z_server, &z_name, &z_user ) == SUCCESS) { convert_to_string_ex( z_name ); convert_to_string_ex( z_user ); convert_to_string_ex( z_server); if ( (char *)(*z_server)->value.str.val != NULL ) strcpy( p_server,(char *)(*z_server)->value.str.val ); if ( (char *)(*z_name)->value.str.val != NULL ) strcpy( p_name,(char *)(*z_name)->value.str.val ); if ( (char *)(*z_user)->value.str.val != NULL ) strcpy( p_user,(char *)(*z_user)->value.str.val ); } break; /* * server, destination, user, and myjobs */ case 4: if (zend_get_parameters_ex( 4, &z_server, &z_name, &z_user, &z_myjobs ) == SUCCESS) { convert_to_string_ex( z_name ); convert_to_string_ex( z_user ); convert_to_string_ex( z_server); if ( (char *)(*z_server)->value.str.val != NULL ) strcpy( p_server,(char *)(*z_server)->value.str.val ); if ( (char *)(*z_name)->value.str.val != NULL ) strcpy( p_name,(char *)(*z_name)->value.str.val ); if ( (char *)(*z_user)->value.str.val != NULL ) strcpy( p_user,(char *)(*z_user)->value.str.val ); convert_to_string_ex( z_myjobs ); p_myjobs = strtoul((char *)(*z_myjobs)->value.str.val,NULL,10); } break; /* * server, destination, user, myjobs, and completed */ case 5: if (zend_get_parameters_ex( 5, &z_server, &z_name, &z_user, &z_myjobs, &z_completed ) == SUCCESS) { convert_to_string_ex( z_name ); convert_to_string_ex( z_user ); convert_to_string_ex( z_server); if ( (char *)(*z_server)->value.str.val != NULL ) strcpy( p_server,(char *)(*z_server)->value.str.val ); if ( (char *)(*z_name)->value.str.val != NULL ) strcpy( p_name,(char *)(*z_name)->value.str.val ); if ( (char *)(*z_user)->value.str.val != NULL ) strcpy( p_user,(char *)(*z_user)->value.str.val ); convert_to_string_ex( z_myjobs ); p_myjobs = strtoul((char *)(*z_myjobs)->value.str.val,NULL,10); convert_to_string_ex( z_completed); p_completed = strtoul((char *)(*z_completed)->value.str.val,NULL,10); } break; } /* * Set the cups server if given. */ if (strlen(p_server)) cupsSetServer(p_server); /* * Set the cups user if given, otherwise get all jobs. */ if (strlen(p_user)) cupsSetUser(p_user); else cupsSetUser("root"); /* * Get the jobs list from the CUPS server. */ num_jobs = cupsGetJobs(&jobs,p_name,p_myjobs,p_completed); /* * Build the array of objects to return. */ for (i=0; i < num_jobs; i++) { jptr = &jobs[i]; MAKE_STD_ZVAL(new_object); if (object_init(new_object) == SUCCESS) { add_property_long(new_object,"id",jptr->id ); add_property_string(new_object,"dest",jptr->dest, 1 ); add_property_string(new_object,"title",jptr->title, 1 ); add_property_string(new_object,"user",jptr->user, 1 ); add_property_string(new_object,"format",jptr->format, 1 ); add_property_long(new_object,"state",jptr->state ); add_property_long(new_object,"size",jptr->size ); add_property_long(new_object,"priority",jptr->priority ); add_property_long(new_object,"completed_time",jptr->completed_time); add_property_long(new_object,"creation_time",jptr->creation_time); add_property_long(new_object,"processing_time",jptr->processing_time); add_next_index_zval( return_value, new_object ); } else { printf("\nError creating new object\n"); } } cupsFreeJobs(num_jobs,jobs); } /* * Function: cups_last_error * * Date: 8 Nov 2002 - TDB * * Parameters: none. * * Returns: error number - Long * * Comments: * */ PHP_FUNCTION(cups_last_error) { static char error[1024]; zval **z_server; char c_server[256]; bzero( c_server, 256 ); switch (ZEND_NUM_ARGS()) { /* * Change servers if passed. */ case 1: if (zend_get_parameters_ex(1,&z_server) != SUCCESS) { WRONG_PARAM_COUNT; } convert_to_string_ex( z_server ); if ( (char *)(*z_server)->value.str.val != NULL ) { strcpy( c_server,(char *)(*z_server)->value.str.val ); cupsSetServer( c_server ); } break; } sprintf( error,"%d", cupsLastError()); RETURN_STRINGL(error, strlen(error)+1, 1); } /* * Function: cups_cancel_job * * Date: 8 Nov 2002 - TDB * * Parameters: server - String - Name or IP of cups server. * name - String - Name of destination. * job - Long - Job ID to cancel. * * Returns: 1 on success, 0 on failure. * * Comments: * */ PHP_FUNCTION(cups_cancel_job) { zval **z_server, **z_name, **z_job; char p_server[256], p_name[256]; int p_job; int ret_val = -1; /* * Get parameters ..... */ if ((ZEND_NUM_ARGS() != 3) || (zend_get_parameters_ex( 3, &z_server, &z_name, &z_job ) != SUCCESS)) { WRONG_PARAM_COUNT; } convert_to_string_ex( z_server); if ( (char *)(*z_name)->value.str.val != NULL ) { strcpy( p_server,(char *)(*z_server)->value.str.val ); cupsSetServer(p_server); } convert_to_string_ex( z_name ); if ( (char *)(*z_name)->value.str.val != NULL ) strcpy( p_name,(char *)(*z_name)->value.str.val ); convert_to_string_ex( z_job ); p_job = strtoul((char *)(*z_job)->value.str.val,NULL,10); if (strlen(p_server)) cupsSetServer(p_server); cupsSetUser("root"); /* * Errrr .... Cancel the job ...... */ ret_val = cupsCancelJob(p_name,p_job); RETURN_LONG(ret_val); } /* * Local function. */ cups_option_t *_phpcups_parse_options( cups_option_t *options, int *num_options, char *param ) { char name[1024], value[1024]; sscanf(param,"%1023[^=]=%1023s", name, value ); if (strlen(name) && strlen(value)) { if (options == NULL) { options = (cups_option_t *)emalloc(sizeof(cups_option_t)); options->name = (char *)emalloc(strlen(name)+1); options->value = (char *)emalloc(strlen(value)+1); strcpy( options->name, name ); strcpy( options->value, value ); (*num_options)++; } else { options = (cups_option_t *)erealloc(options, (*num_options+1) * sizeof(cups_option_t)); options[*num_options].name = (char *)emalloc(strlen(name)+1); options[*num_options].value = (char *)emalloc(strlen(value)+1); strcpy( options[*num_options].name, name ); strcpy( options[*num_options].value, value ); (*num_options)++; } } return(options); } /* * Function: cups_print_file * * Date: 8 Nov 2002 - TDB * * Parameters: printer - String - Name of destination. * filename - String - Name of file to print (full path). * title - String - Title of document. * * Returns: Job ID - Long * * Comments: * */ PHP_FUNCTION(cups_print_file) { zval **z_server, **z_printer, **z_filename, **z_title, **z_options, **keydata; HashTable *param_ht; char p_server[256]; char p_printer[256]; char p_filename[256]; char p_title[256]; char temp[4096]; cups_option_t *options = NULL; int count, current; int ret_val = -1; int zend_num_args = ZEND_NUM_ARGS(); switch (zend_num_args) { /* * Server / Destination / filename only. */ case 3: if (zend_get_parameters_ex( 3, &z_server, &z_printer, &z_filename) != SUCCESS) { WRONG_PARAM_COUNT; } convert_to_string_ex( z_server); if ( (char *)(*z_server)->value.str.val != NULL ) { strcpy( p_server,(char *)(*z_server)->value.str.val ); cupsSetServer(p_server); } convert_to_string_ex( z_printer); if ( (char *)(*z_printer)->value.str.val != NULL ) strcpy( p_printer,(char *)(*z_printer)->value.str.val ); convert_to_string_ex( z_filename ); if ( (char *)(*z_filename)->value.str.val != NULL ) strcpy( p_filename,(char *)(*z_filename)->value.str.val ); strcpy( p_title,"untitled"); break; /* * Server, destination, filename and title. */ case 4: if (zend_get_parameters_ex( 4, &z_server, &z_printer, &z_filename, &z_title) != SUCCESS) { WRONG_PARAM_COUNT; } convert_to_string_ex( z_server); if ( (char *)(*z_server)->value.str.val != NULL ) { strcpy( p_server,(char *)(*z_server)->value.str.val ); cupsSetServer(p_server); } convert_to_string_ex( z_printer); if ( (char *)(*z_printer)->value.str.val != NULL ) strcpy( p_printer,(char *)(*z_printer)->value.str.val ); convert_to_string_ex( z_filename ); if ( (char *)(*z_filename)->value.str.val != NULL ) strcpy( p_filename,(char *)(*z_filename)->value.str.val ); convert_to_string_ex( z_title ); if ( (char *)(*z_title)->value.str.val != NULL ) strcpy( p_title,(char *)(*z_title)->value.str.val ); break; /* * Server, destination, filename and title plus printer options. */ case 5: if (zend_get_parameters_ex( 5, &z_server, &z_printer, &z_filename, &z_title, &z_options) != SUCCESS) { WRONG_PARAM_COUNT; } convert_to_string_ex( z_server); if ( (char *)(*z_server)->value.str.val != NULL ) { strcpy( p_server,(char *)(*z_server)->value.str.val ); cupsSetServer(p_server); } convert_to_string_ex( z_printer); if ( (char *)(*z_printer)->value.str.val != NULL ) strcpy( p_printer,(char *)(*z_printer)->value.str.val ); convert_to_string_ex( z_filename ); if ( (char *)(*z_filename)->value.str.val != NULL ) strcpy( p_filename,(char *)(*z_filename)->value.str.val ); convert_to_string_ex( z_title ); if ( (char *)(*z_title)->value.str.val != NULL ) strcpy( p_title,(char *)(*z_title)->value.str.val ); /* * Convert options array. */ convert_to_array_ex( z_options ); param_ht = Z_ARRVAL_PP( z_options ); count = zend_hash_num_elements( param_ht ); options = emalloc(sizeof(zval **) * count); current = 0; zend_hash_internal_pointer_reset(param_ht); for (current=0; current < count; current++) { zend_hash_get_current_data(param_ht,(void **)&keydata); switch(Z_TYPE_PP(keydata)) { case IS_STRING: convert_to_string_ex(keydata); strcpy(temp,(char *)(*keydata)->value.str.val ); options = _phpcups_parse_options( options, ¤t, temp ); break; } zend_hash_move_forward(param_ht); } break; default: WRONG_PARAM_COUNT; } if (current > 0) { ret_val = cupsPrintFile( p_printer,p_filename,p_title,current,options); for (current=0; current < count; current++) { efree( options[current].name ); efree( options[current].value ); } efree(options); } else ret_val = cupsPrintFile( p_printer,p_filename,p_title,0,NULL ); RETURN_LONG(ret_val); } /* * Function: cups_get_printer_attributes * * Date: 8 Nov 2002 - TDB * * Parameters: name - String - Name of destination. * * Returns: state - String * * Comments: * */ PHP_FUNCTION(cups_get_printer_attributes) { zval **z_server, **z_name; zval *new_object; char p_server[256], p_name[256]; int i, count; /* * Initialize return value. */ array_init(return_value); /* * Get parameters ..... */ if ((ZEND_NUM_ARGS() != 2) || (zend_get_parameters_ex( 2, &z_server, &z_name ) != SUCCESS)) { WRONG_PARAM_COUNT; } convert_to_string_ex( z_server); if ( (char *)(*z_server)->value.str.val != NULL ) { strcpy( p_server,(char *)(*z_server)->value.str.val ); cupsSetServer(p_server); } convert_to_string_ex( z_name ); if ( (char *)(*z_name)->value.str.val != NULL ) strcpy( p_name,(char *)(*z_name)->value.str.val ); /* * Errrr .... Cancel the job ...... */ printer_attrs = NULL; count = _phpcups_get_printer_status( p_name ); /* * Create an array of objects containing name / value pairs. */ for (i=0; i < count; i++) { if ((printer_attrs[i].name != NULL) && (printer_attrs[i].value != NULL)) { MAKE_STD_ZVAL(new_object); if (object_init(new_object) == SUCCESS) { add_property_string(new_object,"name",printer_attrs[i].name, 1 ); add_property_string(new_object,"value",printer_attrs[i].value, 1 ); add_next_index_zval( return_value, new_object ); } } } _phpcups_free_attrs_list(); } /* * Local function - free memory. */ void _phpcups_free_attrs_list(void) { int i; if (num_attrs < 1) return; for ( i=0; i < num_attrs; i++ ) { if (printer_attrs[i].name != NULL) efree(printer_attrs[i].name); if (printer_attrs[i].value != NULL) efree(printer_attrs[i].value ); } efree(printer_attrs); num_attrs = 0; } /* * Local function - add to attributes list. */ int _phpcups_update_attrs_list( char *name, char *value ) { if (num_attrs < 1) { printer_attrs = (printer_attrs_t *)emalloc(sizeof(printer_attrs_t)); printer_attrs->name = (char *)emalloc(strlen(name)+1); printer_attrs->value = (char *)emalloc(strlen(value)+1); strcpy( printer_attrs->name, name ); strcpy( printer_attrs->value, value ); num_attrs = 1; } else { printer_attrs = (printer_attrs_t *)erealloc(printer_attrs, (num_attrs + 1) * sizeof(printer_attrs_t)); printer_attrs[num_attrs].name = (char *)emalloc(strlen(name)+1); printer_attrs[num_attrs].value = (char *)emalloc(strlen(value)+1); strcpy( printer_attrs[num_attrs].name, name ); strcpy( printer_attrs[num_attrs].value, value ); num_attrs++; } return(num_attrs); } /* * Local function - get printer attributes. */ int _phpcups_get_printer_status(char *name ) { ipp_t *request, /* IPP Request */ *response; /* IPP Response */ ipp_attribute_t *attr; /* Current attribute */ cups_lang_t *language; /* Default language */ char printer_uri[1024]; char temp[1024]; /* static char *req_attrs[] = {"printer-state", "printer-state-reason" };*/ int i; if (name == NULL) { last_error = IPP_INTERNAL_ERROR; return (0); } /* * Try to connect to the server... */ if (!cups_connect(name, NULL, NULL)) { last_error = IPP_SERVICE_UNAVAILABLE; return (0); } /* * Build a CUPS_GET_PRINTERS request, which requires the following * attributes: * * attributes-charset * attributes-natural-language * requested-attributes */ request = ippNew(); request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES; request->request.op.request_id = 1; language = cupsLangDefault(); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(language)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, language->language); sprintf(printer_uri, "ipp://localhost/printers/%-s", name ); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri ); /* * Do the request and get back a response... */ num_attrs = 0; printer_attrs = NULL; if ((response = cupsDoRequest(cups_server, request, "/")) != NULL) { last_error = response->request.status.status_code; for (attr = response->attrs; attr != NULL; attr = attr->next) { if (attr->num_values < 1) continue; if (attr->name != NULL && strcasecmp(attr->name, "printer-state") == 0 && attr->value_tag == IPP_TAG_ENUM) { strcpy( temp, "unknown" ); switch( attr->values[0].integer ) { case 3: strcpy( temp, "idle" ); break; case 4: strcpy( temp, "processing" ); break; case 5: strcpy( temp, "stopped" ); break; default: continue; } _phpcups_update_attrs_list( attr->name, temp ); } else if (attr->name != NULL && (attr->value_tag == IPP_TAG_TEXT || attr->value_tag == IPP_TAG_URI || attr->value_tag == IPP_TAG_STRING)) { for (i=0; i < attr->num_values; i++) _phpcups_update_attrs_list(attr->name, attr->values[i].string.text ); } else if (attr->name != NULL && (attr->value_tag == IPP_TAG_ENUM || attr->value_tag == IPP_TAG_BOOLEAN || attr->value_tag == IPP_TAG_INTEGER)) { for (i=0; i < attr->num_values; i++) { sprintf(temp,"%-d", attr->values[i].integer ); _phpcups_update_attrs_list(attr->name, temp ); } } else if (attr->name != NULL && attr->value_tag == IPP_TAG_RESOLUTION) { for (i=0; i < attr->num_values; i++) { sprintf(temp,"X:%-d Y:%-d U:%-d", attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units ); _phpcups_update_attrs_list(attr->name, temp ); } } else if (attr->name != NULL && attr->value_tag == IPP_TAG_RANGE) { for (i=0; i < attr->num_values; i++) { sprintf(temp,"%d-%d", attr->values[i].range.lower, attr->values[i].range.upper ); _phpcups_update_attrs_list(attr->name, temp ); } } } ippDelete(response); } else { last_error = IPP_BAD_REQUEST; return(0); } return (num_attrs); } /* * Functions from the CUPS distribution - util.c * * * * Contents: * * cupsCancelJob() - Cancel a print job. * cupsDoFileRequest() - Do an IPP request... * cupsFreeJobs() - Free memory used by job data. * cupsGetClasses() - Get a list of printer classes. * cupsGetDefault() - Get the default printer or class. * cupsGetJobs() - Get the jobs from the server. * cupsGetPPD() - Get the PPD file for a printer. * cupsGetPrinters() - Get a list of printers. * cupsLastError() - Return the last IPP error that occurred. * cupsPrintFile() - Print a file to a printer or class. * cupsPrintFiles() - Print one or more files to a printer or class. * cups_connect() - Connect to the specified host... * cups_local_auth() - Get the local authorization certificate if * available/applicable... */ /* * 'cupsCancelJob()' - Cancel a print job. */ int /* O - 1 on success, 0 on failure */ cupsCancelJob(const char *name, /* I - Name of printer or class */ int job) /* I - Job ID */ { char printer[HTTP_MAX_URI], /* Printer name */ hostname[HTTP_MAX_URI], /* Hostname */ uri[HTTP_MAX_URI]; /* Printer URI */ ipp_t *request, /* IPP request */ *response; /* IPP response */ cups_lang_t *language; /* Language info */ /* * See if we can connect to the server... */ if (!cups_connect(name, printer, hostname)) { last_error = IPP_SERVICE_UNAVAILABLE; return (0); } /* * Build an IPP_CANCEL_JOB request, which requires the following * attributes: * * attributes-charset * attributes-natural-language * printer-uri * job-id * [requesting-user-name] */ request = ippNew(); request->request.op.operation_id = IPP_CANCEL_JOB; request->request.op.request_id = 1; language = cupsLangDefault(); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(language)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, language != NULL ? language->language : "C"); snprintf(uri, sizeof(uri), "ipp://%s:%d/printers/%s", hostname, ippPort(), printer); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); /* * Do the request... */ if ((response = cupsDoRequest(cups_server, request, "/jobs/")) == NULL) { last_error = IPP_BAD_REQUEST; return (0); } else { last_error = response->request.status.status_code; ippDelete(response); return (1); } } /* * 'cupsDoFileRequest()' - Do an IPP request... */ ipp_t * /* O - Response data */ cupsDoFileRequest(http_t *http, /* I - HTTP connection to server */ ipp_t *request, /* I - IPP request */ const char *resource, /* I - HTTP resource for POST */ const char *filename) /* I - File to send or NULL */ { ipp_t *response; /* IPP response data */ char length[255]; /* Content-Length field */ http_status_t status; /* Status of HTTP request */ FILE *file; /* File to send */ struct stat fileinfo; /* File information */ int bytes; /* Number of bytes read/written */ char buffer[32768]; /* Output buffer */ const char *password; /* Password string */ char realm[HTTP_MAX_VALUE], /* realm="xyz" string */ nonce[HTTP_MAX_VALUE], /* nonce="xyz" string */ plain[255], /* Plaintext username:password */ encode[512]; /* Encoded username:password */ char prompt[1024]; /* Prompt string */ int digest_tries; /* Number of tries with Digest */ if (http == NULL || request == NULL || resource == NULL) { if (request != NULL) ippDelete(request); last_error = IPP_INTERNAL_ERROR; return (NULL); } DEBUG_printf(("cupsDoFileRequest(%p, %08x, \'%s\', \'%s\')\n", http, request, resource, filename ? filename : "(null)")); /* * See if we have a file to send... */ if (filename != NULL) { if (stat(filename, &fileinfo)) { /* * Can't get file information! */ ippDelete(request); last_error = IPP_NOT_FOUND; return (NULL); } if ((file = fopen(filename, "rb")) == NULL) { /* * Can't open file! */ ippDelete(request); last_error = IPP_NOT_FOUND; return (NULL); } } else file = NULL; /* * Loop until we can send the request without authorization problems. */ response = NULL; status = HTTP_ERROR; digest_tries = 0; while (response == NULL) { DEBUG_puts("cupsDoFileRequest: setup..."); /* * Setup the HTTP variables needed... */ if (filename != NULL) sprintf(length, "%lu", (unsigned long)(ippLength(request) + (size_t)fileinfo.st_size)); else sprintf(length, "%lu", (unsigned long)ippLength(request)); httpClearFields(http); httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, length); httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp"); httpSetField(http, HTTP_FIELD_AUTHORIZATION, authstring); /* * Try the request... */ DEBUG_puts("cupsDoFileRequest: post..."); if (httpPost(http, resource)) { if (httpReconnect(http)) { status = HTTP_ERROR; break; } else continue; } /* * Send the IPP data and wait for the response... */ DEBUG_puts("cupsDoFileRequest: ipp write..."); request->state = IPP_IDLE; if (ippWrite(http, request) != IPP_ERROR) if (filename != NULL) { DEBUG_puts("cupsDoFileRequest: file write..."); /* * Send the file... */ rewind(file); while ((bytes = fread(buffer, 1, sizeof(buffer), file)) > 0) if (httpWrite(http, buffer, bytes) < bytes) break; } /* * Get the server's return status... */ DEBUG_puts("cupsDoFileRequest: update..."); while ((status = httpUpdate(http)) == HTTP_CONTINUE); if (status == HTTP_UNAUTHORIZED) { DEBUG_puts("cupsDoFileRequest: unauthorized..."); /* * Flush any error message... */ httpFlush(http); /* * See if we can do local authentication... */ if (cups_local_auth(http)) continue; /* * See if we should retry the current digest password... */ if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Basic", 5) == 0 || digest_tries > 1 || !pwdstring[0]) { /* * Nope - get a password from the user... */ snprintf(prompt, sizeof(prompt), "Password for %s on %s? ", cupsUser(), http->hostname); if ((password = cupsGetPassword(prompt)) == NULL) break; if (!password[0]) break; strncpy(pwdstring, password, sizeof(pwdstring) - 1); pwdstring[sizeof(pwdstring) - 1] = '\0'; digest_tries = 0; } else digest_tries ++; /* * Got a password; encode it for the server... */ if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Basic", 5) == 0) { /* * Basic authentication... */ snprintf(plain, sizeof(plain), "%s:%s", cupsUser(), pwdstring); httpEncode64(encode, plain); snprintf(authstring, sizeof(authstring), "Basic %s", encode); } else { /* * Digest authentication... */ httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "realm", realm); httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "nonce", nonce); httpMD5(cupsUser(), realm, pwdstring, encode); httpMD5Final(nonce, "POST", resource, encode); snprintf(authstring, sizeof(authstring), "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", " "response=\"%s\"", cupsUser(), realm, nonce, encode); } continue; } else if (status == HTTP_ERROR) { #ifdef WIN32 if (http->error != WSAENETDOWN && http->error != WSAENETUNREACH) #else if (http->error != ENETDOWN && http->error != ENETUNREACH) #endif /* WIN32 */ continue; else break; } #ifdef HAVE_LIBSSL else if (status == HTTP_UPGRADE_REQUIRED) { /* * Flush any error message... */ httpFlush(http); /* * Upgrade with encryption... */ httpEncryption(http, HTTP_ENCRYPT_REQUIRED); /* * Try again, this time with encryption enabled... */ continue; } #endif /* HAVE_LIBSSL */ else if (status != HTTP_OK) { DEBUG_printf(("cupsDoFileRequest: error %d...\n", status)); /* * Flush any error message... */ httpFlush(http); break; } else { /* * Read the response... */ DEBUG_puts("cupsDoFileRequest: response..."); response = ippNew(); if (ippRead(http, response) == IPP_ERROR) { /* * Delete the response... */ ippDelete(response); response = NULL; last_error = IPP_SERVICE_UNAVAILABLE; break; } } } /* * Close the file if needed... */ if (filename != NULL) fclose(file); /* * Flush any remaining data... */ httpFlush(http); /* * Delete the original request and return the response... */ ippDelete(request); if (response) last_error = response->request.status.status_code; else if (status == HTTP_NOT_FOUND) last_error = IPP_NOT_FOUND; else if (status == HTTP_UNAUTHORIZED) last_error = IPP_NOT_AUTHORIZED; else if (status != HTTP_OK) last_error = IPP_SERVICE_UNAVAILABLE; return (response); } /* * 'cupsFreeJobs()' - Free memory used by job data. */ void cupsFreeJobs(int num_jobs,/* I - Number of jobs */ cups_job_t *jobs) /* I - Jobs */ { int i; /* Looping var */ if (num_jobs <= 0 || jobs == NULL) return; for (i = 0; i < num_jobs; i ++) { efree(jobs[i].dest); efree(jobs[i].user); efree(jobs[i].format); efree(jobs[i].title); } efree(jobs); } /* * 'cupsGetClasses()' - Get a list of printer classes. */ int /* O - Number of classes */ cupsGetClasses(char ***classes) /* O - Classes */ { int n; /* Number of classes */ ipp_t *request, /* IPP Request */ *response; /* IPP Response */ ipp_attribute_t *attr; /* Current attribute */ cups_lang_t *language; /* Default language */ char **temp; /* Temporary pointer */ if (classes == NULL) { last_error = IPP_INTERNAL_ERROR; return (0); } /* * Try to connect to the server... */ if (!cups_connect("default", NULL, NULL)) { last_error = IPP_SERVICE_UNAVAILABLE; return (0); } /* * Build a CUPS_GET_CLASSES request, which requires the following * attributes: * * attributes-charset * attributes-natural-language * requested-attributes */ request = ippNew(); request->request.op.operation_id = CUPS_GET_CLASSES; request->request.op.request_id = 1; language = cupsLangDefault(); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(language)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, language->language); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "printer-name"); /* * Do the request and get back a response... */ n = 0; *classes = NULL; if ((response = cupsDoRequest(cups_server, request, "/")) != NULL) { last_error = response->request.status.status_code; for (attr = response->attrs; attr != NULL; attr = attr->next) if (attr->name != NULL && strcasecmp(attr->name, "printer-name") == 0 && attr->value_tag == IPP_TAG_NAME) { if (n == 0) temp = emalloc(sizeof(char *)); else temp = erealloc(*classes, sizeof(char *) * (n + 1)); if (temp == NULL) { /* * Ran out of memory! */ while (n > 0) { n --; efree((*classes)[n]); } efree(*classes); ippDelete(response); return (0); } *classes = temp; temp[n] = estrdup(attr->values[0].string.text); n ++; } ippDelete(response); } else last_error = IPP_BAD_REQUEST; return (n); } /* * 'cupsGetDefault()' - Get the default printer or class. */ const char * /* O - Default printer or NULL */ cupsGetDefault(void) { ipp_t *request, /* IPP Request */ *response; /* IPP Response */ ipp_attribute_t *attr; /* Current attribute */ cups_lang_t *language; /* Default language */ const char *var; /* Environment variable */ static char def_printer[256];/* Default printer */ /* * First see if the LPDEST or PRINTER environment variables are * set... However, if PRINTER is set to "lp", ignore it to work * around a "feature" in most Linux distributions - the default * user login scripts set PRINTER to "lp"... */ if ((var = getenv("LPDEST")) != NULL) return (var); else if ((var = getenv("PRINTER")) != NULL && strcmp(var, "lp") != 0) return (var); /* * Try to connect to the server... */ if (!cups_connect("default", NULL, NULL)) { last_error = IPP_SERVICE_UNAVAILABLE; return (NULL); } /* * Build a CUPS_GET_DEFAULT request, which requires the following * attributes: * * attributes-charset * attributes-natural-language */ request = ippNew(); request->request.op.operation_id = CUPS_GET_DEFAULT; request->request.op.request_id = 1; language = cupsLangDefault(); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(language)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, language->language); /* * Do the request and get back a response... */ if ((response = cupsDoRequest(cups_server, request, "/")) != NULL) { last_error = response->request.status.status_code; if ((attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME)) != NULL) { strncpy(def_printer, attr->values[0].string.text, sizeof(def_printer) - 1); def_printer[sizeof(def_printer) - 1] = '\0'; ippDelete(response); return (def_printer); } ippDelete(response); } else last_error = IPP_BAD_REQUEST; return (NULL); } /* * 'cupsGetJobs()' - Get the jobs from the server. */ int /* O - Number of jobs */ cupsGetJobs(cups_job_t **jobs, /* O - Job data */ const char *mydest, /* I - Only show jobs for dest? */ int myjobs, /* I - Only show my jobs? */ int completed) /* I - Only show completed jobs? */ { int n; /* Number of jobs */ ipp_t *request, /* IPP Request */ *response; /* IPP Response */ ipp_attribute_t *attr; /* Current attribute */ cups_lang_t *language; /* Default language */ cups_job_t *temp; /* Temporary pointer */ int id, /* job-id */ priority, /* job-priority */ size; /* job-k-octets */ ipp_jstate_t state; /* job-state */ time_t completed_time, /* time-at-completed */ creation_time, /* time-at-creation */ processing_time; /* time-at-processing */ const char *dest, /* job-printer-uri */ *format, /* document-format */ *title, /* job-name */ *user; /* job-originating-user-name */ char uri[HTTP_MAX_URI]; /* URI for jobs */ static const char *attrs[] = /* Requested attributes */ { "job-id", "job-priority", "job-k-octets", "job-state", "time-at-completed", "time-at-creation", "time-at-processing", "job-printer-uri", "document-format", "job-name", "job-originating-user-name" }; if (jobs == NULL) { last_error = IPP_INTERNAL_ERROR; return (0); } /* * Try to connect to the server... */ if (!cups_connect("default", NULL, NULL)) { last_error = IPP_SERVICE_UNAVAILABLE; return (0); } /* * Build an IPP_GET_JOBS request, which requires the following * attributes: * * attributes-charset * attributes-natural-language * printer-uri * requesting-user-name * which-jobs * my-jobs * requested-attributes */ request = ippNew(); request->request.op.operation_id = IPP_GET_JOBS; request->request.op.request_id = 1; language = cupsLangDefault(); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(language)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, language->language); if (mydest) snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", mydest); else strcpy(uri, "ipp://localhost/jobs"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); if (myjobs) ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1); if (completed) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs", NULL, "completed"); ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", sizeof(attrs) / sizeof(attrs[0]), NULL, attrs); /* * Do the request and get back a response... */ n = 0; *jobs = NULL; if ((response = cupsDoRequest(cups_server, request, "/")) != NULL) { last_error = response->request.status.status_code; for (attr = response->attrs; attr != NULL; attr = attr->next) { /* * Skip leading attributes until we hit a job... */ while (attr != NULL && attr->group_tag != IPP_TAG_JOB) attr = attr->next; if (attr == NULL) break; /* * Pull the needed attributes from this job... */ id = 0; size = 0; priority = 50; state = IPP_JOB_PENDING; user = NULL; dest = NULL; format = NULL; title = NULL; creation_time = 0; completed_time = 0; processing_time = 0; while (attr != NULL && attr->group_tag == IPP_TAG_JOB) { if (strcmp(attr->name, "job-id") == 0 && attr->value_tag == IPP_TAG_INTEGER) id = attr->values[0].integer; else if (strcmp(attr->name, "job-state") == 0 && attr->value_tag == IPP_TAG_ENUM) state = (ipp_jstate_t)attr->values[0].integer; else if (strcmp(attr->name, "job-priority") == 0 && attr->value_tag == IPP_TAG_INTEGER) priority = attr->values[0].integer; else if (strcmp(attr->name, "job-k-octets") == 0 && attr->value_tag == IPP_TAG_INTEGER) size = attr->values[0].integer; else if (strcmp(attr->name, "time-at-completed") == 0 && attr->value_tag == IPP_TAG_INTEGER) completed_time = attr->values[0].integer; else if (strcmp(attr->name, "time-at-creation") == 0 && attr->value_tag == IPP_TAG_INTEGER) creation_time = attr->values[0].integer; else if (strcmp(attr->name, "time-at-processing") == 0 && attr->value_tag == IPP_TAG_INTEGER) processing_time = attr->values[0].integer; else if (strcmp(attr->name, "job-printer-uri") == 0 && attr->value_tag == IPP_TAG_URI) { if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL) dest ++; } else if (strcmp(attr->name, "job-originating-user-name") == 0 && attr->value_tag == IPP_TAG_NAME) user = attr->values[0].string.text; else if (strcmp(attr->name, "document-format") == 0 && attr->value_tag == IPP_TAG_MIMETYPE) format = attr->values[0].string.text; else if (strcmp(attr->name, "job-name") == 0 && (attr->value_tag == IPP_TAG_TEXT || attr->value_tag == IPP_TAG_NAME)) title = attr->values[0].string.text; attr = attr->next; } /* * See if we have everything needed... */ if (dest == NULL || title == NULL || user == NULL || id == 0) { if (attr == NULL) break; else continue; } if (format == NULL) format = "application/octet-stream"; /* * Allocate memory for the job... */ if (n == 0) temp = emalloc(sizeof(cups_job_t)); else temp = erealloc(*jobs, sizeof(cups_job_t) * (n + 1)); if (temp == NULL) { /* * Ran out of memory! */ cupsFreeJobs(n, *jobs); *jobs = NULL; ippDelete(response); return (0); } *jobs = temp; temp += n; n ++; /* * Copy the data over... */ temp->dest = estrdup(dest); temp->user = estrdup(user); temp->format = estrdup(format); temp->title = estrdup(title); temp->id = id; temp->priority = priority; temp->state = state; temp->size = size; temp->completed_time = completed_time; temp->creation_time = creation_time; temp->processing_time = processing_time; if (attr == NULL) break; } ippDelete(response); } else last_error = IPP_BAD_REQUEST; return (n); } /* * 'cupsGetPPD()' - Get the PPD file for a printer. */ const char * /* O - Filename for PPD file */ cupsGetPPD(const char *name) /* I - Printer name */ { int i; /* Looping var */ ipp_t *request, /* IPP request */ *response; /* IPP response */ ipp_attribute_t *attr; /* Current attribute */ cups_lang_t *language; /* Local language */ int fd; /* PPD file */ int bytes; /* Number of bytes read */ char buffer[8192]; /* Buffer for file */ char printer[HTTP_MAX_URI], /* Printer name */ method[HTTP_MAX_URI], /* Method/scheme name */ username[HTTP_MAX_URI], /* Username:password */ hostname[HTTP_MAX_URI], /* Hostname */ resource[HTTP_MAX_URI]; /* Resource name */ int port; /* Port number */ const char *password; /* Password string */ char realm[HTTP_MAX_VALUE], /* realm="xyz" string */ nonce[HTTP_MAX_VALUE], /* nonce="xyz" string */ plain[255], /* Plaintext username:password */ encode[512]; /* Encoded username:password */ http_status_t status; /* HTTP status from server */ char prompt[1024]; /* Prompt string */ int digest_tries; /* Number of tries with Digest */ static char filename[HTTP_MAX_URI]; /* Local filename */ static const char *requested_attrs[] =/* Requested attributes */ { "printer-uri-supported", "printer-type", "member-uris" }; if (name == NULL) { last_error = IPP_INTERNAL_ERROR; return (NULL); } /* * See if we can connect to the server... */ if (!cups_connect(name, printer, hostname)) { last_error = IPP_SERVICE_UNAVAILABLE; return (NULL); } /* * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following * attributes: * * attributes-charset * attributes-natural-language * printer-uri * requested-attributes */ request = ippNew(); request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES; request->request.op.request_id = 1; language = cupsLangDefault(); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(language)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, language->language); snprintf(buffer, sizeof(buffer), "ipp://localhost/printers/%s", printer); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, buffer); ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requested-attributes", sizeof(requested_attrs) / sizeof(requested_attrs[0]), NULL, requested_attrs); /* * Do the request and get back a response... */ if ((response = cupsDoRequest(cups_server, request, "/")) != NULL) { last_error = response->request.status.status_code; printer[0] = '\0'; hostname[0] = '\0'; if ((attr = ippFindAttribute(response, "member-uris", IPP_TAG_URI)) != NULL) { /* * Get the first actual server and printer name in the class... */ for (i = 0; i < attr->num_values; i ++) { httpSeparate(attr->values[0].string.text, method, username, hostname, &port, resource); if (strncmp(resource, "/printers/", 10) == 0) { /* * Found a printer! */ strncpy(printer, resource + 10, sizeof(printer) - 1); printer[sizeof(printer) - 1] = '\0'; break; } } } else if ((attr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL) { /* * Get the actual server and printer names... */ httpSeparate(attr->values[0].string.text, method, username, hostname, &port, resource); strncpy(printer, strrchr(resource, '/') + 1, sizeof(printer) - 1); printer[sizeof(printer) - 1] = '\0'; } ippDelete(response); /* * Remap local hostname to localhost... */ gethostname(buffer, sizeof(buffer)); if (strcasecmp(buffer, hostname) == 0) strcpy(hostname, "localhost"); } cupsLangFree(language); if (!printer[0]) return (NULL); /* * Reconnect to the correct server as needed... */ if (strcasecmp(cups_server->hostname, hostname) != 0) { httpClose(cups_server); if ((cups_server = httpConnectEncrypt(hostname, ippPort(), cupsEncryption())) == NULL) { last_error = IPP_SERVICE_UNAVAILABLE; return (NULL); } } /* * Get a temp file... */ if ((fd = cupsTempFd(filename, sizeof(filename))) < 0) { /* * Can't open file; close the server connection and return NULL... */ httpFlush(cups_server); httpClose(cups_server); cups_server = NULL; return (NULL); } /* * And send a request to the HTTP server... */ snprintf(resource, sizeof(resource), "/printers/%s.ppd", printer); digest_tries = 0; do { httpClearFields(cups_server); httpSetField(cups_server, HTTP_FIELD_HOST, hostname); httpSetField(cups_server, HTTP_FIELD_AUTHORIZATION, authstring); if (httpGet(cups_server, resource)) { if (httpReconnect(cups_server)) { status = HTTP_ERROR; break; } else { status = HTTP_UNAUTHORIZED; continue; } } while ((status = httpUpdate(cups_server)) == HTTP_CONTINUE); if (status == HTTP_UNAUTHORIZED) { DEBUG_puts("cupsGetPPD: unauthorized..."); /* * Flush any error message... */ httpFlush(cups_server); /* * See if we can do local authentication... */ if (cups_local_auth(cups_server)) continue; /* * See if we should retry the current digest password... */ if (strncmp(cups_server->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Basic", 5) == 0 || digest_tries > 1 || !pwdstring[0]) { /* * Nope - get a password from the user... */ snprintf(prompt, sizeof(prompt), "Password for %s on %s? ", cupsUser(), cups_server->hostname); if ((password = cupsGetPassword(prompt)) == NULL) break; if (!password[0]) break; strncpy(pwdstring, password, sizeof(pwdstring) - 1); pwdstring[sizeof(pwdstring) - 1] = '\0'; digest_tries = 0; } else digest_tries ++; /* * Got a password; encode it for the server... */ if (strncmp(cups_server->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Basic", 5) == 0) { /* * Basic authentication... */ snprintf(plain, sizeof(plain), "%s:%s", cupsUser(), pwdstring); httpEncode64(encode, plain); snprintf(authstring, sizeof(authstring), "Basic %s", encode); } else { /* * Digest authentication... */ httpGetSubField(cups_server, HTTP_FIELD_WWW_AUTHENTICATE, "realm", realm); httpGetSubField(cups_server, HTTP_FIELD_WWW_AUTHENTICATE, "nonce", nonce); httpMD5(cupsUser(), realm, pwdstring, encode); httpMD5Final(nonce, "GET", resource, encode); snprintf(authstring, sizeof(authstring), "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", " "response=\"%s\"", cupsUser(), realm, nonce, encode); } continue; } #ifdef HAVE_LIBSSL else if (status == HTTP_UPGRADE_REQUIRED) { /* * Flush any error message... */ httpFlush(cups_server); /* * Upgrade with encryption... */ httpEncryption(cups_server, HTTP_ENCRYPT_REQUIRED); /* * Try again, this time with encryption enabled... */ continue; } #endif /* HAVE_LIBSSL */ } while (status == HTTP_UNAUTHORIZED || status == HTTP_UPGRADE_REQUIRED); /* * See if we actually got the file or an error... */ if (status != HTTP_OK) { unlink(filename); httpFlush(cups_server); httpClose(cups_server); cups_server = NULL; return (NULL); } /* * OK, we need to copy the file... */ while ((bytes = httpRead(cups_server, buffer, sizeof(buffer))) > 0) write(fd, buffer, bytes); close(fd); return (filename); } /* * 'cupsGetPrinters()' - Get a list of printers. */ int /* O - Number of printers */ cupsGetPrinters(char ***printers) /* O - Printers */ { int n; /* Number of printers */ ipp_t *request, /* IPP Request */ *response; /* IPP Response */ ipp_attribute_t *attr; /* Current attribute */ cups_lang_t *language; /* Default language */ char **temp; /* Temporary pointer */ if (printers == NULL) { last_error = IPP_INTERNAL_ERROR; return (0); } /* * Try to connect to the server... */ if (!cups_connect("default", NULL, NULL)) { last_error = IPP_SERVICE_UNAVAILABLE; return (0); } /* * Build a CUPS_GET_PRINTERS request, which requires the following * attributes: * * attributes-charset * attributes-natural-language * requested-attributes */ request = ippNew(); request->request.op.operation_id = CUPS_GET_PRINTERS; request->request.op.request_id = 1; language = cupsLangDefault(); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(language)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, language->language); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "printer-name"); /* * Do the request and get back a response... */ n = 0; *printers = NULL; if ((response = cupsDoRequest(cups_server, request, "/")) != NULL) { last_error = response->request.status.status_code; for (attr = response->attrs; attr != NULL; attr = attr->next) if (attr->name != NULL && strcasecmp(attr->name, "printer-name") == 0 && attr->value_tag == IPP_TAG_NAME) { if (n == 0) temp = emalloc(sizeof(char *)); else temp = erealloc(*printers, sizeof(char *) * (n + 1)); if (temp == NULL) { /* * Ran out of memory! */ while (n > 0) { n --; efree((*printers)[n]); } efree(*printers); ippDelete(response); return (0); } *printers = temp; temp[n] = estrdup(attr->values[0].string.text); n ++; } ippDelete(response); } else last_error = IPP_BAD_REQUEST; return (n); } /* * 'cupsLastError()' - Return the last IPP error that occurred. */ ipp_status_t /* O - IPP error code */ cupsLastError(void) { return (last_error); } /* * 'cupsPrintFile()' - Print a file to a printer or class. */ int /* O - Job ID */ cupsPrintFile(const char *name, /* I - Printer or class name */ const char *filename, /* I - File to print */ const char *title, /* I - Title of job */ int num_options,/* I - Number of options */ cups_option_t *options) /* I - Options */ { DEBUG_printf(("cupsPrintFile(\'%s\', \'%s\', %d, %p)\n", name, filename, num_options, options)); return (cupsPrintFiles(name, 1, &filename, title, num_options, options)); } /* * 'cupsPrintFiles()' - Print one or more files to a printer or class. */ int /* O - Job ID */ cupsPrintFiles(const char *name, /* I - Printer or class name */ int num_files, /* I - Number of files */ const char **files, /* I - File(s) to print */ const char *title, /* I - Title of job */ int num_options,/* I - Number of options */ cups_option_t *options) /* I - Options */ { int i; /* Looping var */ const char *val; /* Pointer to option value */ ipp_t *request; /* IPP request */ ipp_t *response; /* IPP response */ ipp_attribute_t *attr; /* IPP job-id attribute */ char hostname[HTTP_MAX_URI], /* Hostname */ printer[HTTP_MAX_URI], /* Printer or class name */ uri[HTTP_MAX_URI]; /* Printer URI */ cups_lang_t *language; /* Language to use */ int jobid; /* New job ID */ DEBUG_printf(("cupsPrintFiles(\'%s\', %d, %p, %d, %p)\n", name, num_files, files, num_options, options)); if (name == NULL || num_files < 1 || files == NULL) return (0); /* * Setup a connection and request data... */ if (!cups_connect(name, printer, hostname)) { DEBUG_printf(("cupsPrintFile: Unable to open connection - %s.\n", strerror(errno))); last_error = IPP_SERVICE_UNAVAILABLE; return (0); } language = cupsLangDefault(); /* * Build a standard CUPS URI for the printer and fill the standard IPP * attributes... */ if ((request = ippNew()) == NULL) return (0); request->request.op.operation_id = num_files == 1 ? IPP_PRINT_JOB : IPP_CREATE_JOB; request->request.op.request_id = 1; snprintf(uri, sizeof(uri), "ipp://%s:%d/printers/%s", hostname, ippPort(), printer); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(language)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, language != NULL ? language->language : "C"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); if (title) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title); /* * Then add all options... */ cupsEncodeOptions(request, num_options, options); /* * Do the request... */ snprintf(uri, sizeof(uri), "/printers/%s", printer); if (num_files == 1) response = cupsDoFileRequest(cups_server, request, uri, *files); else response = cupsDoRequest(cups_server, request, uri); if (response == NULL) jobid = 0; else if (response->request.status.status_code > IPP_OK_CONFLICT) { DEBUG_printf(("IPP response code was 0x%x!\n", response->request.status.status_code)); jobid = 0; } else if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL) { DEBUG_puts("No job ID!"); jobid = 0; } else jobid = attr->values[0].integer; if (response != NULL) ippDelete(response); /* * Handle multiple file jobs if the create-job operation worked... */ if (jobid > 0 && num_files > 1) for (i = 0; i < num_files; i ++) { /* * Build a standard CUPS URI for the job and fill the standard IPP * attributes... */ if ((request = ippNew()) == NULL) return (0); request->request.op.operation_id = IPP_SEND_DOCUMENT; request->request.op.request_id = 1; snprintf(uri, sizeof(uri), "ipp://%s:%d/jobs/%d", hostname, ippPort(), jobid); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(language)); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, language != NULL ? language->language : "C"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); /* * Handle raw print files... */ if (cupsGetOption("raw", num_options, options)) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, "application/vnd.cups-raw"); else if ((val = cupsGetOption("document-format", num_options, options)) != NULL) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, val); else ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, "application/octet-stream"); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser()); /* * Is this the last document? */ if (i == (num_files - 1)) ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1); /* * Send the file... */ snprintf(uri, sizeof(uri), "/printers/%s", printer); if ((response = cupsDoFileRequest(cups_server, request, uri, files[i])) != NULL) ippDelete(response); } return (jobid); } /* * 'cups_connect()' - Connect to the specified host... */ static char * /* I - Printer name or NULL */ cups_connect(const char *name, /* I - Destination (printer[@host]) */ char *printer, /* O - Printer name [HTTP_MAX_URI] */ char *hostname) /* O - Hostname [HTTP_MAX_URI] */ { char hostbuf[HTTP_MAX_URI]; /* Name of host */ static char printerbuf[HTTP_MAX_URI]; /* Name of printer or class */ DEBUG_printf(("cups_connect(\"%s\", %p, %p)\n", name, printer, hostname)); if (name == NULL) { last_error = IPP_BAD_REQUEST; return (NULL); } if (sscanf(name, "%1023[^@]@%1023s", printerbuf, hostbuf) == 1) { strncpy(hostbuf, cupsServer(), sizeof(hostbuf) - 1); hostbuf[sizeof(hostbuf) - 1] = '\0'; } if (hostname != NULL) { strncpy(hostname, hostbuf, HTTP_MAX_URI - 1); hostname[HTTP_MAX_URI - 1] = '\0'; } else hostname = hostbuf; if (printer != NULL) { strncpy(printer, printerbuf, HTTP_MAX_URI - 1); printer[HTTP_MAX_URI - 1] = '\0'; } else printer = printerbuf; if (cups_server != NULL) { if (strcasecmp(cups_server->hostname, hostname) == 0) return (printer); httpClose(cups_server); } DEBUG_printf(("connecting to %s on port %d...\n", hostname, ippPort())); if ((cups_server = httpConnectEncrypt(hostname, ippPort(), cupsEncryption())) == NULL) { last_error = IPP_SERVICE_UNAVAILABLE; return (NULL); } else return (printer); } /* * 'cups_local_auth()' - Get the local authorization certificate if * available/applicable... */ static int /* O - 1 if available, 0 if not */ cups_local_auth(http_t *http) /* I - Connection */ { #if defined(WIN32) || defined(__EMX__) /* * Currently WIN32 and OS-2 do not support the CUPS server... */ return (0); #else int pid; /* Current process ID */ FILE *fp; /* Certificate file */ char filename[1024], /* Certificate filename */ certificate[33];/* Certificate string */ const char *root; /* Server root directory */ /* * See if we are accessing localhost... */ if (ntohl(http->hostaddr.sin_addr.s_addr) != 0x7f000001 && strcasecmp(http->hostname, "localhost") != 0) return (0); /* * Try opening a certificate file for this PID. If that fails, * try the root certificate... */ if ((root = getenv("CUPS_SERVERROOT")) == NULL) root = CUPS_SERVERROOT; pid = getpid(); snprintf(filename, sizeof(filename), "%s/certs/%d", root, pid); if ((fp = fopen(filename, "r")) == NULL && pid > 0) { snprintf(filename, sizeof(filename), "%s/certs/0", root); fp = fopen(filename, "r"); } if (fp == NULL) return (0); /* * Read the certificate from the file... */ fgets(certificate, sizeof(certificate), fp); fclose(fp); /* * Set the authorization string and return... */ snprintf(authstring, sizeof(authstring), "Local %s", certificate); return (1); #endif /* WIN32 || __EMX__ */ } /* * End of "$Id: phpcups.c,v 1.1.1.1 2003/04/11 21:07:50 jlovell Exp $". */