]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/ipp.c
Get rid of compiler warnings.
[thirdparty/cups.git] / cups / ipp.c
index 32e8be5bb723f3b44d6362575cf6fe56919d2bed..bb5fc9b56993984f8fac69789de8b3b142b300b0 100644 (file)
@@ -1,10 +1,10 @@
 /*
- * "$Id: ipp.c,v 1.31 2000/01/21 20:33:16 mike Exp $"
+ * "$Id: ipp.c,v 1.56 2001/04/15 11:52:43 mike Exp $"
  *
  *   Internet Printing Protocol support functions for the Common UNIX
  *   Printing System (CUPS).
  *
- *   Copyright 1997-2000 by Easy Software Products, all rights reserved.
+ *   Copyright 1997-2001 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
  *   property of Easy Software Products and are protected by Federal
  *   ippErrorString()    - Return a textual message for the given error message.
  *   ippFindAttribute()  - Find a named attribute in a request...
  *   ippLength()         - Compute the length of an IPP request.
+ *   ippNew()            - Allocate a new IPP request.
  *   ippPort()           - Return the default IPP port number.
  *   ippRead()           - Read data for an IPP request.
+ *   ippSetPort()        - Set the default port number.
  *   ippTimeToDate()     - Convert from UNIX time to RFC 1903 format.
  *   ippWrite()          - Write data for an IPP request.
- *   _ipp_add_attr()          - Add a new attribute to the request.
+ *   _ipp_add_attr()     - Add a new attribute to the request.
+ *   _ipp_free_attr()    - Free an attribute.
  *   ipp_read()          - Semi-blocking read on a HTTP connection...
  */
 
 
 #include "ipp.h"
 #include "debug.h"
+#include <ctype.h>
+
+
+/*
+ * Local globals...
+ */
+
+static int     ipp_port = 0;
 
 
 /*
@@ -82,7 +93,7 @@ ippAddBoolean(ipp_t      *ipp,                /* I - IPP request */
   ipp_attribute_t      *attr;          /* New attribute */
 
 
-  DEBUG_printf(("ippAddBoolean(%08x, %02x, \'%s\', %d)\n", ipp, group, name, value));
+  DEBUG_printf(("ippAddBoolean(%p, %02x, \'%s\', %d)\n", ipp, group, name, value));
 
   if (ipp == NULL || name == NULL)
     return (NULL);
