1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  *
  26  */
  27 
  28 /* $Id: ipp-listener.c 146 2006-03-24 00:26:54Z njacobs $ */
  29 
  30 #pragma ident   "%Z%%M% %I%     %E% SMI"
  31 
  32 #include <stdio.h>
  33 #include <stdlib.h>
  34 #include <string.h>
  35 #include <netinet/in.h>
  36 #include <assert.h>
  37 #include <errno.h>
  38 #include <syslog.h>
  39 #include <sys/types.h>
  40 #include <sys/stat.h>
  41 #include <fcntl.h>
  42 #include <unistd.h>
  43 #include <sys/systeminfo.h>
  44 
  45 #include <papi.h>
  46 #include <ipp-listener.h>
  47 #include <uri.h>
  48 
  49 typedef papi_status_t (ipp_handler_t)(papi_service_t svc,
  50                                         papi_attribute_t **request,
  51                                         papi_attribute_t ***response,
  52                                         ipp_reader_t iread, void *fd);
  53 
  54 /*
  55  * protocol request handlers are inserted below.  The handler must be
  56  * declared extern immediately below this comment and then an entry
  57  * must be inserted in the "handlers" table a little further down.
  58  */
  59 extern ipp_handler_t ipp_print_job;
  60 extern ipp_handler_t ipp_validate_job;
  61 extern ipp_handler_t ipp_create_job;
  62 extern ipp_handler_t ipp_get_printer_attributes;
  63 extern ipp_handler_t ipp_get_jobs;
  64 extern ipp_handler_t ipp_pause_printer;
  65 extern ipp_handler_t ipp_resume_printer;
  66 extern ipp_handler_t ipp_disable_printer;
  67 extern ipp_handler_t ipp_enable_printer;
  68 extern ipp_handler_t ipp_purge_jobs;
  69 extern ipp_handler_t ipp_send_document;
  70 extern ipp_handler_t ipp_cancel_job;
  71 extern ipp_handler_t ipp_get_job_attributes;
  72 extern ipp_handler_t ipp_release_job;
  73 extern ipp_handler_t ipp_hold_job;
  74 extern ipp_handler_t ipp_restart_job;
  75 extern ipp_handler_t ipp_set_job_attributes;
  76 extern ipp_handler_t ipp_set_printer_attributes;
  77 extern ipp_handler_t cups_get_default;
  78 extern ipp_handler_t cups_get_printers;
  79 extern ipp_handler_t cups_get_classes;
  80 extern ipp_handler_t cups_accept_jobs;
  81 extern ipp_handler_t cups_reject_jobs;
  82 extern ipp_handler_t cups_move_job;
  83 
  84 /* ARGSUSED0 */
  85 static papi_status_t
  86 default_handler(papi_service_t svc, papi_attribute_t **request,
  87                 papi_attribute_t ***response, ipp_reader_t iread, void *fd)
  88 {
  89         int result = (int)PAPI_INTERNAL_ERROR;
  90 
  91         if (response != NULL)
  92                 (void) papiAttributeListGetInteger(*response, NULL,
  93                                         "status-code", &result);
  94 
  95         return ((papi_status_t)result);
  96 }
  97 
  98 static struct {
  99         int16_t id;
 100         char *name;
 101         ipp_handler_t *function;
 102         enum { OP_REQUIRED, OP_OPTIONAL, OP_VENDOR } type;
 103 } handlers[] = {
 104         /* Printer Operations */
 105         { 0x0002, "print-job",                  ipp_print_job,  OP_REQUIRED },
 106         { 0x0003, "print-uri",                  NULL,           OP_OPTIONAL },
 107         { 0x0004, "validate-job",               ipp_validate_job,
 108                                                                 OP_REQUIRED },
 109         { 0x0005, "create-job",                 ipp_create_job, OP_OPTIONAL },
 110         { 0x000a, "get-jobs",                   ipp_get_jobs,   OP_REQUIRED },
 111         { 0x000b, "get-printer-attributes",     ipp_get_printer_attributes,
 112                                                                 OP_REQUIRED },
 113         { 0x0010, "pause-printer",              ipp_pause_printer,
 114                                                                 OP_OPTIONAL },
 115         { 0x0011, "resume-printer",             ipp_resume_printer,
 116                                                                 OP_OPTIONAL },
 117         { 0x0012, "purge-jobs",                 ipp_purge_jobs, OP_OPTIONAL },
 118         { 0x0013, "set-printer-attributes",     ipp_set_printer_attributes,
 119                                                                 OP_OPTIONAL },
 120         { 0x0014, "set-job-attributes",         ipp_set_job_attributes,
 121                                                                 OP_OPTIONAL },
 122         { 0x0022, "enable-printer",             ipp_enable_printer,
 123                                                                 OP_OPTIONAL },
 124         { 0x0023, "disable-printer",            ipp_disable_printer,
 125                                                                 OP_OPTIONAL },
 126         /* Job Operations */
 127         { 0x0006, "send-document",              ipp_send_document,
 128                                                                 OP_OPTIONAL },
 129         { 0x0007, "send-uri",                   NULL,           OP_OPTIONAL },
 130         { 0x0008, "cancel-job",                 ipp_cancel_job, OP_REQUIRED },
 131         { 0x0009, "get-job-attributes",         ipp_get_job_attributes,
 132                                                                 OP_REQUIRED },
 133         { 0x000c, "hold-job",                   ipp_hold_job,   OP_OPTIONAL },
 134         { 0x000d, "release-job",                ipp_release_job,
 135                                                                 OP_OPTIONAL },
 136         { 0x000e, "restart-job",                ipp_restart_job,
 137                                                                 OP_OPTIONAL },
 138         /* Other Operations */
 139         { 0x4001, "cups-get-default",           cups_get_default,
 140                                                                 OP_VENDOR },
 141         { 0x4002, "cups-get-printers",          cups_get_printers,
 142                                                                 OP_VENDOR },
 143         { 0x4005, "cups-get-classes",           cups_get_classes,
 144                                                                 OP_VENDOR },
 145         { 0x4008, "cups-accept-jobs",           cups_accept_jobs,
 146                                                                 OP_VENDOR },
 147         { 0x4009, "cups-reject-jobs",           cups_reject_jobs,
 148                                                                 OP_VENDOR },
 149         { 0x400D, "cups-move-job",              cups_move_job,  OP_VENDOR },
 150         { 0, NULL, NULL, OP_VENDOR }
 151 };
 152 
 153 static int
 154 ipp_operation_name_to_index(char *name)
 155 {
 156         int i;
 157 
 158         for (i = 0; handlers[i].name != NULL; i++)
 159                 if (strcasecmp(name, handlers[i].name) == 0)
 160                         return (i);
 161 
 162         return (-1);
 163 }
 164 
 165 static int
 166 ipp_operation_id_to_index(int16_t id)
 167 {
 168         int i;
 169 
 170         for (i = 0; handlers[i].name != NULL; i++)
 171                 if (id == handlers[i].id)
 172                         return (i);
 173 
 174         return (-1);
 175 }
 176 
 177 static ipp_handler_t *
 178 ipp_operation_handler(papi_attribute_t **request, papi_attribute_t ***response)
 179 {
 180         int id = 0;
 181         int index;
 182         papi_attribute_t **ops = NULL;
 183         papi_status_t status;
 184         char configured = PAPI_FALSE;
 185 
 186         /* get the operation from the request */
 187         status = papiAttributeListGetInteger(request, NULL,
 188                                 "operation-id", &id);
 189         if (status != PAPI_OK) {
 190                 ipp_set_status(response, PAPI_BAD_ARGUMENT,
 191                         "no operation specified in request");
 192                 return (default_handler);
 193         }
 194 
 195         /* find the operation in the handler table */
 196         index = ipp_operation_id_to_index(id);
 197 #ifdef DEBUG
 198         if (index == -1)
 199                 fprintf(stderr, "Operation: 0x%4.4x\n", id);
 200         else
 201                 fprintf(stderr, "Operation: 0x%4.4x(%s)\n", id,
 202                         handlers[index].name);
 203         fflush(stderr);
 204 #endif
 205 
 206         if ((index == -1) || (handlers[index].function == NULL)) {
 207                 ipp_set_status(response, PAPI_OPERATION_NOT_SUPPORTED,
 208                         "operation (0x%4.4x) not implemented by server",
 209                         id);
 210                 return (default_handler);
 211         }
 212 
 213         /* find the configured operations */
 214         status = papiAttributeListGetCollection(request, NULL,
 215                                 "operations", &ops);
 216         if (status != PAPI_OK) {        /* this should not be possible */
 217                 ipp_set_status(response, PAPI_INTERNAL_ERROR,
 218                         "sofware error, no operations configured");
 219                 return (default_handler);
 220         }
 221 
 222         /* check if the requested operation is configured */
 223         status = papiAttributeListGetBoolean(ops, NULL,
 224                                 handlers[index].name, &configured);
 225         if ((status != PAPI_OK) || (configured != PAPI_TRUE)) {
 226                 ipp_set_status(response, PAPI_OPERATION_NOT_SUPPORTED,
 227                         "operation (%s 0x%4.4x) not enabled on server",
 228                         handlers[index].name, id);
 229                 return (default_handler);
 230         }
 231 
 232         return (handlers[index].function);
 233 }
 234 
 235 static char
 236 type_to_boolean(char *type)
 237 {
 238         char result = PAPI_FALSE;
 239 
 240         if ((strcasecmp(type, "true") == 0) ||
 241             (strcasecmp(type, "yes") == 0) ||
 242             (strcasecmp(type, "on") == 0) ||
 243             (strcasecmp(type, "enable") == 0))
 244                 result = PAPI_TRUE;
 245 
 246         return (result);
 247 }
 248 
 249 static papi_status_t
 250 ipp_configure_required_operations(papi_attribute_t ***list, char boolean)
 251 {
 252         papi_status_t result = PAPI_OK;
 253         int i;
 254 
 255         for (i = 0; ((result == PAPI_OK) && (handlers[i].name != NULL)); i++)
 256                 if (handlers[i].type == OP_REQUIRED)
 257                         result = papiAttributeListAddBoolean(list,
 258                                         PAPI_ATTR_REPLACE, handlers[i].name,
 259                                         boolean);
 260 
 261         return (result);
 262 
 263 }
 264 
 265 static papi_status_t
 266 ipp_configure_all_operations(papi_attribute_t ***list, char boolean)
 267 {
 268         papi_status_t result = PAPI_OK;
 269         int i;
 270 
 271         for (i = 0; ((result == PAPI_OK) && (handlers[i].name != NULL)); i++)
 272                 result = papiAttributeListAddBoolean(list, PAPI_ATTR_REPLACE,
 273                                 handlers[i].name, boolean);
 274 
 275         return (result);
 276 }
 277 
 278 papi_status_t
 279 ipp_configure_operation(papi_attribute_t ***list, char *operation, char *type)
 280 {
 281         papi_status_t result = PAPI_OPERATION_NOT_SUPPORTED;
 282         char boolean = PAPI_FALSE;
 283 
 284         if ((list == NULL) || (operation == NULL) || (type == NULL))
 285                 return (PAPI_BAD_ARGUMENT);
 286 
 287         boolean = type_to_boolean(type);
 288 
 289         if (strcasecmp(operation, "all") == 0) {
 290                 result = ipp_configure_all_operations(list, boolean);
 291         } else if (strcasecmp(operation, "required") == 0) {
 292                 result = ipp_configure_required_operations(list, boolean);
 293         } else if (ipp_operation_name_to_index(operation) != -1) {
 294                 result = papiAttributeListAddBoolean(list, PAPI_ATTR_REPLACE,
 295                                                         operation, boolean);
 296         }
 297 
 298         return (result);
 299 }
 300 
 301 void
 302 ipp_operations_supported(papi_attribute_t ***list, papi_attribute_t **request)
 303 {
 304         papi_attribute_t **group = NULL;
 305 
 306         (void) papiAttributeListGetCollection(request, NULL,
 307                                 "operations", &group);
 308         if (group != NULL) {
 309                 int i;
 310 
 311                 for (i = 0; handlers[i].name != NULL; i++) {
 312                         char boolean = PAPI_FALSE;
 313                         (void) papiAttributeListGetBoolean(group, NULL,
 314                                         handlers[i].name, &boolean);
 315 
 316                         if (boolean == PAPI_TRUE)
 317                                 (void) papiAttributeListAddInteger(list,
 318                                         PAPI_ATTR_APPEND,
 319                                         "operations-supported",
 320                                         handlers[i].id);
 321                 }
 322         }
 323 }
 324 
 325 static papi_status_t
 326 ipp_initialize_response(papi_attribute_t **request,
 327                         papi_attribute_t ***response)
 328 {
 329         papi_attribute_t **operational = NULL;
 330         int i;
 331 
 332         if ((request == NULL) || (response == NULL))
 333                 return (PAPI_BAD_ARGUMENT);
 334 
 335         /* If the response was initialized, start over */
 336         if (*response != NULL) {
 337                 papiAttributeListFree(*response);
 338                 *response = NULL;
 339         }
 340 
 341         /* Add the basic ipp header information to the response */
 342         (void) papiAttributeListGetInteger(request, NULL, "version-major", &i);
 343         (void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
 344                                         "version-major", i);
 345         (void) papiAttributeListGetInteger(request, NULL, "version-minor", &i);
 346         (void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
 347                                         "version-minor", i);
 348 
 349         (void) papiAttributeListGetInteger(request, NULL, "request-id", &i);
 350         (void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
 351                                         "request-id", i);
 352 
 353         /* Add a default operational attributes group to the response */
 354         (void) papiAttributeListAddString(&operational, PAPI_ATTR_EXCL,
 355                         "attributes-charset", "utf-8");
 356         (void) papiAttributeListAddString(&operational, PAPI_ATTR_EXCL,
 357                         "attributes-natural-language", "en-us");
 358 
 359         (void) papiAttributeListAddCollection(response, PAPI_ATTR_REPLACE,
 360                                 "operational-attributes-group", operational);
 361         papiAttributeListFree(operational);
 362 
 363         return (PAPI_OK);
 364 }
 365 
 366 /* simplistic check for cyclical service references */
 367 static int
 368 cyclical_service_check(char *svc_name, int port)
 369 {
 370         papi_attribute_t **list;
 371         char buf[BUFSIZ];
 372         uri_t *uri = NULL;
 373         char *s = NULL;
 374 
 375         /* was there a service_uri? */
 376         if (svc_name == NULL)
 377                 return (0);
 378 
 379         if ((list = getprinterbyname(svc_name, NULL)) == NULL)
 380                 return (0);     /* if it doesnt' resolve, we will fail later */
 381 
 382         papiAttributeListGetString(list, NULL, "printer-uri-supported", &s);
 383         if ((s == NULL) || (strcasecmp(svc_name, s) != 0))
 384                 return (0);     /* they don't match */
 385 
 386         /* is it in uri form? */
 387         if (uri_from_string(s, &uri) < 0)
 388                 return (0);
 389 
 390         if ((uri == NULL) || (uri->scheme == NULL) || (uri->host == NULL)) {
 391                 uri_free(uri);
 392                 return (0);
 393         }
 394 
 395         /* is it ipp form */
 396         if (strcasecmp(uri->scheme, "ipp") != 0) {
 397                 uri_free(uri);
 398                 return (0);
 399         }
 400 
 401         /* does the host match up */
 402         if (is_localhost(uri->host) != 0) {
 403                 uri_free(uri);
 404                 return (0);
 405         }
 406 
 407         /* does the port match our own */
 408         if (((uri->port == NULL) && (port != 631)) ||
 409             ((uri->port != NULL) && (atoi(uri->port) != port))) {
 410                 uri_free(uri);
 411                 return (0);
 412         }
 413 
 414         uri_free(uri);
 415 
 416         return (1);
 417 }
 418 
 419 static papi_status_t
 420 print_service_connect(papi_service_t *svc, papi_attribute_t **request,
 421                 papi_attribute_t ***response)
 422 {
 423         papi_status_t status;
 424         papi_attribute_t **operational = NULL;
 425         char *printer_uri = NULL;
 426         char *svc_name = NULL;
 427         char *user = NULL;
 428         int port = 631;
 429 
 430         /* Get the operational attributes group from the request */
 431         (void) papiAttributeListGetCollection(request, NULL,
 432                                 "operational-attributes-group", &operational);
 433 
 434         /* get the user name */
 435         (void) papiAttributeListGetString(request, NULL, "default-user", &user);
 436         (void) papiAttributeListGetString(operational, NULL,
 437                                 "requesting-user-name", &user);
 438 
 439         /* get the printer or service name */
 440         (void) papiAttributeListGetString(request, NULL,
 441                                 "default-service", &svc_name);
 442         get_printer_id(operational, &svc_name, NULL);
 443 
 444         /* get the port that we are listening on */
 445         (void) papiAttributeListGetInteger(request, NULL, "uri-port", &port);
 446 
 447         if (cyclical_service_check(svc_name, port) != 0) {
 448                 status = PAPI_NOT_POSSIBLE;
 449                 ipp_set_status(response, status, "printer-uri is cyclical");
 450                 return (status);
 451         }
 452 
 453         status = papiServiceCreate(svc, svc_name, user, NULL, NULL,
 454                                         PAPI_ENCRYPT_NEVER, NULL);
 455         if (status != PAPI_OK) {
 456                 ipp_set_status(response, status, "print service: %s",
 457                                 papiStatusString(status));
 458                 return (status);
 459         }
 460 
 461         /*
 462          * Trusted Solaris can't be trusting of intermediaries.  Pass
 463          * the socket connection to the print service to retrieve the
 464          * sensativity label off of a multi-level port.
 465          */
 466         {
 467                 int fd = -1;
 468 
 469                 (void) papiAttributeListGetInteger(request, NULL,
 470                                         "peer-socket", &fd);
 471                 if (fd != -1)
 472                         papiServiceSetPeer(*svc, fd);
 473         }
 474 
 475         return (status);
 476 }
 477 
 478 papi_status_t
 479 ipp_process_request(papi_attribute_t **request, papi_attribute_t ***response,
 480         ipp_reader_t iread, void *fd)
 481 {
 482         papi_status_t result = PAPI_OK;
 483 
 484         ipp_initialize_response(request, response);
 485 
 486 #ifdef DEBUG
 487         fprintf(stderr, "REQUEST:");
 488         papiAttributeListPrint(stderr, request, " %d  ", getpid());
 489         fprintf(stderr, "\n");
 490 #endif
 491 
 492         /* verify that the request is "well-formed" */
 493         if ((result = ipp_validate_request(request, response)) == PAPI_OK) {
 494                 papi_service_t svc = NULL;
 495                 ipp_handler_t *handler;
 496 
 497                 result = print_service_connect(&svc, request, response);
 498                 handler = ipp_operation_handler(request, response);
 499 
 500                 /* process the request */
 501                 if ((result == PAPI_OK) && (handler != NULL))
 502                         result = (handler)(svc, request, response, iread, fd);
 503 #ifdef DEBUG
 504                 fprintf(stderr, "RESULT: %s\n", papiStatusString(result));
 505 #endif
 506                 papiServiceDestroy(svc);
 507         }
 508 
 509         (void) papiAttributeListAddInteger(response, PAPI_ATTR_EXCL,
 510                                 "status-code", result);
 511         massage_response(request, *response);
 512 
 513 #ifdef DEBUG
 514         fprintf(stderr, "RESPONSE:");
 515         papiAttributeListPrint(stderr, *response, " %d  ", getpid());
 516         fprintf(stderr, "\n");
 517 #endif
 518 
 519         return (result);
 520 }