@@ -114,7 +125,7 @@ ippAddBooleans(ipp_t      *ipp,             /* I - IPP request */
   ipp_attribute_t      *attr;          /* New attribute */
 
 
-  DEBUG_printf(("ippAddBooleans(%08x, %02x, \'%s\', %d, %08x)\n", ipp,
+  DEBUG_printf(("ippAddBooleans(%p, %02x, \'%s\', %d, %p)\n", ipp,
                 group, name, num_values, values));
 
   if (ipp == NULL || name == NULL)
@@ -148,7 +159,7 @@ ippAddDate(ipp_t             *ipp,  /* I - IPP request */
   ipp_attribute_t      *attr;          /* New attribute */
 
 
-  DEBUG_printf(("ippAddDate(%08x, %02x, \'%s\', %08x)\n", ipp, group, name,
+  DEBUG_printf(("ippAddDate(%p, %02x, \'%s\', %p)\n", ipp, group, name,
                 value));
 
   if (ipp == NULL || name == NULL || value == NULL)
@@ -180,7 +191,7 @@ ippAddInteger(ipp_t      *ipp,              /* I - IPP request */
   ipp_attribute_t      *attr;          /* New attribute */
 
 
-  DEBUG_printf(("ippAddInteger(%08x, %d, \'%s\', %d)\n", ipp, group, name,
+  DEBUG_printf(("ippAddInteger(%p, %d, \'%s\', %d)\n", ipp, group, name,
                 value));
 
   if (ipp == NULL || name == NULL)
@@ -257,7 +268,24 @@ ippAddString(ipp_t      *ipp,              /* I - IPP request */
   attr->group_tag                 = group;
   attr->value_tag                 = type;
   attr->values[0].string.charset  = charset ? strdup(charset) : NULL;
-  attr->values[0].string.text     = strdup(value);
+  attr->values[0].string.text     = value ? strdup(value) : NULL;
+
+  if ((type == IPP_TAG_LANGUAGE || type == IPP_TAG_CHARSET) &&
+      attr->values[0].string.text)
+  {
+   /*
+    * Convert to lowercase and change _ to - as needed...
+    */
+
+    char *p;
+
+
+    for (p = attr->values[0].string.text; *p; p ++)
+      if (*p == '_')
+        *p = '-';
+      else
+        *p = tolower(*p);
+  }
 
   return (attr);
 }
@@ -453,7 +481,7 @@ ippAddSeparator(ipp_t *ipp)         /* I - IPP request */
   ipp_attribute_t      *attr;          /* New attribute */
 
 
-  DEBUG_printf(("ippAddSeparator(%08x)\n", ipp));
+  DEBUG_printf(("ippAddSeparator(%p)\n", ipp));
 
   if (ipp == NULL)
     return (NULL);
@@ -524,48 +552,19 @@ ippDateToTime(const ipp_uchar_t *date)    /* I - RFC 1903 date info */
 void
 ippDelete(ipp_t *ipp)          /* I - IPP request */
 {
-  int                  i;      /* Looping var */
   ipp_attribute_t      *attr,  /* Current attribute */
                        *next;  /* Next attribute */
 
 
+  DEBUG_printf(("ippNew(): %p\n", ipp));
+
   if (ipp == NULL)
     return;
 
   for (attr = ipp->attrs; attr != NULL; attr = next)
   {
-    switch (attr->value_tag)
-    {
-      case IPP_TAG_TEXT :
-      case IPP_TAG_NAME :
-      case IPP_TAG_KEYWORD :
-      case IPP_TAG_STRING :
-      case IPP_TAG_URI :
-      case IPP_TAG_URISCHEME :
-      case IPP_TAG_CHARSET :
-      case IPP_TAG_LANGUAGE :
-      case IPP_TAG_MIMETYPE :
-          for (i = 0; i < attr->num_values; i ++)
-           free(attr->values[i].string.text);
-         break;
-
-      case IPP_TAG_TEXTLANG :
-      case IPP_TAG_NAMELANG :
-          for (i = 0; i < attr->num_values; i ++)
-         {
-           if (attr->values[i].string.charset)
-             free(attr->values[i].string.charset);
-           free(attr->values[i].string.text);
-         }
-         break;
-    }
-
     next = attr->next;
-
-    if (attr->name != NULL)
-      free(attr->name);
-
-    free(attr);
+    _ipp_free_attr(attr);
   }
 
   free(ipp);
@@ -579,73 +578,80 @@ ippDelete(ipp_t *ipp)             /* I - IPP request */
 const char *                           /* O - Text string */
 ippErrorString(ipp_status_t error)     /* I - Error status */
 {
-  static cups_lang_t   *language = 0;  /* Language info */
+  static char  unknown[255];           /* Unknown error statuses */
+  static const char *status_oks[] =    /* "OK" status codes */
+               {
+                 "successful-ok",
+                 "successful-ok-ignored-or-substituted-attributes",
+                 "successful-ok-conflicting-attributes",
+                 "successful-ok-ignored-subscriptions",
+                 "successful-ok-ignored-notifications",
+                 "successful-ok-too-many-events",
+                 "successful-ok-but-cancel-subscription"
+               },
+               *status_400s[] =        /* Client errors */
+               {
+                 "client-error-bad-request",
+                 "client-error-forbidden",
+                 "client-error-not-authenticated",
+                 "client-error-not-authorized",
+                 "client-error-not-possible",
+                 "client-error-timeout",
+                 "client-error-not-found",
+                 "client-error-gone",
+                 "client-error-request-entity-too-large",
+                 "client-error-request-value-too-long",
+                 "client-error-document-format-not-supported",
+                 "client-error-attributes-or-values-not-supported",
+                 "client-error-uri-scheme-not-supported",
+                 "client-error-charset-not-supported",
+                 "client-error-conflicting-attributes",
+                 "client-error-compression-not-supported",
+                 "client-error-compression-error",
+                 "client-error-document-format-error",
+                 "client-error-document-access-error",
+                 "client-error-attributes-not-settable",
+                 "client-error-ignored-all-subscriptions",
+                 "client-error-too-many-subscriptions",
+                 "client-error-ignored-all-notifications",
+                 "client-error-print-support-file-not-found"
+               },
+               *status_500s[] =        /* Server errors */
+               {
+                 "server-error-internal-error",
+                 "server-error-operation-not-supported",
+                 "server-error-service-unavailable",
+                 "server-error-version-not-supported",
+                 "server-error-device-error",
+                 "server-error-temporary-error",
+                 "server-error-not-accepting-jobs",
+                 "server-error-busy",
+                 "server-error-job-canceled",
+                 "server-error-multiple-document-jobs-not-supported",
+                 "server-error-printer-is-deactivated"
+               };
 
 
  /*
-  * Load the localized message file as needed...
+  * See if the error code is a known value...
   */
 
-  if (!language)
-    language = cupsLangDefault();
+  if (error >= IPP_OK && error <= IPP_OK_BUT_CANCEL_SUBSCRIPTION)
+    return (status_oks[error]);
+  else if (error == IPP_REDIRECTION_OTHER_SITE)
+    return ("redirection-other-site");
+  else if (error >= IPP_BAD_REQUEST && error <= IPP_PRINT_SUPPORT_FILE_NOT_FOUND)
+    return (status_400s[error - IPP_BAD_REQUEST]);
+  else if (error >= IPP_INTERNAL_ERROR && error <= IPP_PRINTER_IS_DEACTIVATED)
+    return (status_500s[error - IPP_INTERNAL_ERROR]);
 
  /*
-  * Return the appropriate message...
+  * No, build an "unknown-xxxx" error string...
   */
 
-  switch (error)
-  {
-    case IPP_OK :
-    case IPP_OK_SUBST :
-    case IPP_OK_CONFLICT :
-        return ("OK");
-
-    case IPP_BAD_REQUEST :
-        return (cupsLangString(language, HTTP_BAD_REQUEST));
-
-    case IPP_FORBIDDEN :
-        return (cupsLangString(language, HTTP_FORBIDDEN));
-
-    case IPP_NOT_AUTHENTICATED :
-    case IPP_NOT_AUTHORIZED :
-        return (cupsLangString(language, HTTP_UNAUTHORIZED));
-
-    case IPP_NOT_POSSIBLE :
-        return (cupsLangString(language, HTTP_METHOD_NOT_ALLOWED));
-
-    case IPP_TIMEOUT :
-        return (cupsLangString(language, HTTP_REQUEST_TIMEOUT));
-
-    case IPP_NOT_FOUND :
-        return (cupsLangString(language, HTTP_NOT_FOUND));
-
-    case IPP_GONE :
-        return (cupsLangString(language, HTTP_GONE));
-
-    case IPP_DOCUMENT_FORMAT :
-        return (cupsLangString(language, HTTP_UNSUPPORTED_MEDIATYPE));
-
-    case IPP_CONFLICT :
-        return (cupsLangString(language, HTTP_CONFLICT));
-
-    case IPP_INTERNAL_ERROR :
-        return (cupsLangString(language, HTTP_SERVER_ERROR));
-
-    case IPP_OPERATION_NOT_SUPPORTED :
-    case IPP_VERSION_NOT_SUPPORTED :
-        return (cupsLangString(language, HTTP_NOT_SUPPORTED));
-
-    case IPP_SERVICE_UNAVAILABLE :
-    case IPP_DEVICE_UNAVAILABLE :
-    case IPP_TEMPORARY_ERROR :
-    case IPP_PRINTER_BUSY :
-        return (cupsLangString(language, HTTP_SERVICE_UNAVAILABLE));
-
-    case IPP_NOT_ACCEPTING :
-        return (cupsLangString(language, CUPS_MSG_NOT_ACCEPTING_JOBS));
-  }
+  sprintf(unknown, "unknown-%04x", error);
 
-  return ("ERROR");
+  return (unknown);
 }
 
 
@@ -658,23 +664,26 @@ ippFindAttribute(ipp_t      *ipp, /* I - IPP request */
                  const char *name,     /* I - Name of attribute */
                 ipp_tag_t  type)       /* I - Type of attribute */
 {
-  ipp_attribute_t      *attr;  /* Current atttribute */
+  ipp_attribute_t      *attr;          /* Current atttribute */
+  ipp_tag_t            value_tag;      /* Value tag */
 
 
-  DEBUG_printf(("ippFindAttribute(%08x, \'%s\')\n", ipp, name));
+  DEBUG_printf(("ippFindAttribute(%p, \'%s\')\n", ipp, name));
 
   if (ipp == NULL || name == NULL)
     return (NULL);
 
   for (attr = ipp->attrs; attr != NULL; attr = attr->next)
   {
-    DEBUG_printf(("ippFindAttribute: attr = %08x, name = \'%s\'\n", attr,
+    DEBUG_printf(("ippFindAttribute: attr = %p, name = \'%s\'\n", attr,
                   attr->name));
 
+    value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_MASK);
+
     if (attr->name != NULL && strcasecmp(attr->name, name) == 0 &&
-        (attr->value_tag == type ||
-        (attr->value_tag == IPP_TAG_TEXTLANG && type == IPP_TAG_TEXT) ||
-        (attr->value_tag == IPP_TAG_NAMELANG && type == IPP_TAG_NAME)))
+        (value_tag == type || type == IPP_TAG_ZERO ||
+        (value_tag == IPP_TAG_TEXTLANG && type == IPP_TAG_TEXT) ||
+        (value_tag == IPP_TAG_NAMELANG && type == IPP_TAG_NAME)))
       return (attr);
   }
 
@@ -729,7 +738,7 @@ ippLength(ipp_t *ipp)               /* I - IPP request */
     bytes += 2 * attr->num_values;     /* Name lengths */
     bytes += 2 * attr->num_values;     /* Value lengths */
 
-    switch (attr->value_tag)
+    switch (attr->value_tag & ~IPP_TAG_COPY)
     {
       case IPP_TAG_INTEGER :
       case IPP_TAG_ENUM :
@@ -767,11 +776,16 @@ ippLength(ipp_t *ipp)             /* I - IPP request */
 
       case IPP_TAG_TEXTLANG :
       case IPP_TAG_NAMELANG :
-          bytes += 2 * attr->num_values;/* Charset length */
+          bytes += 4 * attr->num_values;/* Charset + text length */
           for (i = 0; i < attr->num_values; i ++)
            bytes += strlen(attr->values[i].string.charset) +
                     strlen(attr->values[i].string.text);
          break;
+
+      default :
+          for (i = 0; i < attr->num_values; i ++)
+            bytes += attr->values[0].unknown.length;
+         break;
     }
   }
 
@@ -785,10 +799,29 @@ ippLength(ipp_t *ipp)             /* I - IPP request */
 }
 
 
+/*
+ * 'ippNew()' - Allocate a new IPP request.
+ */
+
 ipp_t *                                /* O - New IPP request */
 ippNew(void)
 {
-  return ((ipp_t *)calloc(sizeof(ipp_t), 1));
+  ipp_t        *temp;                  /* New IPP request */
+
+
+  if ((temp = (ipp_t *)calloc(1, sizeof(ipp_t))) != NULL)
+  {
+   /*
+    * Default to IPP 1.1...
+    */
+
+    temp->request.any.version[0] = 1;
+    temp->request.any.version[1] = 1;
+  }
+
+  DEBUG_printf(("ippNew(): %p\n", temp));
+
+  return (temp);
 }
 
 
@@ -801,12 +834,13 @@ ippRead(http_t *http,             /* I - HTTP data */
         ipp_t  *ipp)           /* I - IPP data */
 {
   int                  n;              /* Length of data */
-  unsigned char                buffer[8192];   /* Data buffer */
+  unsigned char                buffer[8192],   /* Data buffer */
+                       *bufptr;        /* Pointer into buffer */
   ipp_attribute_t      *attr;          /* Current attribute */
   ipp_tag_t            tag;            /* Current tag */
 
 
-  DEBUG_printf(("ippRead(%08x, %08x)\n", http, ipp));
+  DEBUG_printf(("ippRead(%p, %p)\n", http, ipp));
 
   if (http == NULL || ipp == NULL)
     return (IPP_ERROR);
@@ -852,6 +886,10 @@ ippRead(http_t *http,              /* I - HTTP data */
        ipp->current = NULL;
        ipp->curtag  = IPP_TAG_ZERO;
 
+        DEBUG_printf(("ippRead: version=%d.%d\n", buffer[0], buffer[1]));
+       DEBUG_printf(("ippRead: op_status=%04x\n", ipp->request.any.op_status));
+       DEBUG_printf(("ippRead: request_id=%d\n", ipp->request.any.request_id));
+
        /*
         * If blocking is disabled, stop here...
        */
@@ -921,6 +959,32 @@ ippRead(http_t *http,              /* I - HTTP data */
 
             attr = ipp->current;
 
+          /*
+           * Make sure we aren't adding a new value of a different
+           * type...
+           */
+
+           if (attr->value_tag == IPP_TAG_STRING ||
+               (attr->value_tag >= IPP_TAG_TEXTLANG &&
+                attr->value_tag <= IPP_TAG_MIMETYPE))
+            {
+            /*
+             * String values can sometimes come across in different
+             * forms; accept sets of differing values...
+             */
+
+             if (tag != IPP_TAG_STRING &&
+                 (tag < IPP_TAG_TEXTLANG || tag > IPP_TAG_MIMETYPE))
+               return (IPP_ERROR);
+            }
+           else if (attr->value_tag != tag)
+             return (IPP_ERROR);
+
+           /*
+           * Finally, make sure we don't have too many elements in the
+           * attribute array...
+           */
+
            if (attr->num_values >= IPP_MAX_VALUES)
              return (IPP_ERROR);
          }
@@ -1026,21 +1090,45 @@ ippRead(http_t *http,           /* I - HTTP data */
                if (ipp_read(http, buffer, n) < n)
                  return (IPP_ERROR);
 
-                buffer[n] = '\0';
+                bufptr = buffer;
 
-                attr->values[attr->num_values].string.charset = strdup((char *)buffer);
+              /*
+               * text-with-language and name-with-language are composite
+               * values:
+               *
+               *    charset-length
+               *    charset
+               *    text-length
+               *    text
+               */
 
-               if (ipp_read(http, buffer, 2) < 2)
-                 return (IPP_ERROR);
+               n = (bufptr[0] << 8) | bufptr[1];
 
-               n = (buffer[0] << 8) | buffer[1];
+                attr->values[attr->num_values].string.charset = calloc(n + 1, 1);
 
-               if (ipp_read(http, buffer, n) < n)
-                 return (IPP_ERROR);
+               memcpy(attr->values[attr->num_values].string.charset,
+                      bufptr + 2, n);
 
-                buffer[n] = '\0';
+                bufptr += 2 + n;
+               n = (bufptr[0] << 8) | bufptr[1];
 
-                attr->values[attr->num_values].string.text = strdup((char *)buffer);
+                attr->values[attr->num_values].string.text = calloc(n + 1, 1);
+
+               memcpy(attr->values[attr->num_values].string.text,
+                      bufptr + 2, n);
+
+               break;
+
+            default : /* Other unsupported values */
+                attr->values[attr->num_values].unknown.length = n;
+               if (n > 0)
+               {
+                 attr->values[attr->num_values].unknown.data = malloc(n);
+                 if (ipp_read(http, attr->values[attr->num_values].unknown.data, n) < n)
+                   return (IPP_ERROR);
+               }
+               else
+                 attr->values[attr->num_values].unknown.data = NULL;
                break;
          }
 
@@ -1057,6 +1145,9 @@ ippRead(http_t *http,             /* I - HTTP data */
 
     case IPP_DATA :
         break;
+
+    default :
+        break; /* anti-compiler-warning-code */
   }
 
   return (ipp->state);
@@ -1140,8 +1231,8 @@ ippWrite(http_t *http,            /* I - HTTP data */
 
         bufptr = buffer;
 
-       *bufptr++ = 1;
-       *bufptr++ = 0;
+       *bufptr++ = ipp->request.any.version[0];
+       *bufptr++ = ipp->request.any.version[1];
        *bufptr++ = ipp->request.any.op_status >> 8;
        *bufptr++ = ipp->request.any.op_status;
        *bufptr++ = ipp->request.any.request_id >> 24;
@@ -1159,6 +1250,10 @@ ippWrite(http_t *http,           /* I - HTTP data */
        ipp->current = ipp->attrs;
        ipp->curtag  = IPP_TAG_ZERO;
 
+        DEBUG_printf(("ippWrite: version=%d.%d\n", buffer[0], buffer[1]));
+       DEBUG_printf(("ippWrite: op_status=%04x\n", ipp->request.any.op_status));
+       DEBUG_printf(("ippWrite: request_id=%d\n", ipp->request.any.request_id));
+
        /*
         * If blocking is disabled, stop here...
        */
@@ -1193,7 +1288,8 @@ ippWrite(http_t *http,            /* I - HTTP data */
            *bufptr++ = attr->group_tag;
          }
 
-          n = strlen(attr->name);
+          if ((n = strlen(attr->name)) > (sizeof(buffer) - 3))
+           return (IPP_ERROR);
 
           DEBUG_printf(("ippWrite: writing value tag = %x\n", attr->value_tag));
           DEBUG_printf(("ippWrite: writing name = %d, \'%s\'\n", n, attr->name));
@@ -1204,12 +1300,23 @@ ippWrite(http_t *http,          /* I - HTTP data */
          memcpy(bufptr, attr->name, n);
          bufptr += n;
 
-         switch (attr->value_tag)
+         switch (attr->value_tag & ~IPP_TAG_COPY)
          {
            case IPP_TAG_INTEGER :
            case IPP_TAG_ENUM :
                for (i = 0; i < attr->num_values; i ++)
                {
+                  if ((sizeof(buffer) - (bufptr - buffer)) < 9)
+                 {
+                    if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+                   {
+                     DEBUG_puts("ippWrite: Could not write IPP attribute...");
+                     return (IPP_ERROR);
+                   }
+
+                   bufptr = buffer;
+                 }
+
                  if (i)
                  {
                   /*
@@ -1234,6 +1341,17 @@ ippWrite(http_t *http,           /* I - HTTP data */
            case IPP_TAG_BOOLEAN :
                for (i = 0; i < attr->num_values; i ++)
                {
+                  if ((sizeof(buffer) - (bufptr - buffer)) < 6)
+                 {
+                    if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+                   {
+                     DEBUG_puts("ippWrite: Could not write IPP attribute...");
+                     return (IPP_ERROR);
+                   }
+
+                   bufptr = buffer;
+                 }
+
                  if (i)
                  {
                   /*
@@ -1274,6 +1392,17 @@ ippWrite(http_t *http,           /* I - HTTP data */
                                  attr->value_tag));
                    DEBUG_printf(("ippWrite: writing name = 0, \'\'\n"));
 
+                    if ((sizeof(buffer) - (bufptr - buffer)) < 3)
+                   {
+                      if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+                     {
+                       DEBUG_puts("ippWrite: Could not write IPP attribute...");
+                       return (IPP_ERROR);
+                     }
+
+                     bufptr = buffer;
+                   }
+
                     *bufptr++ = attr->value_tag;
                    *bufptr++ = 0;
                    *bufptr++ = 0;
@@ -1281,6 +1410,9 @@ ippWrite(http_t *http,            /* I - HTTP data */
 
                   n = strlen(attr->values[i].string.text);
 
+                  if (n > sizeof(buffer))
+                   return (IPP_ERROR);
+
                   DEBUG_printf(("ippWrite: writing string = %d, \'%s\'\n", n,
                                attr->values[i].string.text));
 
@@ -1305,6 +1437,17 @@ ippWrite(http_t *http,           /* I - HTTP data */
            case IPP_TAG_DATE :
                for (i = 0; i < attr->num_values; i ++)
                {
+                  if ((sizeof(buffer) - (bufptr - buffer)) < 16)
+                 {
+                    if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+                   {
+                     DEBUG_puts("ippWrite: Could not write IPP attribute...");
+                     return (IPP_ERROR);
+                   }
+
+                   bufptr = buffer;
+                 }
+
                  if (i)
                  {
                   /*
@@ -1327,6 +1470,17 @@ ippWrite(http_t *http,           /* I - HTTP data */
            case IPP_TAG_RESOLUTION :
                for (i = 0; i < attr->num_values; i ++)
                {
+                  if ((sizeof(buffer) - (bufptr - buffer)) < 14)
+                 {
+                    if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+                   {
+                     DEBUG_puts("ippWrite: Could not write IPP attribute...");
+                     return (IPP_ERROR);
+                   }
+
+                   bufptr = buffer;
+                 }
+
                  if (i)
                  {
                   /*
@@ -1356,6 +1510,17 @@ ippWrite(http_t *http,           /* I - HTTP data */
            case IPP_TAG_RANGE :
                for (i = 0; i < attr->num_values; i ++)
                {
+                  if ((sizeof(buffer) - (bufptr - buffer)) < 13)
+                 {
+                    if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+                   {
+                     DEBUG_puts("ippWrite: Could not write IPP attribute...");
+                     return (IPP_ERROR);
+                   }
+
+                   bufptr = buffer;
+                 }
+
                  if (i)
                  {
                   /*
@@ -1392,12 +1557,28 @@ ippWrite(http_t *http,          /* I - HTTP data */
                    * values with a zero-length name...
                    */
 
+                    if ((sizeof(buffer) - (bufptr - buffer)) < 3)
+                   {
+                      if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+                     {
+                       DEBUG_puts("ippWrite: Could not write IPP attribute...");
+                       return (IPP_ERROR);
+                     }
+
+                     bufptr = buffer;
+                   }
+
                     *bufptr++ = attr->value_tag;
                    *bufptr++ = 0;
                    *bufptr++ = 0;
                  }
 
-                  n = strlen(attr->values[i].string.charset);
+                  n = strlen(attr->values[i].string.charset) +
+                     strlen(attr->values[i].string.text) +
+                     4;
+
+                  if (n > sizeof(buffer))
+                   return (IPP_ERROR);
 
                   if ((sizeof(buffer) - (bufptr - buffer)) < (n + 2))
                  {
@@ -1410,12 +1591,60 @@ ippWrite(http_t *http,          /* I - HTTP data */
                    bufptr = buffer;
                  }
 
+                 /* Length of entire value */
                  *bufptr++ = n >> 8;
                  *bufptr++ = n;
+
+                 /* Length of charset */
+                  n = strlen(attr->values[i].string.charset);
+                 *bufptr++ = n >> 8;
+                 *bufptr++ = n;
+
+                 /* Charset */
                  memcpy(bufptr, attr->values[i].string.charset, n);
                  bufptr += n;
 
+                 /* Length of text */
                   n = strlen(attr->values[i].string.text);
+                 *bufptr++ = n >> 8;
+                 *bufptr++ = n;
+
+                 /* Text */
+                 memcpy(bufptr, attr->values[i].string.text, n);
+                 bufptr += n;
+               }
+               break;
+
+            default :
+               for (i = 0; i < attr->num_values; i ++)
+               {
+                 if (i)
+                 {
+                  /*
+                   * Arrays and sets are done by sending additional
+                   * values with a zero-length name...
+                   */
+
+                    if ((sizeof(buffer) - (bufptr - buffer)) < 3)
+                   {
+                      if (httpWrite(http, (char *)buffer, bufptr - buffer) < 0)
+                     {
+                       DEBUG_puts("ippWrite: Could not write IPP attribute...");
+                       return (IPP_ERROR);
+                     }
+
+                     bufptr = buffer;
+                   }
+
+                    *bufptr++ = attr->value_tag;
+                   *bufptr++ = 0;
+                   *bufptr++ = 0;
+                 }
+
+                  n = attr->values[i].unknown.length;
+
+                  if (n > sizeof(buffer))
+                   return (IPP_ERROR);
 
                   if ((sizeof(buffer) - (bufptr - buffer)) < (n + 2))
                  {
@@ -1428,10 +1657,16 @@ ippWrite(http_t *http,          /* I - HTTP data */
                    bufptr = buffer;
                  }
 
+                 /* Length of unknown value */
                  *bufptr++ = n >> 8;
                  *bufptr++ = n;
-                 memcpy(bufptr, attr->values[i].string.text, n);
-                 bufptr += n;
+
+                 /* Value */
+                 if (n > 0)
+                 {
+                   memcpy(bufptr, attr->values[i].unknown.data, n);
+                   bufptr += n;
+                 }
                }
                break;
          }
@@ -1475,6 +1710,9 @@ ippWrite(http_t *http,            /* I - HTTP data */
 
     case IPP_DATA :
         break;
+
+    default :
+        break; /* anti-compiler-warning-code */
   }
 
   return (ipp->state);
@@ -1492,12 +1730,25 @@ ippPort(void)
   struct servent *port;                /* Port number info */  
 
 
-  if ((server_port = getenv("IPP_PORT")) != NULL)
-    return (atoi(server_port));
+  if (ipp_port)
+    return (ipp_port);
+  else if ((server_port = getenv("IPP_PORT")) != NULL)
+    return (ipp_port = atoi(server_port));
   else if ((port = getservbyname("ipp", NULL)) == NULL)
-    return (IPP_PORT);
+    return (ipp_port = IPP_PORT);
   else
-    return (ntohs(port->s_port));
+    return (ipp_port = ntohs(port->s_port));
+}
+
+
+/*
+ * 'ippSetPort()' - Set the default port number.
+ */
+
+void
+ippSetPort(int p)              /* I - Port number to use */
+{
+  ipp_port = p;
 }
 
 
@@ -1512,7 +1763,7 @@ _ipp_add_attr(ipp_t *ipp, /* I - IPP request */
   ipp_attribute_t      *attr;  /* New attribute */
 
 
-  DEBUG_printf(("_ipp_add_attr(%08x, %d)\n", ipp, num_values));
+  DEBUG_printf(("_ipp_add_attr(%p, %d)\n", ipp, num_values));
 
   if (ipp == NULL || num_values < 0)
     return (NULL);
@@ -1532,10 +1783,60 @@ _ipp_add_attr(ipp_t *ipp,       /* I - IPP request */
 
   ipp->last = attr;
 
+  DEBUG_printf(("_ipp_add_attr(): %p\n", attr));
+
   return (attr);
 }
 
 
+/*
+ * '_ipp_free_attr()' - Free an attribute.
+ */
+
+void
+_ipp_free_attr(ipp_attribute_t *attr)  /* I - Attribute to free */
+{
+  int  i;                              /* Looping var */
+
+
+  DEBUG_printf(("_ipp_free_attr(): %p\n", attr));
+
+  switch (attr->value_tag)
+  {
+    case IPP_TAG_TEXT :
+    case IPP_TAG_NAME :
+    case IPP_TAG_KEYWORD :
+    case IPP_TAG_STRING :
+    case IPP_TAG_URI :
+    case IPP_TAG_URISCHEME :
+    case IPP_TAG_CHARSET :
+    case IPP_TAG_LANGUAGE :
+    case IPP_TAG_MIMETYPE :
+        for (i = 0; i < attr->num_values; i ++)
+         free(attr->values[i].string.text);
+       break;
+
+    case IPP_TAG_TEXTLANG :
+    case IPP_TAG_NAMELANG :
+        for (i = 0; i < attr->num_values; i ++)
+       {
+         if (attr->values[i].string.charset && i == 0)
+           free(attr->values[i].string.charset);
+         free(attr->values[i].string.text);
+       }
+       break;
+
+    default :
+        break; /* anti-compiler-warning-code */
+  }
+
+  if (attr->name != NULL)
+    free(attr->name);
+
+  free(attr);
+}
+
+
 /*
  * 'ipp_read()' - Semi-blocking read on a HTTP connection...
  */
@@ -1553,7 +1854,7 @@ ipp_read(http_t        *http,     /* I - Client connection */
   * Loop until all bytes are read...
   */
 
-  for (tbytes = 0; tbytes < length; tbytes += bytes, buffer += bytes)
+  for (tbytes = 0, bytes = 0; tbytes < length; tbytes += bytes, buffer += bytes)
     if ((bytes = httpRead(http, (char *)buffer, length - tbytes)) <= 0)
       break;
 
@@ -1561,10 +1862,13 @@ ipp_read(http_t        *http,   /* I - Client connection */
   * Return the number of bytes read...
   */
 
-  return (tbytes);
+  if (tbytes == 0 && bytes < 0)
+    return (-1);
+  else
+    return (tbytes);
 }
 
 
 /*
- * End of "$Id: ipp.c,v 1.31 2000/01/21 20:33:16 mike Exp $".
+ * End of "$Id: ipp.c,v 1.56 2001/04/15 11:52:43 mike Exp $".
  */