Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

wsp_headers.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * wsp_headers.c - Implement WSP PDU headers
00059  * 
00060  * References:
00061  *   WSP specification version 1.1
00062  *   RFC 2068, Hypertext Transfer Protocol HTTP/1.1
00063  *   RFC 2616, Hypertext Transfer Protocol HTTP/1.1
00064  *
00065  *   For push headers, WSP specification, June 2000 conformance release
00066  *
00067  * This file has two parts.  The first part decodes the request's headers
00068  * from WSP to HTTP.  The second part encodes the response's headers from
00069  * HTTP to WSP.
00070  *
00071  * Note that push header encoding and decoding are divided two parts:
00072  * first decoding and encoding numeric values and then packing these values
00073  * into WSP format and unpacking them from WSP format. This module contains
00074  * only packing and unpacking parts.
00075  *
00076  * Some functions are declared non-static to provide them for external use,
00077  * ie. the MMS encapsulation encoding and decoding routines implemented in 
00078  * other files.
00079  *
00080  * Richard Braakman
00081  * Stipe Tolj <stolj@wapme.de>
00082  */
00083 
00084 #include <string.h>
00085 #include <limits.h>
00086 #include <ctype.h>
00087 
00088 #include "gwlib/gwlib.h"
00089 #include "wsp.h"
00090 #include "wsp_headers.h"
00091 #include "wsp_strings.h"
00092 
00093 /*
00094  * get field value and return its type as predefined data types
00095  * There are three kinds of field encodings:
00096  *   WSP_FIELD_VALUE_NUL_STRING: 0-terminated string
00097  *   WSP_FIELD_VALUE_ENCODED: short integer, range 0-127
00098  *   WSP_FIELD_VALUE_DATA: octet string defined by length
00099  * The function will return one of those values, and modify the parse context
00100  * to make it easy to get the field data.
00101  *   WSP_FIELD_VALUE_NUL_STRING: Leave parsing position at start of string
00102  *   WSP_FIELD_VALUE_ENCODED: Put value in *well_known_value, leave
00103  *        parsing position after field value.
00104  *   WSP_FIELD_VALUE_DATA: Leave parsing position at start of data, and set
00105  *        a parse limit at the end of data.
00106  */
00107 int wsp_field_value(ParseContext *context, int *well_known_value)
00108 {
00109     int val;
00110     unsigned long len;
00111 
00112     val = parse_get_char(context);
00113     if (val > 0 && val < 31) {
00114         *well_known_value = -1;
00115         parse_limit(context, val);
00116         return WSP_FIELD_VALUE_DATA;
00117     } else if (val == 31) {
00118         *well_known_value = -1;
00119         len = parse_get_uintvar(context);
00120         parse_limit(context, len);
00121         return WSP_FIELD_VALUE_DATA;
00122     } else if (val > 127) {
00123         *well_known_value = val - 128;
00124         return WSP_FIELD_VALUE_ENCODED;
00125     } else if (val == WSP_QUOTE || val == '"') {  /* 127 */
00126         *well_known_value = -1;
00127         /* We already consumed the Quote */
00128         return WSP_FIELD_VALUE_NUL_STRING;
00129     } else {    /* implicite val == 0 */ 
00130         *well_known_value = -1;
00131         /* Un-parse the character we just read */
00132         parse_skip(context, -1);
00133         return WSP_FIELD_VALUE_NUL_STRING;
00134     }
00135 }
00136 
00137 /* Skip over a field_value as defined above. */
00138 void wsp_skip_field_value(ParseContext *context)
00139 {
00140     int val;
00141     int ret;
00142 
00143     ret = wsp_field_value(context, &val);
00144     if (ret == WSP_FIELD_VALUE_DATA) {
00145         parse_skip_to_limit(context);
00146         parse_pop_limit(context);
00147     }
00148 }
00149 
00150 /* Multi-octet-integer is defined in 8.4.2.1 */
00151 static long unpack_multi_octet_integer(ParseContext *context, long len)
00152 {
00153     long val = 0;
00154 
00155     if (len > (long) sizeof(val) || len < 0)
00156         return -1;
00157 
00158     while (len > 0) {
00159         val = val * 256 + parse_get_char(context);
00160         len--;
00161     }
00162 
00163     if (parse_error(context))
00164         return -1;
00165 
00166     return val;
00167 }
00168 
00169 /* This function is similar to field_value, but it is used at various
00170  * places in the grammar where we expect either an Integer-value
00171  * or some kind of NUL-terminated text.
00172  * 
00173  * Return values are just like field_value except that WSP_FIELD_VALUE_DATA
00174  * will not be returned.
00175  *
00176  * As a special case, we parse a 0-length Long-integer as an
00177  * WSP_FIELD_VALUE_NONE, so that we can distinguish between No-value
00178  * and an Integer-value of 0.  (A real integer 0 would be encoded as
00179  * a Short-integer; the definition of Long-integer seems to allow
00180  * 0-length integers, but the definition of Multi-octet-integer does
00181  * not, so this is an unclear area of the specification.)
00182  */
00183 int wsp_secondary_field_value(ParseContext *context, long *result)
00184 {
00185     int val;
00186     long length;
00187 
00188     val = parse_get_char(context);
00189     if (val == 0) {
00190         *result = 0;
00191         return WSP_FIELD_VALUE_NONE;
00192     } else if (val > 0 && val < 31) {
00193         *result = unpack_multi_octet_integer(context, val);
00194         return WSP_FIELD_VALUE_ENCODED;
00195     } else if (val == 31) {
00196         length = parse_get_uintvar(context);
00197         *result = unpack_multi_octet_integer(context, length);
00198         return WSP_FIELD_VALUE_ENCODED;
00199     } else if (val > 127) {
00200         *result = val - 128;
00201         return WSP_FIELD_VALUE_ENCODED;
00202     } else if (val == WSP_QUOTE) {  /* 127 */
00203         *result = -1;
00204         return WSP_FIELD_VALUE_NUL_STRING;
00205     } else {
00206         *result = -1;
00207         /* Un-parse the character we just read */
00208         parse_skip(context, -1);
00209         return WSP_FIELD_VALUE_NUL_STRING;
00210     }
00211 }
00212 
00213 /* Integer-value is defined in 8.4.2.3 */
00214 Octstr *wsp_unpack_integer_value(ParseContext *context)
00215 {
00216     Octstr *decoded;
00217     unsigned long value;
00218     int val;
00219 
00220     val = parse_get_char(context);
00221     if (val < 31) {
00222         value = unpack_multi_octet_integer(context, val);
00223     } else if (val > 127) {
00224         value = val - 128;
00225     } else {
00226         warning(0, "WSP headers: bad integer-value.");
00227         return NULL;
00228     }
00229 
00230     decoded = octstr_create("");
00231     octstr_append_decimal(decoded, value);
00232     return decoded;
00233 }
00234 
00235 /* Q-value is defined in 8.4.2.3 */
00236 static Octstr *convert_q_value(int q)
00237 {
00238     Octstr *result = NULL;
00239 
00240     /* When quality factor 0 and quality factors with one or two
00241      * decimal digits are encoded, they shall be multiplied by 100
00242      * and incremented by one, so that they encode as a one-octet
00243      * value in range 1-100. */
00244     if (q >= 1 && q <= 100) {
00245         q = q - 1;
00246         result = octstr_create("0.");
00247         octstr_append_char(result, (q / 10) + '0');
00248         if (q % 10 > 0)
00249             octstr_append_char(result, (q % 10) + '0');
00250         return result;
00251     }
00252 
00253     /* Three decimal quality factors shall be multiplied with 1000
00254      * and incremented by 100. */
00255     if (q > 100 && q <= 1000) {
00256         q = q - 100;
00257         result = octstr_create("0.");
00258         octstr_append_char(result, (q / 100) + '0');
00259         if (q % 100 > 0)
00260             octstr_append_char(result, (q / 10 % 10) + '0');
00261         if (q % 10 > 0)
00262             octstr_append_char(result, (q % 10) + '0');
00263         return result;
00264     }
00265 
00266     return NULL;
00267 }
00268 
00269 /* Q-value is defined in 8.4.2.3 */
00270 static Octstr *unpack_q_value(ParseContext *context)
00271 {
00272     int c, c2;
00273 
00274     c = parse_get_char(context);
00275     if (c < 0)
00276         return NULL;
00277 
00278     if (c & 0x80) {
00279         c2 = parse_get_char(context);
00280         if (c2 < 0 || (c2 & 0x80))
00281             return NULL;
00282         c = ((c & 0x7f) << 8) + c2;
00283     }
00284 
00285     return convert_q_value(c);
00286 }
00287 
00288 
00289 /* Version-value is defined in 8.4.2.3. Encoding-Version uses coding
00290  * defined in this chapter, see 8.4.2.70.  */
00291 Octstr *wsp_unpack_version_value(long value)
00292 {
00293     Octstr *result;
00294     int major, minor;
00295 
00296     major = ((value >> 4) & 0x7);
00297     minor = (value & 0xf);
00298 
00299     result = octstr_create("");
00300     octstr_append_char(result, major + '0');
00301     if (minor != 15) {
00302         octstr_append_char(result, '.');
00303         octstr_append_decimal(result, minor);
00304     }
00305 
00306     return result;
00307 }
00308 
00309 static Octstr *unpack_encoding_version(ParseContext *context)
00310 {
00311     int ch;
00312 
00313     ch = parse_get_char(context);
00314     if (ch < 128) {
00315         warning(0, "WSP: bad Encoding-Version value");
00316         return NULL;
00317     }
00318 
00319     return wsp_unpack_version_value(((long) ch) - 128);
00320 }
00321 
00322 
00323 /* Called with the parse limit set to the end of the parameter data,
00324  * and decoded containing the unpacked header line so far.
00325  * Parameter is defined in 8.4.2.4. */
00326 static int unpack_parameter(ParseContext *context, Octstr *decoded)
00327 {
00328     Octstr *parm = NULL;
00329     Octstr *value = NULL;
00330     int ret;
00331     long type;
00332     long val;
00333 
00334     ret = wsp_secondary_field_value(context, &type);
00335     if (parse_error(context) || ret == WSP_FIELD_VALUE_NONE) {
00336         warning(0, "bad parameter");
00337         goto error;
00338     }
00339 
00340     if (ret == WSP_FIELD_VALUE_ENCODED) {
00341         /* Typed-parameter */
00342         parm = wsp_parameter_to_string(type);
00343         if (!parm)
00344             warning(0, "Unknown parameter %02lx.", type);
00345     } else if (ret == WSP_FIELD_VALUE_NUL_STRING) {
00346         /* Untyped-parameter */
00347         parm = parse_get_nul_string(context);
00348         if (!parm)
00349             warning(0, "Format error in parameter.");
00350         type = -1;
00351         /* We treat Untyped-value as a special type.  Its format
00352          * Integer-value | Text-value is pretty similar to most
00353          * typed formats. */
00354     } else {
00355         panic(0, "Unknown secondary field value type %d.", ret);
00356     }
00357 
00358     if (type == 0x00) /* q */
00359         value = unpack_q_value(context);
00360     else {
00361         ret = wsp_secondary_field_value(context, &val);
00362         if (parse_error(context)) {
00363             warning(0, "bad parameter value");
00364             goto error;
00365         }
00366 
00367         if (ret == WSP_FIELD_VALUE_ENCODED) {
00368             switch (type) {
00369             case -1:  /* untyped: Integer-value */
00370             case 3:  /* type: Integer-value */
00371             case 8:  /* padding: Short-integer */
00372                 value = octstr_create("");
00373                 octstr_append_decimal(value, val);
00374                 break;
00375             case 0:  /* q, already handled above */
00376                 gw_assert(0);
00377                 break;
00378             case 1:  /* charset: Well-known-charset */
00379                 value = wsp_charset_to_string(val);
00380                 if (!value)
00381                     warning(0, "Unknown charset %04lx.", val);
00382                 break;
00383             case 2:  /* level: Version-value */
00384                 value = wsp_unpack_version_value(val);
00385                 break;
00386             case 5:  /* name: Text-string */
00387             case 6:  /* filename: Text-string */
00388                 warning(0, "Text-string parameter with integer encoding");
00389                 break;
00390             case 7:  /* differences: Field-name */
00391                 value = wsp_header_to_string(val);
00392                 if (!value)
00393                     warning(0, "Unknown differences header %02lx.", val);
00394                 break;
00395             default:
00396                 warning(0, "Unknown parameter encoding %02lx.",
00397                         type);
00398                 break;
00399             }
00400         } else if (ret == WSP_FIELD_VALUE_NONE) {
00401             value = octstr_create("");
00402         } else {
00403             gw_assert(ret == WSP_FIELD_VALUE_NUL_STRING);
00404             /* Text-value = No-value | Token-text | Quoted-string */
00405             value = parse_get_nul_string(context);
00406             if (!value)
00407                 warning(0, "Format error in parameter value.");
00408             else {
00409                 if (octstr_get_char(value, 0) == '"') {
00410                     /* Quoted-string */
00411                     octstr_append_char(value, '"');
00412                 } else { /* DAVI! */
00413                     octstr_insert(value, octstr_imm("\""), 0);
00414                     octstr_append_char(value, '"');
00415                 }
00416             }
00417         }
00418     }
00419 
00420     if (!parm || !value) {
00421         warning(0, "Skipping parameters");
00422         goto error;
00423     }
00424 
00425     octstr_append(decoded, octstr_imm("; "));
00426     octstr_append(decoded, parm);
00427     if (octstr_len(value) > 0) {
00428         octstr_append_char(decoded, '=');
00429         octstr_append(decoded, value);
00430     }
00431     octstr_destroy(parm);
00432     octstr_destroy(value);
00433     return 0;
00434 
00435 error:
00436     parse_skip_to_limit(context);
00437     octstr_destroy(parm);
00438     octstr_destroy(value);
00439     parse_set_error(context);
00440     return -1;
00441 }
00442 
00443 void wsp_unpack_all_parameters(ParseContext *context, Octstr *decoded)
00444 {
00445     int ret = 0;
00446 
00447     while (ret >= 0 && !parse_error(context) &&
00448            parse_octets_left(context) > 0) {
00449         ret = unpack_parameter(context, decoded);
00450     }
00451 }
00452 
00453 /* Unpack parameters in the format used by credentials and challenge,
00454  * which differs from the format used by all other HTTP headers. */
00455 static void unpack_broken_parameters(ParseContext *context, Octstr *decoded)
00456 {
00457     int ret = 0;
00458     int first = 1;
00459     long pos;
00460 
00461     while (ret >= 0 && !parse_error(context) &&
00462        parse_octets_left(context) > 0) {
00463         pos = octstr_len(decoded);
00464         ret = unpack_parameter(context, decoded);
00465         if (ret >= 0) {
00466             if (first) {
00467                 /* Zap ';' */
00468                 octstr_delete(decoded, pos, 1);
00469                 first = 0;
00470             } else {
00471                 /* Replace ';' with ',' */
00472                 octstr_set_char(decoded, pos, first ? ' ' : ',');
00473             }
00474         }
00475     }
00476 }
00477 
00478 static void unpack_optional_q_value(ParseContext *context, Octstr *decoded)
00479 {
00480     if (parse_octets_left(context) > 0) {
00481         Octstr *qval = unpack_q_value(context);
00482         if (qval) {
00483             octstr_append(decoded, octstr_imm("; q="));
00484             octstr_append(decoded, qval);
00485             octstr_destroy(qval);
00486         } else
00487             warning(0, "Bad q-value");
00488     }
00489 }
00490 
00491 /* Date-value is defined in 8.4.2.3. */
00492 Octstr *wsp_unpack_date_value(ParseContext *context)
00493 {
00494     unsigned long timeval;
00495     int length;
00496 
00497     length = parse_get_char(context);
00498     if (length > 30) {
00499         warning(0, "WSP headers: bad date-value.");
00500         return NULL;
00501     }
00502 
00503     timeval = unpack_multi_octet_integer(context, length);
00504     if (timeval < 0) {
00505         warning(0, "WSP headers: cannot unpack date-value.");
00506         return NULL;
00507     }
00508 
00509     return date_format_http(timeval);
00510 }
00511 
00512 /* Accept-general-form is defined in 8.4.2.7 */
00513 Octstr *wsp_unpack_accept_general_form(ParseContext *context)
00514 {
00515     Octstr *decoded = NULL;
00516     int ret;
00517     long val;
00518 
00519     /* The definition for Accept-general-form looks quite complicated,
00520      * but the "Q-token Q-value" part fits the normal expansion of
00521      * Parameter, so it simplifies to:
00522      *  Value-length Media-range *(Parameter)
00523      * and we've already parsed Value-length.
00524      */
00525 
00526     /* We use this function to parse content-general-form too,
00527      * because its definition of Media-type is identical to Media-range.
00528      */
00529 
00530     ret = wsp_secondary_field_value(context, &val);
00531     if (parse_error(context) || ret == WSP_FIELD_VALUE_NONE) {
00532         warning(0, "bad media-range or media-type");
00533         return NULL;
00534     }
00535 
00536     if (ret == WSP_FIELD_VALUE_ENCODED) {
00537         decoded = wsp_content_type_to_string(val);
00538         if (!decoded) {
00539             warning(0, "Unknown content type 0x%02lx.", val);
00540             return NULL;
00541         }
00542     } else if (ret == WSP_FIELD_VALUE_NUL_STRING) {
00543         decoded = parse_get_nul_string(context);
00544         if (!decoded) {
00545             warning(0, "Format error in content type");
00546             return NULL;
00547         }
00548     } else {
00549         panic(0, "Unknown secondary field value type %d.", ret);
00550     }
00551 
00552     wsp_unpack_all_parameters(context, decoded);
00553     return decoded;
00554 }
00555 
00556 /* Accept-charset-general-form is defined in 8.4.2.8 */
00557 Octstr *wsp_unpack_accept_charset_general_form(ParseContext *context)
00558 {
00559     Octstr *decoded = NULL;
00560     int ret;
00561     long val;
00562 
00563     ret = wsp_secondary_field_value(context, &val);
00564     if (parse_error(context) || ret == WSP_FIELD_VALUE_NONE) {
00565         warning(0, "Bad accept-charset-general-form");
00566         return NULL;
00567     }
00568 
00569     if (ret == WSP_FIELD_VALUE_ENCODED) {
00570         decoded = wsp_charset_to_string(val);
00571         if (!decoded) {
00572             warning(0, "Unknown character set %04lx.", val);
00573             return NULL;
00574         }
00575     } else if (ret == WSP_FIELD_VALUE_NUL_STRING) {
00576         decoded = parse_get_nul_string(context);
00577         if (!decoded) {
00578             warning(0, "Format error in accept-charset");
00579             return NULL;
00580         }
00581     } else {
00582         panic(0, "Unknown secondary field value type %d.", ret);
00583     }
00584 
00585     unpack_optional_q_value(context, decoded);
00586     return decoded;
00587 }
00588 
00589 /* Accept-language-general-form is defined in 8.4.2.10 */
00590 static Octstr *unpack_accept_language_general_form(ParseContext *context)
00591 {
00592     Octstr *decoded = NULL;
00593     int ret;
00594     long val;
00595 
00596     ret = wsp_secondary_field_value(context, &val);
00597     if (parse_error(context) || ret == WSP_FIELD_VALUE_NONE) {
00598         warning(0, "Bad accept-language-general-form");
00599         return NULL;
00600     }
00601 
00602     if (ret == WSP_FIELD_VALUE_ENCODED) {
00603         /* Any-language is handled by a special entry in the
00604          * language table. */
00605         decoded = wsp_language_to_string(val);
00606         if (!decoded) {
00607             warning(0, "Unknown language %02lx.", val);
00608             return NULL;
00609         }
00610     } else if (ret == WSP_FIELD_VALUE_NUL_STRING) {
00611         decoded = parse_get_nul_string(context);
00612         if (!decoded) {
00613             warning(0, "Format error in accept-language");
00614             return NULL;
00615         }
00616     } else {
00617         panic(0, "Unknown secondary field value type %d.", ret);
00618     }
00619 
00620     unpack_optional_q_value(context, decoded);
00621     return decoded;
00622 }
00623 
00624 /* Credentials is defined in 8.4.2.5 */
00625 static Octstr *unpack_credentials(ParseContext *context)
00626 {
00627     Octstr *decoded = NULL;
00628     int val;
00629 
00630     val = parse_peek_char(context);
00631 
00632     if (val == BASIC_AUTHENTICATION) {
00633         Octstr *userid, *password;
00634 
00635         parse_skip(context, 1);
00636 
00637         userid = parse_get_nul_string(context);
00638         password = parse_get_nul_string(context);
00639 
00640         if (parse_error(context)) {
00641             octstr_destroy(userid);
00642             octstr_destroy(password);
00643         } else {
00644             /* Create the user-pass cookie */
00645             decoded = octstr_duplicate(userid);
00646             octstr_append_char(decoded, ':');
00647             octstr_append(decoded, password);
00648 
00649             /* XXX Deal with cookies that overflow the 76-per-line
00650              * limit of base64.  Either go through and zap all
00651              * CR LF sequences, or give the conversion function
00652              * a flag or something to leave them out. */
00653             octstr_binary_to_base64(decoded);
00654 
00655             /* Zap the CR LF at the end */
00656             octstr_delete(decoded, octstr_len(decoded) - 2, 2);
00657 
00658             octstr_insert_data(decoded, 0, "Basic ", 6);
00659 
00660             octstr_destroy(userid);
00661             octstr_destroy(password);
00662         }
00663     } else if (val >= 32 && val < 128) {
00664         /* Generic authentication scheme */
00665         decoded = parse_get_nul_string(context);
00666         if (decoded)
00667             unpack_broken_parameters(context, decoded);
00668     }
00669 
00670     if (!decoded)
00671         warning(0, "Cannot parse credentials.");
00672 
00673     return decoded;
00674 }
00675 
00676 /* Credentials is defined in 8.4.2.5 
00677  * but as Proxy-Authentication is to be used by kannel, 
00678  * a simplier to parse version is used here */
00679 static Octstr *proxy_unpack_credentials(ParseContext *context)
00680 {
00681     Octstr *decoded = NULL;
00682     int val;
00683 
00684     val = parse_peek_char(context);
00685 
00686     if (val == BASIC_AUTHENTICATION) {
00687         Octstr *userid, *password;
00688 
00689         parse_skip(context, 1);
00690 
00691         userid = parse_get_nul_string(context);
00692         password = parse_get_nul_string(context);
00693 
00694         if (parse_error(context)) {
00695             octstr_destroy(userid);
00696             octstr_destroy(password);
00697         } else {
00698             /* Create the user-pass cookie */
00699             decoded = octstr_duplicate(userid);
00700             octstr_append_char(decoded, ':');
00701             octstr_append(decoded, password);
00702             octstr_destroy(userid);
00703             octstr_destroy(password);
00704         }
00705     } else if (val >= 32 && val < 128) {
00706         /* Generic authentication scheme */
00707         decoded = parse_get_nul_string(context);
00708         if (decoded)
00709             unpack_broken_parameters(context, decoded);
00710     }
00711 
00712     if (!decoded)
00713         warning(0, "Cannot parse credentials.");
00714 
00715     return decoded;
00716 }
00717 
00718 /* Challenge is defined in 8.4.2.5 */
00719 static Octstr *unpack_challenge(ParseContext *context)
00720 {
00721     Octstr *decoded = NULL;
00722     Octstr *realm_value = NULL;
00723     int val;
00724 
00725     val = parse_peek_char(context);
00726     if (val == BASIC_AUTHENTICATION) {
00727         parse_skip(context, 1);
00728         realm_value = parse_get_nul_string(context);
00729         if (realm_value) {
00730             decoded = octstr_create("Basic realm=\"");
00731             octstr_append(decoded, realm_value);
00732             octstr_append_char(decoded, '"');
00733         }
00734     } else if (val >= 32 && val < 128) {
00735         /* Generic authentication scheme */
00736         decoded = parse_get_nul_string(context);
00737         realm_value = parse_get_nul_string(context);
00738         if (decoded && realm_value) {
00739             octstr_append(decoded,
00740                           octstr_imm(" realm=\""));
00741             octstr_append(decoded, realm_value);
00742             octstr_append_char(decoded, '"');
00743             if (parse_octets_left(context) > 0) {
00744                 /* Prepare for following parameter list */
00745                 octstr_append_char(decoded, ',');
00746             }
00747             unpack_broken_parameters(context, decoded);
00748         }
00749     }
00750 
00751     if (!decoded)
00752         warning(0, "Cannot parse challenge.");
00753 
00754     octstr_destroy(realm_value);
00755     return decoded;
00756 }
00757 
00758 /* Content-range is defined in 8.4.2.23 */
00759 static Octstr *unpack_content_range(ParseContext *context)
00760 {
00761     /* We'd have to figure out how to access the content range
00762      * length (i.e. user_data size) from here to parse this,
00763      * and I don't see why the _client_ would send this in any case. */
00764     warning(0, "Decoding of content-range not supported");
00765     return NULL;
00766 
00767     /*
00768         Octstr *decoded = NULL;
00769         unsigned long first_byte_pos, entity_length;
00770         unsigned long last_byte_pos;
00771      
00772         first_byte_pos = parse_get_uintvar(context);
00773         entity_length = parse_get_uintvar(context);
00774      
00775         if (parse_error(context)) {
00776             warning(0, "Cannot parse content-range header");
00777             return NULL;
00778         }
00779      
00780         decoded = octstr_create("bytes ");
00781         octstr_append_decimal(decoded, first_byte_pos);
00782         octstr_append_char(decoded, '-');
00783         octstr_append_decimal(decoded, last_byte_pos);
00784         octstr_append_char(decoded, '/');
00785         octstr_append_decimal(decoded, entity_length);
00786      
00787         return decoded;
00788     */
00789 }
00790 
00791 /* Field-name is defined in 8.4.2.6 */
00792 static Octstr *unpack_field_name(ParseContext *context)
00793 {
00794     Octstr *decoded = NULL;
00795     int ret;
00796     int val;
00797 
00798     ret = wsp_field_value(context, &val);
00799     if (parse_error(context) || ret == WSP_FIELD_VALUE_DATA) {
00800         warning(0, "Bad field-name encoding");
00801         return NULL;
00802     }
00803 
00804     if (ret == WSP_FIELD_VALUE_ENCODED) {
00805         decoded = wsp_header_to_string(val);
00806         if (!decoded) {
00807             warning(0, "Unknown field-name 0x%02x.", val);
00808             return NULL;
00809         }
00810     } else if (ret == WSP_FIELD_VALUE_NUL_STRING) {
00811         decoded = parse_get_nul_string(context);
00812         if (!decoded) {
00813             warning(0, "Bad field-name encoding");
00814             return NULL;
00815         }
00816     } else {
00817         panic(0, "Unknown field value type %d.", ret);
00818     }
00819 
00820     return decoded;
00821 }
00822 
00823 /* Cache-directive is defined in 8.4.2.15 */
00824 static Octstr *unpack_cache_directive(ParseContext *context)
00825 {
00826     Octstr *decoded = NULL;
00827     int ret;
00828     int val;
00829 
00830     ret = wsp_field_value(context, &val);
00831     if (parse_error(context) || ret == WSP_FIELD_VALUE_DATA) {
00832         warning(0, "Bad cache-directive");
00833         goto error;
00834     }
00835 
00836     if (ret == WSP_FIELD_VALUE_ENCODED) {
00837         decoded = wsp_cache_control_to_string(val);
00838         if (!decoded) {
00839             warning(0, "Bad cache-directive 0x%02x.", val);
00840             goto error;
00841         }
00842         octstr_append_char(decoded, '=');
00843         switch (val) {
00844         case WSP_CACHE_CONTROL_NO_CACHE:
00845         case WSP_CACHE_CONTROL_PRIVATE:
00846             if (parse_octets_left(context) == 0) {
00847                 warning(0, "Too short cache-directive");
00848                 goto error;
00849             }
00850             octstr_append_char(decoded, '"');
00851             do {
00852                 Octstr *fieldname = unpack_field_name(context);
00853                 if (!fieldname) {
00854                     warning(0, "Bad field name in cache directive");
00855                     goto error;
00856                 }
00857                 octstr_append(decoded, fieldname);
00858                 octstr_destroy(fieldname);
00859                 if (parse_octets_left(context) > 0) {
00860                     octstr_append_char(decoded, ',');
00861                     octstr_append_char(decoded, ' ');
00862                 }
00863             } while (parse_octets_left(context) > 0 &&
00864                      !parse_error(context));
00865             octstr_append_char(decoded, '"');
00866             break;
00867         case WSP_CACHE_CONTROL_MAX_AGE:
00868         case WSP_CACHE_CONTROL_MAX_STALE:
00869         case WSP_CACHE_CONTROL_MIN_FRESH:
00870             {
00871                 Octstr *seconds;
00872                 seconds = wsp_unpack_integer_value(context);
00873                 if (!seconds) {
00874                     warning(0, "Bad integer value in cache directive");
00875                     goto error;
00876                 }
00877                 octstr_append(decoded, seconds);
00878                 octstr_destroy(seconds);
00879             }
00880             break;
00881         default:
00882             warning(0, "Unexpected value 0x%02x in cache directive.", val);
00883             break;
00884         }
00885     } else if (ret == WSP_FIELD_VALUE_NUL_STRING) {
00886         /* XXX: WSP grammar seems wrong here.  It works out
00887          * to Token-text followed by Parameter.  But the
00888          * grammar in RFC2616 works out to a key = value
00889          * pair, i.e. only a Parameter. */
00890         decoded = parse_get_nul_string(context);
00891         if (!decoded) {
00892             warning(0, "Format error in cache-control.");
00893             return NULL;
00894         }
00895         /* Yes, the grammar allows only one */
00896         unpack_parameter(context, decoded);
00897     } else {
00898         panic(0, "Unknown field value type %d.", ret);
00899     }
00900 
00901     return decoded;
00902 
00903 error:
00904     octstr_destroy(decoded);
00905     return NULL;
00906 }
00907 
00908 /* Retry-after is defined in 8.4.2.44 */
00909 static Octstr *unpack_retry_after(ParseContext *context)
00910 {
00911     int selector;
00912 
00913     selector = parse_get_char(context);
00914     if (selector == ABSOLUTE_TIME) {
00915         return wsp_unpack_date_value(context);
00916     } else if (selector == RELATIVE_TIME) {
00917         return wsp_unpack_integer_value(context);
00918     } else {
00919         warning(0, "Cannot parse retry-after value.");
00920         return NULL;
00921     }
00922 }
00923 
00924 /* Disposition is defined in 8.4.2.53 */
00925 static Octstr *unpack_disposition(ParseContext *context)
00926 {
00927     Octstr *decoded = NULL;
00928     int selector;
00929 
00930     selector = parse_get_char(context) - 128;
00931     decoded = wsp_disposition_to_string(selector);
00932     if (!decoded) {
00933         warning(0, "Cannot parse content-disposition value.");
00934         return NULL;
00935     }
00936     wsp_unpack_all_parameters(context, decoded);
00937     return decoded;
00938 }
00939 
00940 /* Range-value is defined in 8.4.2.42 */
00941 static Octstr *unpack_range_value(ParseContext *context)
00942 {
00943     Octstr *decoded = NULL;
00944     int selector;
00945     unsigned long first_byte_pos, last_byte_pos, suffix_length;
00946 
00947     selector = parse_get_char(context);
00948     if (selector == BYTE_RANGE) {
00949         first_byte_pos = parse_get_uintvar(context);
00950         if (parse_error(context))
00951             goto error;
00952 
00953         decoded = octstr_create("bytes = ");
00954         octstr_append_decimal(decoded, first_byte_pos);
00955         octstr_append_char(decoded, '-');
00956 
00957         last_byte_pos = parse_get_uintvar(context);
00958         if (parse_error(context)) {
00959             /* last_byte_pos is optional */
00960             parse_clear_error(context);
00961         } else {
00962             octstr_append_decimal(decoded, last_byte_pos);
00963         }
00964     } else if (selector == SUFFIX_BYTE_RANGE) {
00965         suffix_length = parse_get_uintvar(context);
00966         if (parse_error(context))
00967             goto error;
00968 
00969         decoded = octstr_create("bytes = -");
00970         octstr_append_decimal(decoded, suffix_length);
00971     } else {
00972         goto error;
00973     }
00974 
00975     return decoded;
00976 
00977 error:
00978     warning(0, "Bad format for range-value.");
00979     octstr_destroy(decoded);
00980     return NULL;
00981 }
00982 
00983 /* Warning-value is defined in 8.4.2.51 */
00984 static Octstr *unpack_warning_value(ParseContext *context)
00985 {
00986     Octstr *decoded = NULL;
00987     Octstr *warn_code = NULL;
00988     Octstr *warn_agent = NULL;
00989     Octstr *warn_text = NULL;
00990     unsigned char quote = '"';
00991 
00992     warn_code = wsp_unpack_integer_value(context);
00993 
00994     warn_agent = parse_get_nul_string(context);
00995     if (warn_agent && octstr_get_char(warn_agent, 0) == WSP_QUOTE)
00996         octstr_delete(warn_agent, 0, 1);
00997 
00998     warn_text = parse_get_nul_string(context);
00999     if (warn_text && octstr_get_char(warn_text, 0) == WSP_QUOTE)
01000         octstr_delete(warn_text, 0, 1);
01001     if (octstr_get_char(warn_text, 0) != quote)
01002         octstr_insert_data(warn_text, 0, (char *)&quote, 1);
01003     if (octstr_get_char(warn_text, octstr_len(warn_text) - 1) != quote)
01004         octstr_append_char(warn_text, quote);
01005 
01006     if (parse_error(context) || !warn_agent || !warn_text)
01007         goto error;
01008 
01009     decoded = octstr_create("");
01010     octstr_append(decoded, warn_code);
01011     octstr_append_char(decoded, ' ');
01012     octstr_append(decoded, warn_agent);
01013     octstr_append_char(decoded, ' ');
01014     octstr_append(decoded, warn_text);
01015 
01016     octstr_destroy(warn_agent);
01017     octstr_destroy(warn_code);
01018     octstr_destroy(warn_text);
01019     return decoded;
01020 
01021 error:
01022     warning(0, "Bad format for warning-value.");
01023     octstr_destroy(warn_agent);
01024     octstr_destroy(warn_code);
01025     octstr_destroy(warn_text);
01026     octstr_destroy(decoded);
01027     return NULL;
01028 }
01029 
01030 void wsp_unpack_well_known_field(List *unpacked, int field_type,
01031                                  ParseContext *context)
01032 {
01033     int val, ret;
01034     unsigned char *headername = NULL;
01035     unsigned char *ch = NULL;
01036     Octstr *decoded = NULL;
01037 
01038     ret = wsp_field_value(context, &val);
01039     if (parse_error(context)) {
01040         warning(0, "Faulty header, skipping remaining headers.");
01041         parse_skip_to_limit(context);
01042         return;
01043     }
01044 
01045     headername = wsp_header_to_cstr(field_type);
01046     /* headername can still be NULL.  This is checked after parsing
01047      * the field value.  We want to parse the value before exiting,
01048      * so that we are ready for the next header. */
01049 
01050     /* The following code must set "ch" or "decoded" to a non-NULL
01051      * value if the header is valid. */
01052 
01053     if (ret == WSP_FIELD_VALUE_NUL_STRING) {
01054         /* We allow any header to have a text value, even if that
01055          * is not defined in the grammar.  Be generous in what
01056          * you accept, etc. */
01057         /* This covers Text-string, Token-Text, and Uri-value rules */
01058         decoded = parse_get_nul_string(context);
01059     } else if (ret == WSP_FIELD_VALUE_ENCODED) {
01060         switch (field_type) {
01061         case WSP_HEADER_ACCEPT:
01062         case WSP_HEADER_CONTENT_TYPE:
01063             ch = wsp_content_type_to_cstr(val);
01064             if (!ch)
01065                 warning(0, "Unknown content type 0x%02x.", val);
01066             break;
01067 
01068         case WSP_HEADER_ACCEPT_CHARSET:
01069             ch = wsp_charset_to_cstr(val);
01070             if (!ch)
01071                 warning(0, "Unknown charset 0x%02x.", val);
01072             break;
01073 
01074         case WSP_HEADER_ACCEPT_ENCODING:
01075         case WSP_HEADER_CONTENT_ENCODING:
01076             ch = wsp_encoding_to_cstr(val);
01077             if (!ch)
01078                 warning(0, "Unknown encoding 0x%02x.", val);
01079             break;
01080 
01081         case WSP_HEADER_ACCEPT_LANGUAGE:
01082         case WSP_HEADER_CONTENT_LANGUAGE:
01083             ch = wsp_language_to_cstr(val);
01084             if (!ch)
01085                 warning(0, "Unknown language 0x%02x.", val);
01086             break;
01087 
01088         case WSP_HEADER_ACCEPT_RANGES:
01089             ch = wsp_ranges_to_cstr(val);
01090             if (!ch)
01091                 warning(0, "Unknown ranges value 0x%02x.", val);
01092             break;
01093 
01094         case WSP_HEADER_AGE:
01095         case WSP_HEADER_CONTENT_LENGTH:
01096         case WSP_HEADER_MAX_FORWARDS:
01097             /* Short-integer version of Integer-value */
01098             decoded = octstr_create("");
01099             octstr_append_decimal(decoded, val);
01100             break;
01101 
01102         case WSP_HEADER_ALLOW:
01103         case WSP_HEADER_PUBLIC:
01104             ch = wsp_method_to_cstr(val);
01105             if (!ch) {
01106                 /* FIXME Support extended methods */
01107                 warning(0, "Unknown method 0x%02x.", val);
01108             }
01109             break;
01110 
01111         case WSP_HEADER_CACHE_CONTROL:
01112         case WSP_HEADER_CACHE_CONTROL_V13:
01113         case WSP_HEADER_CACHE_CONTROL_V14:
01114             ch = wsp_cache_control_to_cstr(val);
01115             if (!ch)
01116                 warning(0, "Unknown cache-control value 0x%02x.", val);
01117             break;
01118 
01119         case WSP_HEADER_CONNECTION:
01120             ch = wsp_connection_to_cstr(val);
01121             if (!ch)
01122                 warning(0, "Unknown connection value 0x%02x.", val);
01123             break;
01124 
01125 
01126         case WSP_HEADER_PRAGMA:
01127             if (val == 0)
01128                 ch = (unsigned char *)"no-cache";
01129             else
01130                 warning(0, "Unknown pragma value 0x%02x.", val);
01131             break;
01132 
01133         case WSP_HEADER_TRANSFER_ENCODING:
01134             ch = wsp_transfer_encoding_to_cstr(val);
01135             if (!ch)
01136                 warning(0, "Unknown transfer encoding value 0x%02x.", val);
01137             break;
01138 
01139         case WSP_HEADER_VARY:
01140             ch = wsp_header_to_cstr(val);
01141             if (!ch)
01142                 warning(0, "Unknown Vary field name 0x%02x.", val);
01143             break;
01144 
01145         case WSP_HEADER_WARNING:
01146             decoded = octstr_create("");
01147             octstr_append_decimal(decoded, val);
01148             break;
01149 
01150         case WSP_HEADER_BEARER_INDICATION:
01151              ch = wsp_bearer_indication_to_cstr(val);
01152              if (!ch)
01153                  warning(0, "Unknown Bearer-Indication field name 0x%02x.", val);
01154              break;
01155 
01156         case WSP_HEADER_ACCEPT_APPLICATION:
01157              ch = wsp_application_id_to_cstr(val);
01158              if (!ch)
01159                  warning(0, "Unknown Accept-Application field name 0x%02x.", val);
01160              break;
01161 
01162         default:
01163             if (headername) {
01164                 warning(0, "Did not expect short-integer with "
01165                         "'%s' header, skipping.", headername);
01166             }
01167             break;
01168         }
01169     } else if (ret == WSP_FIELD_VALUE_DATA) {
01170         switch (field_type) {
01171         case WSP_HEADER_ACCEPT:
01172         case WSP_HEADER_CONTENT_TYPE:
01173             /* Content-general-form and Accept-general-form
01174              * are defined separately in WSP, but their
01175              * definitions are equivalent. */
01176             decoded = wsp_unpack_accept_general_form(context);
01177             break;
01178 
01179         case WSP_HEADER_ACCEPT_CHARSET:
01180             decoded = wsp_unpack_accept_charset_general_form(context);
01181             break;
01182 
01183         case WSP_HEADER_ACCEPT_LANGUAGE:
01184             decoded = unpack_accept_language_general_form(context);
01185             break;
01186 
01187         case WSP_HEADER_AGE:
01188         case WSP_HEADER_CONTENT_LENGTH:
01189         case WSP_HEADER_MAX_FORWARDS:
01190         case WSP_HEADER_BEARER_INDICATION:
01191         case WSP_HEADER_ACCEPT_APPLICATION:
01192             /* Long-integer version of Integer-value */
01193             {
01194                 long l = unpack_multi_octet_integer(context,
01195                                                     parse_octets_left(context));
01196                 decoded = octstr_create("");
01197                 octstr_append_decimal(decoded, l);
01198             }
01199             break;
01200 
01201         case WSP_HEADER_AUTHORIZATION:
01202             decoded = unpack_credentials(context);
01203             break;
01204 
01205         case WSP_HEADER_PROXY_AUTHORIZATION:
01206             decoded = proxy_unpack_credentials(context);
01207             break;
01208 
01209         case WSP_HEADER_CACHE_CONTROL:
01210             decoded = unpack_cache_directive(context);
01211             break;
01212 
01213         case WSP_HEADER_CONTENT_MD5:
01214             decoded = parse_get_octets(context,
01215                                        parse_octets_left(context));
01216             octstr_binary_to_base64(decoded);
01217             /* Zap the CR LF sequence at the end */
01218             octstr_delete(decoded, octstr_len(decoded) - 2, 2);
01219             break;
01220 
01221         case WSP_HEADER_CONTENT_RANGE:
01222             decoded = unpack_content_range(context);
01223             break;
01224 
01225         case WSP_HEADER_DATE:
01226         case WSP_HEADER_EXPIRES:
01227         case WSP_HEADER_IF_MODIFIED_SINCE:
01228         case WSP_HEADER_IF_RANGE:
01229         case WSP_HEADER_IF_UNMODIFIED_SINCE:
01230         case WSP_HEADER_LAST_MODIFIED:
01231             /* Back up to get the length byte again */
01232             parse_skip(context, -1);
01233             decoded = wsp_unpack_date_value(context);
01234             break;
01235 
01236         case WSP_HEADER_PRAGMA:
01237             /* The value is a bare Parameter, without a preceding
01238              * header body.  unpack_parameter wasn't really
01239              * designed for this.  We work around it here. */
01240             decoded = octstr_create("");
01241             if (unpack_parameter(context, decoded) < 0) {
01242                 octstr_destroy(decoded);
01243                 decoded = NULL;
01244             } else {
01245                 /* Remove the leading "; " */
01246                 octstr_delete(decoded, 0, 2);
01247             }
01248             break;
01249 
01250         case WSP_HEADER_PROXY_AUTHENTICATE:
01251         case WSP_HEADER_WWW_AUTHENTICATE:
01252             decoded = unpack_challenge(context);
01253             break;
01254 
01255         case WSP_HEADER_RANGE:
01256             decoded = unpack_range_value(context);
01257             break;
01258 
01259         case WSP_HEADER_RETRY_AFTER:
01260             decoded = unpack_retry_after(context);
01261             break;
01262 
01263         case WSP_HEADER_WARNING:
01264             decoded = unpack_warning_value(context);
01265             break;
01266 
01267         case WSP_HEADER_CONTENT_DISPOSITION:
01268             decoded = unpack_disposition(context);
01269             break;
01270 
01271         case WSP_HEADER_ENCODING_VERSION:
01272             decoded = unpack_encoding_version(context);
01273             break;
01274 
01275         default:
01276             if (headername) {
01277                 warning(0, "Did not expect value-length with "
01278                         "'%s' header, skipping.", headername);
01279             }
01280             break;
01281         }
01282         if (headername && parse_octets_left(context) > 0) {
01283             warning(0, "WSP: %s: skipping %ld trailing octets.",
01284                     headername, parse_octets_left(context));
01285         }
01286         parse_skip_to_limit(context);
01287         parse_pop_limit(context);
01288     } else {
01289         panic(0, "Unknown field-value type %d.", ret);
01290     }
01291 
01292     if (ch == NULL && decoded != NULL)
01293         ch = (unsigned char *)octstr_get_cstr(decoded);
01294     if (ch == NULL)
01295         goto value_error;
01296 
01297     if (!headername) {
01298         warning(0, "Unknown header number 0x%02x.", field_type);
01299         goto value_error;
01300     }
01301 
01302     http_header_add(unpacked, (char *)headername,(char *) ch);
01303     octstr_destroy(decoded);
01304     return;
01305 
01306 value_error:
01307     warning(0, "Skipping faulty header.");
01308     octstr_destroy(decoded);
01309 }
01310 
01311 void wsp_unpack_app_header(List *unpacked, ParseContext *context)
01312 {
01313     Octstr *header = NULL;
01314     Octstr *value = NULL;
01315 
01316     header = parse_get_nul_string(context);
01317     value = parse_get_nul_string(context);
01318 
01319     if (header && value) {
01320         http_header_add(unpacked, octstr_get_cstr(header),
01321                         octstr_get_cstr(value));
01322     }
01323 
01324     if (parse_error(context))
01325         warning(0, "Error parsing application-header.");
01326 
01327     octstr_destroy(header);
01328     octstr_destroy(value);
01329 }
01330 
01331 List *wsp_headers_unpack(Octstr *headers, int content_type_present)
01332 {
01333     ParseContext *context;
01334     int byte;
01335     List *unpacked;
01336     int code_page;
01337 
01338     unpacked = http_create_empty_headers();
01339     context = parse_context_create(headers);
01340 
01341     if (octstr_len(headers) > 0) {
01342         debug("wsp", 0, "WSP: decoding headers:");
01343         octstr_dump(headers, 0);
01344     }
01345 
01346     if (content_type_present)
01347         wsp_unpack_well_known_field(unpacked,
01348                                     WSP_HEADER_CONTENT_TYPE, context);
01349 
01350     code_page = 1;   /* default */
01351 
01352     while (parse_octets_left(context) > 0 && !parse_error(context)) {
01353         byte = parse_get_char(context);
01354 
01355         if (byte == 127 || (byte >= 1 && byte <= 31)) {
01356             if (byte == 127)
01357                 code_page = parse_get_char(context);
01358             else
01359                 code_page = byte;
01360             if (code_page == 1)
01361                 info(0, "Returning to code page 1 (default).");
01362             else {
01363                 warning(0, "Shift to unknown code page %d.",
01364                         code_page);
01365                 warning(0, "Will try to skip headers until "
01366                         "next known code page.");
01367             }
01368         } else if (byte >= 128) {  /* well-known-header */
01369             if (code_page == 1)
01370                 wsp_unpack_well_known_field(unpacked, byte - 128, context);
01371             else {
01372                 debug("wsp", 0, "Skipping field 0x%02x.", byte);
01373                 wsp_skip_field_value(context);
01374             }
01375         } else if (byte > 31 && byte < 127) {
01376             /* Un-parse the character we just read */
01377             parse_skip(context, -1);
01378             wsp_unpack_app_header(unpacked, context);
01379         } else {
01380             warning(0, "Unsupported token or header (start 0x%x)", byte);
01381             break;
01382         }
01383     }
01384 
01385     if (gwlist_len(unpacked) > 0) {
01386         long i;
01387 
01388         debug("wsp", 0, "WSP: decoded headers:");
01389         for (i = 0; i < gwlist_len(unpacked); i++) {
01390             Octstr *header = gwlist_get(unpacked, i);
01391             debug("wsp", 0, "%s", octstr_get_cstr(header));
01392         }
01393         debug("wsp", 0, "WSP: End of decoded headers.");
01394     }
01395 
01396     parse_context_destroy(context);
01397     return unpacked;
01398 }
01399 
01400 
01401 /**********************************************************************/
01402 /* Start of header packing code (HTTP to WSP)                         */
01403 /**********************************************************************/
01404 
01405 static int pack_accept(Octstr *packet, Octstr *value);
01406 static int pack_accept_charset(Octstr *packet, Octstr *value);
01407 static int pack_accept_encoding(Octstr *packet, Octstr *value);
01408 static int pack_accept_language(Octstr *packet, Octstr *value);
01409 static int pack_cache_control(Octstr *packet, Octstr *value);
01410 static int pack_challenge(Octstr *packet, Octstr *value);
01411 static int pack_connection(Octstr *packet, Octstr *value);
01412 static int pack_content_disposition(Octstr *packet, Octstr *value);
01413 static int pack_content_range(Octstr *packet, Octstr *value);
01414 static int pack_credentials(Octstr *packet, Octstr *value);
01415 static int pack_encoding(Octstr *packet, Octstr *value);
01416 static int pack_expires(Octstr *packet, Octstr *value);
01417 static int pack_field_name(Octstr *packet, Octstr *value);
01418 static int pack_if_range(Octstr *packet, Octstr *value);
01419 static int pack_language(Octstr *packet, Octstr *value);
01420 static int pack_md5(Octstr *packet, Octstr *value);
01421 static int pack_method(Octstr *packet, Octstr *value);
01422 static int pack_pragma(Octstr *packet, Octstr *value);
01423 static int pack_range(Octstr *packet, Octstr *value);
01424 static int pack_range_unit(Octstr *packet, Octstr *value);
01425 static int pack_transfer_encoding(Octstr *packet, Octstr *value);
01426 static int pack_uri(Octstr *packet, Octstr *value);
01427 static int pack_warning(Octstr *packet, Octstr *value);
01428 
01429 
01430 
01431 /* these are used in MMS encapsulation code too */
01432 
01433 struct headerinfo headerinfo[] =
01434     {
01435         { WSP_HEADER_ACCEPT, pack_accept, LIST },
01436         { WSP_HEADER_ACCEPT_CHARSET, pack_accept_charset, LIST },
01437         { WSP_HEADER_ACCEPT_ENCODING, pack_accept_encoding, LIST },
01438         { WSP_HEADER_ACCEPT_LANGUAGE, pack_accept_language, LIST },
01439         { WSP_HEADER_ACCEPT_RANGES, pack_range_unit, LIST },
01440         { WSP_HEADER_AGE, wsp_pack_integer_string, 0 },
01441         /* pack_method is slightly too general because Allow is only
01442          * supposed to encode well-known-methods. */
01443         { WSP_HEADER_ALLOW, pack_method, LIST },
01444         { WSP_HEADER_AUTHORIZATION, pack_credentials, BROKEN_LIST },
01445         { WSP_HEADER_CACHE_CONTROL, pack_cache_control, LIST },
01446         { WSP_HEADER_CACHE_CONTROL_V13, pack_cache_control, LIST },
01447         { WSP_HEADER_CACHE_CONTROL_V14, pack_cache_control, LIST },
01448         { WSP_HEADER_CONNECTION, pack_connection, LIST },
01449         { WSP_HEADER_CONTENT_BASE, pack_uri, 0 },
01450         { WSP_HEADER_CONTENT_ENCODING, pack_encoding, LIST },
01451         { WSP_HEADER_CONTENT_LANGUAGE, pack_language, LIST },
01452         { WSP_HEADER_CONTENT_LENGTH, wsp_pack_integer_string, 0 },
01453         { WSP_HEADER_CONTENT_LOCATION, pack_uri, 0 },
01454         { WSP_HEADER_CONTENT_MD5, pack_md5, 0 },
01455         { WSP_HEADER_CONTENT_RANGE, pack_content_range, 0 },
01456         { WSP_HEADER_CONTENT_TYPE, wsp_pack_content_type, 0 },
01457         { WSP_HEADER_DATE, wsp_pack_date, 0 },
01458         { WSP_HEADER_ETAG, wsp_pack_text, 0 },
01459         { WSP_HEADER_EXPIRES, pack_expires, 0 },
01460         { WSP_HEADER_FROM, wsp_pack_text, 0 },
01461         { WSP_HEADER_HOST, wsp_pack_text, 0 },
01462         { WSP_HEADER_IF_MODIFIED_SINCE, wsp_pack_date, 0 },
01463         { WSP_HEADER_IF_MATCH, wsp_pack_text, 0 },
01464         { WSP_HEADER_IF_NONE_MATCH, wsp_pack_text, 0 },
01465         { WSP_HEADER_IF_RANGE, pack_if_range, 0 },
01466         { WSP_HEADER_IF_UNMODIFIED_SINCE, wsp_pack_date, 0 },
01467         { WSP_HEADER_LAST_MODIFIED, wsp_pack_date, 0 },
01468         { WSP_HEADER_LOCATION, pack_uri, 0 },
01469         { WSP_HEADER_MAX_FORWARDS, wsp_pack_integer_string, 0 },
01470         { WSP_HEADER_PRAGMA, pack_pragma, LIST },
01471         { WSP_HEADER_PROXY_AUTHENTICATE, pack_challenge, BROKEN_LIST },
01472         { WSP_HEADER_PROXY_AUTHORIZATION, pack_credentials, BROKEN_LIST },
01473         { WSP_HEADER_PUBLIC, pack_method, LIST },
01474         { WSP_HEADER_RANGE, pack_range, 0 },
01475         { WSP_HEADER_REFERER, pack_uri, 0 },
01476         { WSP_HEADER_RETRY_AFTER, wsp_pack_retry_after, 0 },
01477         { WSP_HEADER_SERVER, wsp_pack_text, 0 },
01478         { WSP_HEADER_TRANSFER_ENCODING, pack_transfer_encoding, LIST },
01479         { WSP_HEADER_UPGRADE, wsp_pack_text, LIST },
01480         { WSP_HEADER_USER_AGENT, wsp_pack_text, 0 },
01481         { WSP_HEADER_VARY, pack_field_name, LIST },
01482         { WSP_HEADER_VIA, wsp_pack_text, LIST },
01483         { WSP_HEADER_WARNING, pack_warning, LIST },
01484         { WSP_HEADER_WWW_AUTHENTICATE, pack_challenge, BROKEN_LIST },
01485         { WSP_HEADER_CONTENT_DISPOSITION, pack_content_disposition, 0 },
01486         { WSP_HEADER_PUSH_FLAG, wsp_pack_integer_string, 0},
01487         { WSP_HEADER_X_WAP_CONTENT_URI, pack_uri, 0},
01488         { WSP_HEADER_X_WAP_INITIATOR_URI, pack_uri, 0},
01489         { WSP_HEADER_X_WAP_APPLICATION_ID, wsp_pack_integer_string, 0},
01490         { WSP_HEADER_CONTENT_ID, wsp_pack_quoted_text, 0},
01491         { WSP_HEADER_ENCODING_VERSION, wsp_pack_version_value, 0 }
01492         // DAVI { WSP_HEADER_SET_COOKIE, pack_version_value, 0 }
01493     };
01494 
01495 static Parameter *parm_create(Octstr *key, Octstr *value)
01496 {
01497     Parameter *parm;
01498 
01499     parm = gw_malloc(sizeof(*parm));
01500     parm->key = key;
01501     parm->value = value;
01502     return parm;
01503 }
01504 
01505 static void parm_destroy(Parameter *parm)
01506 {
01507     if (parm == NULL)
01508         return;
01509 
01510     octstr_destroy(parm->key);
01511     octstr_destroy(parm->value);
01512     gw_free(parm);
01513 }
01514 
01515 void parm_destroy_item(void *parm)
01516 {
01517     parm_destroy(parm);
01518 }
01519 
01520 static Parameter *parm_parse(Octstr *value)
01521 {
01522     long pos;
01523     Octstr *key, *val;
01524 
01525     pos = octstr_search_char(value, '=', 0);
01526     if (pos > 0) {
01527         key = octstr_copy(value, 0, pos);
01528         val = octstr_copy(value, pos + 1, octstr_len(value) - pos);
01529         octstr_strip_blanks(key);
01530         octstr_strip_blanks(val);
01531     } else {
01532         key = octstr_duplicate(value);
01533         val = NULL;
01534     }
01535 
01536     return parm_create(key, val);
01537 }
01538 
01539 /* Many HTTP field elements can take parameters in a standardized
01540  * form: parameters appear after the main value, each is introduced
01541  * by a semicolon (;), and consists of a key=value pair or just
01542  * a key, where the key is a token and the value is either a token
01543  * or a quoted-string.
01544  * The main value itself is a series of tokens, separators, and
01545  * quoted-strings.
01546  *
01547  * This function will take such a field element, and remove all
01548  * parameters from it.  The parameters are returned as a List
01549  * of Parameter, where the value field is left NULL
01550  * if the parameter was just a key.
01551  * It returns NULL if there were no parameters.
01552  */
01553 List *wsp_strip_parameters(Octstr *value)
01554 {
01555     long pos;
01556     long len;
01557     int c;
01558     long end;
01559     List *parms;
01560     long firstparm;
01561 
01562     len = octstr_len(value);
01563     /* Find the start of the first parameter. */
01564     for (pos = 0; pos < len; pos++) {
01565         c = octstr_get_char(value, pos);
01566         if (c == ';')
01567             break;
01568         else if (c == '"')
01569             pos += http_header_quoted_string_len(value, pos) - 1;
01570     }
01571 
01572     if (pos >= len)
01573         return NULL;   /* no parameters */
01574 
01575     parms = gwlist_create();
01576     firstparm = pos;
01577 
01578     for (pos++; pos > 0 && pos < len; pos++) {
01579         Octstr *key = NULL;
01580         Octstr *val = NULL;
01581 
01582         end = octstr_search_char(value, '=', pos);
01583         if (end < 0)
01584             end = octstr_search_char(value, ';', pos);
01585         if (end < 0)
01586             end = octstr_len(value);
01587         key = octstr_copy(value, pos, end - pos);
01588         octstr_strip_blanks(key);
01589         pos = end;
01590 
01591         if (octstr_get_char(value, pos) == '=') {
01592             pos++;
01593             while (isspace(octstr_get_char(value, pos)))
01594                 pos++;
01595             if (octstr_get_char(value, pos) == '"')
01596                 end = pos + http_header_quoted_string_len(value, pos);
01597             else
01598                 end = octstr_search_char(value, ';', pos);
01599             if (end < 0)
01600                 end = octstr_len(value);
01601             val = octstr_copy(value, pos, end - pos);
01602             octstr_strip_blanks(val);
01603             pos = end;
01604             pos = octstr_search_char(value, ';', pos);
01605         }
01606 
01607         gwlist_append(parms, parm_create(key, val));
01608     }
01609 
01610     octstr_delete(value, firstparm, octstr_len(value) - firstparm);
01611     octstr_strip_blanks(value);
01612     return parms;
01613 }
01614 
01615 int wsp_pack_text(Octstr *packed, Octstr *text)
01616 {
01617     /* This check catches 0-length strings as well, because
01618      * octstr_get_char will return -1. */
01619     if (octstr_get_char(text, 0) >= 128 || octstr_get_char(text, 0) < 32)
01620         octstr_append_char(packed, WSP_QUOTE);
01621     octstr_append(packed, text);
01622     octstr_append_char(packed, 0);
01623     return 0;
01624 }
01625 
01626 /* Pack a string as quoted-text WAP WSP 203, Section 8.4.2.1 */
01627 int wsp_pack_quoted_text(Octstr *packed, Octstr *text)
01628 {
01629      octstr_append_char(packed, '"');
01630      octstr_append(packed,text);
01631      octstr_append_char(packed,0);
01632      return 0;
01633 }
01634 
01635 /* Pack text as Quoted-string if it starts with a " character.
01636  * Pack it as Text-string otherwise. */
01637 static void pack_quoted_string(Octstr *packed, Octstr *text)
01638 {
01639     octstr_append(packed, text);
01640     if (octstr_get_char(text, octstr_len(text) - 1) == '"' &&
01641         octstr_get_char(text, 0) == '"')
01642         octstr_delete(packed, octstr_len(packed) - 1, 1);
01643     octstr_append_char(packed, 0);
01644 }
01645 
01646 /* Is this char in the 'separators' set defined by HTTP? */
01647 static int is_separator_char(int c)
01648 {
01649     switch (c) {
01650     case '(':
01651     case ')':
01652     case '<':
01653     case '>':
01654     case '@':
01655     case ',':
01656     case ';':
01657     case ':':
01658     case '\\':
01659     case '"':
01660     case '/':
01661     case '[':
01662     case ']':
01663     case '?':
01664     case '=':
01665     case '{':
01666     case '}':
01667     case 32:  /* SP */
01668     case 9:   /* HT */
01669         return 1;
01670     default:
01671         return 0;
01672     }
01673 }
01674 
01675 /* Is this char part of a 'token' as defined by HTTP? */
01676 static int is_token_char(int c)
01677 {
01678     return c >= 32 && c < 127 && !is_separator_char(c);
01679 }
01680 
01681 /* Is this string a 'token' as defined by HTTP? */
01682 static int is_token(Octstr *token)
01683 {
01684     return octstr_len(token) > 0 &&
01685            octstr_check_range(token, 0, octstr_len(token), is_token_char);
01686 }
01687 
01688 /* We represent qvalues as integers from 0 through 1000, rather than
01689  * as floating values. */
01690 static int parse_qvalue(Octstr *value)
01691 {
01692     int qvalue;
01693 
01694     if (value == NULL)
01695         return -1;
01696 
01697     if (!isdigit(octstr_get_char(value, 0)))
01698         return -1;
01699 
01700     qvalue = (octstr_get_char(value, 0) - '0') * 1000;
01701 
01702     if (octstr_get_char(value, 1) != '.')
01703         goto gotvalue;
01704 
01705     if (!isdigit(octstr_get_char(value, 2)))
01706         goto gotvalue;
01707     qvalue += (octstr_get_char(value, 2) - '0') * 100;
01708 
01709 
01710     if (!isdigit(octstr_get_char(value, 3)))
01711         goto gotvalue;
01712     qvalue += (octstr_get_char(value, 3) - '0') * 10;
01713 
01714     if (!isdigit(octstr_get_char(value, 4)))
01715         goto gotvalue;
01716     qvalue += (octstr_get_char(value, 4) - '0');
01717 
01718 gotvalue:
01719     if (qvalue < 0 || qvalue > 1000)
01720         return -1;
01721 
01722     return qvalue;
01723 }
01724 
01725 static int get_qvalue(List *parms, int default_qvalue)
01726 {
01727     long i;
01728     Parameter *parm;
01729     int qvalue;
01730 
01731     for (i = 0; i < gwlist_len(parms); i++) {
01732         parm = gwlist_get(parms, i);
01733         if (octstr_str_compare(parm->key, "q") == 0 ||
01734             octstr_str_compare(parm->key, "Q") == 0) {
01735             qvalue = parse_qvalue(parm->value);
01736             if (qvalue >= 0)
01737                 return qvalue;
01738         }
01739     }
01740 
01741     return default_qvalue;
01742 }
01743 
01744 static int pack_qvalue(Octstr *packed, int qvalue)
01745 {
01746     /* "Quality factor 1 is the default value and shall never
01747      * be sent." */
01748     if (qvalue == 1000)
01749         return -1;
01750 
01751     /* Remember that our qvalues are already multiplied by 1000. */
01752     if (qvalue % 10 == 0)
01753         qvalue = qvalue / 10 + 1;
01754     else
01755         qvalue = qvalue + 100;
01756     octstr_append_uintvar(packed, qvalue);
01757     return 0;
01758 }
01759 
01760 /* Pack value as a Value-length followed by the encoded value. */
01761 void wsp_pack_value(Octstr *packed, Octstr *encoded)
01762 {
01763     long len;
01764 
01765     len = octstr_len(encoded);
01766     if (len <= 30)
01767         octstr_append_char(packed, len);
01768     else {
01769         octstr_append_char(packed, 31);
01770         octstr_append_uintvar(packed, len);
01771     }
01772 
01773     octstr_append(packed, encoded);
01774 }
01775 
01776 void wsp_pack_long_integer(Octstr *packed, unsigned long integer)
01777 {
01778     long oldlen = octstr_len(packed);
01779     unsigned char octet;
01780     long len;
01781 
01782     if (integer == 0) {
01783     /* The Multi-octet-integer has to be at least 1 octet long. */
01784     octstr_append_char(packed, 1); /* length */
01785     octstr_append_char(packed, 0); /* value */
01786     return;
01787     }
01788 
01789     /* Encode it back-to-front, by repeatedly inserting
01790      * at the same position, because that's easier. */
01791     for (len = 0; integer != 0; integer >>= 8, len++) {
01792         octet = integer & 0xff;
01793         octstr_insert_data(packed, oldlen, (char *)&octet, 1);
01794     }
01795 
01796     octet = len;
01797     octstr_insert_data(packed, oldlen, (char *)&octet, 1);
01798 }
01799 
01800 void wsp_pack_short_integer(Octstr *packed, unsigned long integer)
01801 {
01802     gw_assert(integer <= MAX_SHORT_INTEGER);
01803 
01804     octstr_append_char(packed, integer + 0x80);
01805 }
01806 
01807 void wsp_pack_integer_value(Octstr *packed, unsigned long integer)
01808 {
01809     if (integer <= MAX_SHORT_INTEGER)
01810         wsp_pack_short_integer(packed, integer);
01811     else
01812         wsp_pack_long_integer(packed, integer);
01813 }
01814 
01815 int wsp_pack_integer_string(Octstr *packed, Octstr *value)
01816 {
01817     unsigned long integer;
01818     long pos;
01819     int c;
01820     int digit;
01821 
01822     integer = 0;
01823     for (pos = 0; pos < octstr_len(value); pos++) {
01824         c = octstr_get_char(value, pos);
01825         if (!isdigit(c))
01826             break;
01827         digit = c - '0';
01828         if (integer > ULONG_MAX / 10)
01829             goto overflow;
01830         integer *= 10;
01831         if (integer > ULONG_MAX - digit)
01832             goto overflow;
01833         integer += digit;
01834     }
01835 
01836     wsp_pack_integer_value(packed, integer);
01837     return 0;
01838 
01839 overflow:
01840     warning(0, "WSP: Number too large to handle: '%s'.",
01841             octstr_get_cstr(value));
01842     return -1;
01843 }
01844 
01845 
01846 int wsp_pack_version_value(Octstr *packed, Octstr *version)
01847 {
01848     long major, minor;
01849     long pos;
01850 
01851     pos = octstr_parse_long(&major, version, 0, 10);
01852     if (pos < 0 || major < 1 || major > 7)
01853         goto usetext;
01854 
01855     if (pos == octstr_len(version))
01856         minor = 15;
01857     else {
01858         if (octstr_get_char(version, pos) != '.')
01859             goto usetext;
01860         pos = octstr_parse_long(&minor, version, pos + 1, 10);
01861         if (pos != octstr_len(version) || minor < 0 || minor > 14)
01862             goto usetext;
01863     }
01864 
01865     wsp_pack_short_integer(packed, major << 4 | minor);
01866     return 0;
01867 
01868 usetext:
01869     wsp_pack_text(packed, version);
01870     return 0;
01871 }
01872 
01873 int wsp_pack_constrained_value(Octstr *packed, Octstr *text, long value)
01874 {
01875     if (value >= 0)
01876         wsp_pack_short_integer(packed, value);
01877     else
01878         wsp_pack_text(packed, text);
01879     return 0;
01880 }
01881 
01882 static void pack_parameter(Octstr *packed, Parameter *parm)
01883 {
01884     long keytoken;
01885     long tmp;
01886     long start;
01887 
01888     start = octstr_len(packed);
01889 
01890     /* Parameter = Typed-parameter | Untyped-parameter */
01891     /* keytoken = wsp_string_to_parameter(parm->key); */
01892     /* XXX this should obey what kind of WSP Encoding-Version the client is using */
01893     keytoken = wsp_string_to_versioned_parameter(parm->key, WSP_1_2);
01894    
01895     if (keytoken >= 0) {
01896         /* Typed-parameter = Well-known-parameter-token Typed-value */
01897         /* Well-known-parameter-token = Integer-value */
01898         wsp_pack_integer_value(packed, keytoken);
01899         /* Typed-value = Compact-value | Text-value */
01900         /* First try to pack as Compact-value or No-value.
01901          * If that fails, pack as Text-value. */
01902         if (parm->value == NULL) {
01903             octstr_append_char(packed, 0);  /* No-value */
01904             return;
01905         } else switch (keytoken) {
01906             case 0:   /* q */
01907                 tmp = parse_qvalue(parm->value);
01908                 if (tmp >= 0) {
01909                     if (pack_qvalue(packed, tmp) < 0)
01910                         octstr_delete(packed, start,
01911                                       octstr_len(packed) - start);
01912                     return;
01913                 }
01914                 break;
01915             case 1:  /* charset */
01916                 tmp = wsp_string_to_charset(parm->value);
01917                 if (tmp >= 0) {
01918                     wsp_pack_integer_value(packed, tmp);
01919                     return;
01920                 }
01921                 break;
01922             case 2:  /* level */
01923                 wsp_pack_version_value(packed, parm->value);
01924                 return;
01925             case 3:  /* type */
01926                 if (octstr_check_range(parm->value, 0,
01927                                        octstr_len(parm->value), 
01928                        gw_isdigit) &&
01929                     wsp_pack_integer_string(packed, parm->value) >= 0)
01930                     return;
01931                 break;
01932             case 5:  /* name */
01933             case 6:  /* filename */
01934                 break;
01935             case 7:  /* differences */
01936                 if (pack_field_name(packed, parm->value) >= 0)
01937                     return;
01938                 break;
01939             case 8:  /* padding */
01940                 if (octstr_parse_long(&tmp, parm->value, 0, 10)
01941                     == octstr_len(parm->value) &&
01942                     tmp >= 0 && tmp <= MAX_SHORT_INTEGER) {
01943                     wsp_pack_short_integer(packed, tmp);
01944                     return;
01945                 }
01946                 break;
01947             }
01948         pack_quoted_string(packed, parm->value);
01949     } else {
01950         /* Untyped-parameter = Token-text Untyped-value */
01951         wsp_pack_text(packed, parm->key);
01952         /* Untyped-value = Integer-value | Text-value */
01953         if (parm->value == NULL) {
01954             octstr_append_char(packed, 0);  /* No-value */
01955             return;
01956         }
01957         /* If we can pack as integer, do so. */
01958         if (octstr_parse_long(&tmp, parm->value, 0, 10)
01959             == octstr_len(parm->value)) {
01960             wsp_pack_integer_value(packed, tmp);
01961         } else {
01962             pack_quoted_string(packed, parm->value);
01963         }
01964     }
01965 }
01966 
01967 void wsp_pack_parameters(Octstr *packed, List *parms)
01968 {
01969     long i;
01970     Parameter *parm;
01971 
01972     for (i = 0; i < gwlist_len(parms); i++) {
01973         parm = gwlist_get(parms, i);
01974         pack_parameter(packed, parm);
01975     }
01976 }
01977 
01978 static int pack_uri(Octstr *packed, Octstr *value)
01979 {
01980     wsp_pack_text(packed, value);
01981     return 0;
01982 }
01983 
01984 static int pack_md5(Octstr *packed, Octstr *value)
01985 {
01986     Octstr *binary;
01987 
01988     binary = octstr_duplicate(value);
01989     octstr_base64_to_binary(binary);
01990 
01991     if (octstr_len(binary) != 16) {
01992         error(0, "WSP: MD5 value not 128 bits.");
01993         return -1;
01994     }
01995 
01996     octstr_append_char(packed, 16);
01997     octstr_append(packed, binary);
01998 
01999     octstr_destroy(binary);
02000 
02001     return 0;
02002 }
02003 
02004 /* Actually packs a "Value-length Challenge" */
02005 /* Relies on http_split_auth_value to have converted the entry to
02006  * the normal HTTP parameter format rather than the comma-separated
02007  * one used by challenge and credentials. */
02008 static int pack_challenge(Octstr *packed, Octstr *value)
02009 {
02010     Octstr *encoding = NULL;
02011     Octstr *scheme = NULL;
02012     Octstr *basic = octstr_imm("Basic");
02013     Octstr *realm = octstr_imm("realm");
02014     Octstr *parmstring = NULL;
02015     List *parms = NULL;
02016     Parameter *realmparm = NULL;
02017     long realmpos = -1;
02018     Octstr *realmval = NULL;
02019     long pos;
02020 
02021     encoding = octstr_create("");
02022 
02023     /* Get authentication scheme */
02024     for (pos = 0; pos < octstr_len(value); pos++) {
02025         if (!is_token_char(octstr_get_char(value, pos)))
02026             break;
02027     }
02028     scheme = octstr_copy(value, 0, pos);
02029     octstr_strip_blanks(scheme);
02030 
02031     /* Skip whitespace */
02032     while (isspace(octstr_get_char(value, pos)))
02033         pos++;
02034 
02035     if (octstr_case_compare(scheme, basic) == 0) {
02036         parmstring = octstr_copy(value, pos, octstr_len(value) - pos);
02037         realmparm = parm_parse(parmstring);
02038 
02039         octstr_append_char(encoding, BASIC_AUTHENTICATION);
02040         realmpos = octstr_len(encoding);
02041     } else {
02042         long i;
02043 
02044         wsp_pack_text(encoding, scheme);
02045         realmpos = octstr_len(encoding);
02046 
02047         /* Find the realm parameter and exclude it */
02048         parms = wsp_strip_parameters(value);
02049         for (i = 0; i < gwlist_len(parms); i++) {
02050             Parameter *parm = gwlist_get(parms, i);
02051             if (octstr_case_compare(realm, parm->key) == 0) {
02052                 realmparm = parm;
02053                 gwlist_delete(parms, i, 1);
02054                 break;
02055             }
02056         }
02057 
02058         wsp_pack_parameters(encoding, parms);
02059     }
02060 
02061     /*
02062      * In the WSP encoding we have to put the realm value first, but
02063      * with non-Basic challenges we don't know if it will come first
02064      * in the HTTP header.  So we just start parsing parameters, and
02065      * go back and insert the realm value later.  The same technique
02066      * is used for Basic authentication to simplify the code.
02067      */
02068 
02069     if (realmparm == NULL ||
02070         octstr_case_compare(realmparm->key, realm) != 0 ||
02071         realmparm->value == NULL)
02072         goto error;
02073 
02074     /* Zap quote marks */
02075     if (octstr_get_char(realmparm->value, 0) == '"' &&
02076         octstr_get_char(realmparm->value, octstr_len(realmparm->value) - 1) == '"') {
02077         octstr_delete(realmparm->value, 0, 1);
02078         octstr_delete(realmparm->value, octstr_len(realmparm->value) - 1, 1);
02079     }
02080 
02081     gw_assert(realmpos >= 0);
02082 
02083     realmval = octstr_create("");
02084     wsp_pack_text(realmval, realmparm->value);
02085     octstr_insert(encoding, realmval, realmpos);
02086 
02087     wsp_pack_value(packed, encoding);
02088 
02089     octstr_destroy(encoding);
02090     octstr_destroy(scheme);
02091     octstr_destroy(parmstring);
02092     parm_destroy(realmparm);
02093     gwlist_destroy(parms, parm_destroy_item);
02094     octstr_destroy(realmval);
02095     return 0;
02096 
02097 error:
02098     warning(0, "WSP: Cannot parse challenge.");
02099     octstr_destroy(encoding);
02100     octstr_destroy(scheme);
02101     octstr_destroy(parmstring);
02102     parm_destroy(realmparm);
02103     gwlist_destroy(parms, parm_destroy_item);
02104     octstr_destroy(realmval);
02105     return -1;
02106 }
02107 
02108 /* Actually packs a "Value-length Credentials" */
02109 /* Relies on http_split_auth_value to have converted the entry to
02110  * the normal HTTP parameter format rather than the comma-separated
02111  * one used by challenge and credentials. */
02112 static int pack_credentials(Octstr *packed, Octstr *value)
02113 {
02114     Octstr *encoding = NULL;
02115     Octstr *scheme = NULL;
02116     Octstr *basic = NULL;
02117     long pos;
02118 
02119     encoding = octstr_create("");
02120 
02121     /* Get authentication scheme */
02122     for (pos = 0; pos < octstr_len(value); pos++) {
02123         if (!is_token_char(octstr_get_char(value, pos)))
02124             break;
02125     }
02126     scheme = octstr_copy(value, 0, pos);
02127     octstr_strip_blanks(scheme);
02128 
02129     /* Skip whitespace */
02130     while (isspace(octstr_get_char(value, pos)))
02131         pos++;
02132 
02133     basic = octstr_imm("Basic");
02134     if (octstr_case_compare(scheme, basic) == 0) {
02135         Octstr *cookie;
02136         Octstr *userid;
02137         Octstr *password;
02138 
02139         octstr_append_char(encoding, BASIC_AUTHENTICATION);
02140 
02141         cookie = octstr_copy(value, pos, octstr_len(value) - pos);
02142         octstr_base64_to_binary(cookie);
02143         pos = octstr_search_char(cookie, ':', 0);
02144         if (pos < 0) {
02145             warning(0, "WSP: bad cookie in credentials '%s'.",
02146                     octstr_get_cstr(value));
02147             octstr_destroy(cookie);
02148             goto error;
02149         }
02150 
02151         userid = octstr_copy(cookie, 0, pos);
02152         password = octstr_copy(cookie, pos + 1, octstr_len(cookie) - pos);
02153         wsp_pack_text(encoding, userid);
02154         wsp_pack_text(encoding, password);
02155 
02156         octstr_destroy(cookie);
02157         octstr_destroy(userid);
02158         octstr_destroy(password);
02159     } else {
02160         List *parms;
02161 
02162         wsp_pack_text(encoding, scheme);
02163         parms = wsp_strip_parameters(value);
02164         wsp_pack_parameters(encoding, parms);
02165         gwlist_destroy(parms, parm_destroy_item);
02166     }
02167 
02168     wsp_pack_value(packed, encoding);
02169     octstr_destroy(encoding);
02170     octstr_destroy(scheme);
02171 
02172     return 0;
02173 
02174 error:
02175     octstr_destroy(encoding);
02176     octstr_destroy(scheme);
02177     return -1;
02178 }
02179 
02180 int wsp_pack_date(Octstr *packed, Octstr *value)
02181 {
02182     long timeval;
02183 
02184     /* If we get a negative timeval here, this means either
02185      * we're beyond the time_t 32 bit int positive border for the 
02186      * timestamp or we're really handling time before epoch. */
02187     timeval = date_parse_http(value);
02188     if (timeval == -1) {
02189         warning(0, "WSP headers: cannot decode date '%s'",
02190                 octstr_get_cstr(value));
02191         return -1;
02192     }
02193 
02194     /* We'll assume that we don't package time before epoch. */
02195     wsp_pack_long_integer(packed, (unsigned long) timeval);
02196     return 0;
02197 }
02198 
02199 static int pack_connection(Octstr *packed, Octstr *value)
02200 {
02201     return wsp_pack_constrained_value(packed, value,
02202                                       wsp_string_to_connection(value));
02203 }
02204 
02205 static int pack_encoding(Octstr *packed, Octstr *value)
02206 {
02207     return wsp_pack_constrained_value(packed, value,
02208                                       wsp_string_to_encoding(value));
02209 }
02210 
02211 static int pack_field_name(Octstr *packed, Octstr *value)
02212 {
02213     /* XXX we need to obey which WSP encoding-version to use */
02214     /* return pack_constrained_value(packed, value,
02215                                   wsp_string_to_header(value)); */
02216     return wsp_pack_constrained_value(packed, value,
02217                                       wsp_string_to_versioned_header(value, WSP_1_2));
02218 }
02219 
02220 static int pack_language(Octstr *packed, Octstr *value)
02221 {
02222     long language;
02223 
02224     /* Can't use pack_constrained_value here because
02225      * language does not necessarily fit in a Short-integer. */
02226     language = wsp_string_to_language(value);
02227     if (language >= 0)
02228         wsp_pack_integer_value(packed, language);
02229     else
02230         wsp_pack_text(packed, value);
02231     return 0;
02232 }
02233 
02234 /* Encode value as Well-known-method | Token-text */
02235 static int pack_method(Octstr *packed, Octstr *value)
02236 {
02237     /* In the future, we will need some way to refer to extended
02238      * method names negotiated for this session. */ 
02239     return wsp_pack_constrained_value(packed, value,
02240                                       wsp_string_to_method(value));
02241 }
02242 
02243 /* Encode value as Accept-ranges-value */
02244 static int pack_range_unit(Octstr *packed, Octstr *value)
02245 {
02246     return wsp_pack_constrained_value(packed, value,
02247                                       wsp_string_to_ranges(value));
02248 }
02249 
02250 /* Encode byte-range-spec | suffix-byte-range-spec as Range-value.
02251  * Return position after the parsed spec. */
02252 static int pack_range_value(Octstr *packed, Octstr *value, long pos)
02253 {
02254     long first_byte_pos;
02255     long last_byte_pos;
02256     long suffix_length;
02257     Octstr *encoding;
02258 
02259     while (isspace(octstr_get_char(value, pos)))
02260         pos++;
02261 
02262     if (isdigit(octstr_get_char(value, pos))) {
02263         /* byte-range-spec */
02264         pos = octstr_parse_long(&first_byte_pos, value, pos, 10);
02265         if (pos < 0 || first_byte_pos < 0)
02266             return -1;
02267 
02268         while (isspace(octstr_get_char(value, pos)))
02269             pos++;
02270 
02271         if (octstr_get_char(value, pos) != '-')
02272             return -1;
02273         pos++;
02274 
02275         while (isspace(octstr_get_char(value, pos)))
02276             pos++;
02277 
02278         if (isdigit(octstr_get_char(value, pos))) {
02279             pos = octstr_parse_long(&last_byte_pos, value, pos, 10);
02280             if (pos < 0 || last_byte_pos < 0)
02281                 return -1;
02282         } else {
02283             last_byte_pos = -1;
02284         }
02285 
02286         encoding = octstr_create("");
02287         octstr_append_char(encoding, BYTE_RANGE);
02288         octstr_append_uintvar(encoding, first_byte_pos);
02289         if (last_byte_pos >= 0)
02290             octstr_append_uintvar(encoding, last_byte_pos);
02291         wsp_pack_value(packed, encoding);
02292         octstr_destroy(encoding);
02293     } else if (octstr_get_char(value, pos) == '-') {
02294         /* suffix-byte-range-spec */
02295         pos++;
02296 
02297         pos = octstr_parse_long(&suffix_length, value, pos, 10);
02298         if (pos < 0 || suffix_length < 0)
02299             return -1;
02300 
02301         encoding = octstr_create("");
02302         octstr_append_char(encoding, SUFFIX_BYTE_RANGE);
02303         octstr_append_uintvar(encoding, suffix_length);
02304         wsp_pack_value(packed, encoding);
02305         octstr_destroy(encoding);
02306     } else
02307         return -1;
02308 
02309     return pos;
02310 }
02311 
02312 static int pack_transfer_encoding(Octstr *packed, Octstr *value)
02313 {
02314     return wsp_pack_constrained_value(packed, value,
02315                                       wsp_string_to_transfer_encoding(value));
02316 }
02317 
02318 /* Also used by pack_content_type  */
02319 static int pack_accept(Octstr *packed, Octstr *value)
02320 {
02321     List *parms;
02322     long media;
02323 
02324     parms = wsp_strip_parameters(value);
02325     /* XXX we need to obey which WSP encoding-version to use */
02326     /* media = wsp_string_to_content_type(value); */
02327     media = wsp_string_to_versioned_content_type(value, WSP_1_2);
02328 
02329     /* See if we can fit this in a Constrained-media encoding */
02330     if (parms == NULL && media <= MAX_SHORT_INTEGER) {
02331         wsp_pack_constrained_value(packed, value, media);
02332     } else {
02333         Octstr *encoding = octstr_create("");
02334 
02335         if (media >= 0)
02336             wsp_pack_integer_value(encoding, media);
02337         else
02338             wsp_pack_text(encoding, value);
02339 
02340         wsp_pack_parameters(encoding, parms);
02341         wsp_pack_value(packed, encoding);
02342         octstr_destroy(encoding);
02343     }
02344 
02345     gwlist_destroy(parms, parm_destroy_item);
02346     return 0;
02347 }
02348 
02349 static int pack_accept_charset(Octstr *packed, Octstr *value)
02350 {
02351     List *parms;
02352     long charset;
02353     long qvalue;
02354 
02355     parms = wsp_strip_parameters(value);
02356     charset = wsp_string_to_charset(value);
02357     qvalue = 1000;
02358     if (parms)
02359         qvalue = get_qvalue(parms, qvalue);
02360     gwlist_destroy(parms, parm_destroy_item);
02361 
02362     /* See if we can fit this in a Constrained-charset encoding */
02363     if (qvalue == 1000 && charset <= MAX_SHORT_INTEGER) {
02364         wsp_pack_constrained_value(packed, value, charset);
02365     } else {
02366         Octstr *encoding = octstr_create("");
02367 
02368         if (charset >= 0)
02369             wsp_pack_integer_value(encoding, charset);
02370         else
02371             wsp_pack_text(encoding, value);
02372 
02373         if (qvalue != 1000)
02374             pack_qvalue(encoding, qvalue);
02375         wsp_pack_value(packed, encoding);
02376         octstr_destroy(encoding);
02377     }
02378 
02379     return 0;
02380 }
02381 
02382 /* WSP 8.4.2.9 does not specify any way to encode parameters for
02383  * an Accept-encoding field, but RFC2616 allows q parameters.
02384  * We'll have to simplify: qvalue > 0 means encode it, qvalue == 0
02385  * means don't encode it.
02386  */
02387 static int pack_accept_encoding(Octstr *packed, Octstr *value)
02388 {
02389     List *parms;
02390     int qvalue;
02391 
02392     qvalue = 1000;   /* default */
02393 
02394     parms = wsp_strip_parameters(value);
02395     if (parms)
02396         qvalue = get_qvalue(parms, qvalue);
02397     gwlist_destroy(parms, parm_destroy_item);
02398 
02399     if (qvalue > 0) {
02400         if (qvalue < 1000)
02401             warning(0, "Cannot encode q-value in Accept-Encoding.");
02402         pack_encoding(packed, value);
02403     } else {
02404         warning(0, "Cannot encode q=0 in Accept-Encoding; skipping this encoding.");
02405         return -1;
02406     }
02407 
02408     return 0;
02409 }
02410 
02411 static int pack_accept_language(Octstr *packed, Octstr *value)
02412 {
02413     List *parms;
02414     long language;
02415     long qvalue;
02416 
02417     parms = wsp_strip_parameters(value);
02418     language = wsp_string_to_language(value);
02419     qvalue = 1000;
02420     if (parms)
02421         qvalue = get_qvalue(parms, qvalue);
02422     gwlist_destroy(parms, parm_destroy_item);
02423 
02424     /* See if we can fit this in a Constrained-language encoding. */
02425     /* Note that our language table already includes Any-language */
02426     if (qvalue == 1000 && language <= MAX_SHORT_INTEGER) {
02427         wsp_pack_constrained_value(packed, value, language);
02428     } else {
02429         Octstr *encoding = octstr_create("");
02430 
02431         if (language >= 0)
02432             wsp_pack_integer_value(encoding, language);
02433         else
02434             wsp_pack_text(encoding, value);
02435 
02436         if (qvalue != 1000)
02437             pack_qvalue(encoding, qvalue);
02438         wsp_pack_value(packed, encoding);
02439         octstr_destroy(encoding);
02440     }
02441 
02442     return 0;
02443 }
02444 
02445 static int pack_cache_control(Octstr *packed, Octstr *value)
02446 {
02447     Parameter *parm;
02448     long tmp;
02449 
02450     parm = parm_parse(value);
02451 
02452     if (parm->value == NULL) {
02453         wsp_pack_constrained_value(packed, value,
02454                                    wsp_string_to_cache_control(parm->key));
02455     } else {
02456         Octstr *encoding = octstr_create("");
02457 
02458         tmp = wsp_string_to_cache_control(parm->key);
02459         if (tmp < 0) {
02460             /* According to WSP 8.4.2.15, the format
02461              * is "Cache-extension Parameter", and
02462              * Cache-extension is a Token-text.
02463              * But in HTTP a cache-extension is of
02464              * the form token=value, which maps
02465              * nicely to Parameter.  So what is
02466              * this extra Token-text?  I decided to leave it blank.
02467              *  - Richard Braakman
02468              */
02469             wsp_pack_text(encoding, octstr_imm(""));
02470             pack_parameter(encoding, parm);
02471         } else {
02472             int done = 0;
02473             Octstr *value_encoding;
02474             List *names;
02475             Octstr *element;
02476 
02477             value_encoding = octstr_create("");
02478             switch (tmp) {
02479             case WSP_CACHE_CONTROL_NO_CACHE:
02480             case WSP_CACHE_CONTROL_PRIVATE:
02481                 if (octstr_get_char(parm->value, 0) == '"')
02482                     octstr_delete(parm->value, 0, 1);
02483                 if (octstr_get_char(parm->value, octstr_len(parm->value) - 1) == '"')
02484                     octstr_delete(parm->value, octstr_len(parm->value) - 1, 1);
02485                 names = http_header_split_value(parm->value);
02486                 while ((element = gwlist_consume(names))) {
02487                     pack_field_name(value_encoding, element);
02488                     octstr_destroy(element);
02489                 }
02490                 gwlist_destroy(names, octstr_destroy_item);
02491                 done = 1;
02492                 break;
02493 
02494             case WSP_CACHE_CONTROL_MAX_AGE:
02495             case WSP_CACHE_CONTROL_MAX_STALE:
02496             case WSP_CACHE_CONTROL_MIN_FRESH:
02497                 if (wsp_pack_integer_string(value_encoding, parm->value) >= 0)
02498                     done = 1;
02499                 break;
02500             }
02501 
02502             if (done) {
02503                 wsp_pack_short_integer(encoding, tmp);
02504                 octstr_append(encoding, value_encoding);
02505             } else {
02506                 /* See note above */
02507                 pack_parameter(encoding, parm);
02508             }
02509             octstr_destroy(value_encoding);
02510         }
02511 
02512         wsp_pack_value(packed, encoding);
02513         octstr_destroy(encoding);
02514     }
02515 
02516     parm_destroy(parm);
02517     return 1;
02518 }
02519 
02520 static int pack_content_disposition(Octstr *packed, Octstr *value)
02521 {
02522     List *parms;
02523     long disposition;
02524 
02525     parms = wsp_strip_parameters(value);
02526     disposition = wsp_string_to_disposition(value);
02527 
02528     if (disposition >= 0) {
02529         Octstr *encoding = octstr_create("");
02530 
02531         wsp_pack_short_integer(encoding, disposition);
02532         wsp_pack_parameters(encoding, parms);
02533         wsp_pack_value(packed, encoding);
02534         octstr_destroy(encoding);
02535     } else {
02536         warning(0, "WSP: Cannot encode Content-Disposition '%s'.",
02537                 octstr_get_cstr(value));
02538         goto error;
02539     }
02540 
02541     gwlist_destroy(parms, parm_destroy_item);
02542     return 0;
02543 
02544 error:
02545     gwlist_destroy(parms, parm_destroy_item);
02546     return -1;
02547 }
02548 
02549 static int pack_content_range(Octstr *packed, Octstr *value)
02550 {
02551     Octstr *bytes;
02552     long pos;
02553     long firstbyte, lastbyte, instancelen;
02554     Octstr *encoding;
02555 
02556     bytes = octstr_imm("bytes ");
02557     if (octstr_ncompare(value, bytes, octstr_len(bytes)) != 0)
02558         goto error;
02559 
02560     pos = octstr_len(bytes);
02561     while (isspace(octstr_get_char(value, pos)))
02562         pos++;
02563     if (octstr_get_char(value, pos) == '*')
02564         goto warning;
02565     pos = octstr_parse_long(&firstbyte, value, pos, 10);
02566     if (pos < 0)
02567         goto error;
02568 
02569     while (isspace(octstr_get_char(value, pos)))
02570         pos++;
02571     if (octstr_get_char(value, pos++) != '-')
02572         goto error;
02573 
02574     pos = octstr_parse_long(&lastbyte, value, pos, 10);
02575     if (pos < 0)
02576         goto error;
02577 
02578     while (isspace(octstr_get_char(value, pos)))
02579         pos++;
02580     if (octstr_get_char(value, pos++) != '/')
02581         goto error;
02582 
02583     while (isspace(octstr_get_char(value, pos)))
02584         pos++;
02585     if (octstr_get_char(value, pos) == '*')
02586         goto warning;
02587     pos = octstr_parse_long(&instancelen, value, pos, 10);
02588     if (pos < 0)
02589         goto error;
02590 
02591     /* XXX: If the range is valid but not representable,
02592      * or if it's invalid, then should we refrain from sending
02593      * anything at all?  It might pollute the client's cache. */
02594 
02595     if (lastbyte < firstbyte || instancelen < lastbyte) {
02596         warning(0, "WSP: Content-Range '%s' is invalid.",
02597                 octstr_get_cstr(value));
02598         return -1;
02599     }
02600 
02601     encoding = octstr_create("");
02602     octstr_append_uintvar(encoding, firstbyte);
02603     octstr_append_uintvar(encoding, instancelen);
02604     wsp_pack_value(packed, encoding);
02605     octstr_destroy(encoding);
02606 
02607     return 0;
02608 
02609 error:
02610     warning(0, "WSP: Cannot parse Content-Range '%s'.",
02611             octstr_get_cstr(value));
02612     return -1;
02613 
02614 warning:
02615     warning(0, "WSP: Cannot encode Content-Range '%s'.",
02616             octstr_get_cstr(value));
02617     return -1;
02618 }
02619 
02620 int wsp_pack_content_type(Octstr *packed, Octstr *value)
02621 {
02622     /* The expansion of Content-type-value works out to be
02623      * equivalent to Accept-value. */ 
02624     return pack_accept(packed, value);
02625 }
02626 
02627 static int pack_expires(Octstr *packed, Octstr *value)
02628 {
02629     int ret;
02630 
02631     ret = wsp_pack_date(packed, value);
02632 
02633     if (ret < 0) {
02634     /* Responses with an invalid Expires header should be treated
02635     as already expired.  If we just skip this header, then the client
02636     won't know that.  So we encode one with a date far in the past. */
02637     wsp_pack_long_integer(packed, LONG_AGO_VALUE);
02638     ret = 0;
02639     }
02640 
02641     return ret;
02642 }
02643 
02644 static int pack_if_range(Octstr *packed, Octstr *value)
02645 {
02646     if (octstr_get_char(value, 0) == '"' ||
02647         (octstr_get_char(value, 0) == 'W' &&
02648          octstr_get_char(value, 1) == '/')) {
02649         return wsp_pack_text(packed, value);   /* It's an etag */
02650     } else {
02651         return wsp_pack_date(packed, value);
02652     }
02653 }
02654 
02655 static int pack_pragma(Octstr *packed, Octstr *value)
02656 {
02657     Octstr *nocache;
02658 
02659     nocache = octstr_imm("no-cache");
02660     if (octstr_case_compare(value, nocache) == 0)
02661         wsp_pack_short_integer(packed, WSP_CACHE_CONTROL_NO_CACHE);
02662     else {
02663         Parameter *parm;
02664         Octstr *encoding;
02665 
02666         encoding = octstr_create("");
02667         parm = parm_parse(value);
02668         pack_parameter(encoding, parm);
02669         wsp_pack_value(packed, encoding);
02670         octstr_destroy(encoding);
02671         parm_destroy(parm);
02672     }
02673 
02674     return 0;
02675 }
02676 
02677 static int pack_range(Octstr *packed, Octstr *value)
02678 {
02679     Octstr *bytes = octstr_imm("bytes");
02680     long pos;
02681 
02682     if (octstr_ncompare(value, bytes, octstr_len(bytes)) != 0
02683         || is_token_char(octstr_get_char(value, octstr_len(bytes))))
02684         goto error;
02685 
02686     pos = octstr_len(bytes);
02687     while (isspace(octstr_get_char(value, pos)))
02688         pos++;
02689 
02690     if (octstr_get_char(value, pos) != '=')
02691         goto error;
02692     pos++;
02693 
02694     for (;;) {
02695         /* Discard the whole header if any part of it can't be
02696          * parsed.  Probably a partial Range header is worse
02697          * than none at all. */
02698         pos = pack_range_value(packed, value, pos);
02699         if (pos < 0)
02700             goto error;
02701 
02702         while (isspace(octstr_get_char(value, pos)))
02703             pos++;
02704 
02705         if (octstr_get_char(value, pos) != ',')
02706             break;
02707         pos++;
02708 
02709         wsp_pack_short_integer(packed, WSP_HEADER_RANGE);
02710     }
02711 
02712     return 0;
02713 
02714 error:
02715     warning(0, "WSP: Cannot parse 'Range: %s'.",
02716             octstr_get_cstr(value));
02717     return -1;
02718 }
02719 
02720 /* The value is either a HTTP-date or a delta-seconds (integer). */
02721 int wsp_pack_retry_after(Octstr *packed, Octstr *value)
02722 {
02723     Octstr *encoded = NULL;
02724 
02725     encoded = octstr_create("");
02726     if (isdigit(octstr_get_char(value, 0))) {
02727         octstr_append_char(encoded, RELATIVE_TIME);
02728         if (wsp_pack_integer_string(encoded, value) < 0)
02729             goto error;
02730     } else {
02731         octstr_append_char(encoded, ABSOLUTE_TIME);
02732         if (wsp_pack_date(encoded, value) < 0)
02733             goto error;
02734     }
02735     wsp_pack_value(packed, encoded);
02736 
02737     octstr_destroy(encoded);
02738     return 0;
02739 
02740 error:
02741     octstr_destroy(encoded);
02742     return -1;
02743 }
02744 
02745 
02746 static int convert_rfc2616_warning_to_rfc2068(int warn_code)
02747 {
02748     int i;
02749     struct {
02750     int rfc2616code;
02751     int rfc2068code;
02752     } code_transform[] = {
02753     { 110, 10 },  /* Response is stale */
02754     { 111, 11 },  /* Revalidation failed */
02755     { 112, 12 },  /* Disconnected operation */
02756     { 113, 13 },  /* Heuristic expiration */
02757     { 199, 99 },  /* Miscellaneous warning */
02758     { 214, 14 },  /* Transformation applied */
02759     { 299, 99 },  /* Miscellaneous (persistent) warning */
02760     { -1, -1 }
02761     };
02762     
02763     for (i = 0; code_transform[i].rfc2616code >= 0; i++) {
02764     if (code_transform[i].rfc2616code == warn_code)
02765         return code_transform[i].rfc2068code;
02766     }
02767 
02768     return warn_code; /* conversion failed */
02769 }
02770 
02771 static int pack_warning(Octstr *packed, Octstr *value)
02772 {
02773     long warn_code = -1;
02774     Octstr *warn_agent = NULL;
02775     Octstr *warn_text = NULL;
02776     long pos;
02777     long start;
02778 
02779     pos = octstr_parse_long(&warn_code, value, 0, 10);
02780     if (pos < 0 || warn_code < 0)
02781         goto error;
02782 
02783     if (warn_code > 99) {
02784     /* RFC2068 uses 2-digit codes, and RFC2616 uses 3-digit codes.
02785      * This must be an RFC2616 code.  We don't have room in the
02786      * encoding for such codes, so we try to convert it back to
02787      * an RFC2068 code. */
02788     warn_code = convert_rfc2616_warning_to_rfc2068(warn_code);
02789     }
02790 
02791     if (warn_code > MAX_SHORT_INTEGER) {
02792     warning(0, "WSP: Cannot encode warning code %ld.", warn_code);
02793     return -1;
02794     }
02795 
02796     while (isspace(octstr_get_char(value, pos)))
02797         pos++;
02798 
02799     start = pos;
02800     while (pos < octstr_len(value) && !isspace(octstr_get_char(value, pos)))
02801         pos++;
02802 
02803     if (pos > start)
02804         warn_agent = octstr_copy(value, start, pos - start);
02805 
02806     while (isspace(octstr_get_char(value, pos)))
02807         pos++;
02808 
02809     start = pos;
02810     pos += http_header_quoted_string_len(value, pos);
02811     if (pos > start)
02812         warn_text = octstr_copy(value, start, pos - start);
02813 
02814     if (warn_agent == NULL && warn_text == NULL) {
02815         /* Simple encoding */
02816         wsp_pack_short_integer(packed, warn_code);
02817     } else {
02818         /* General encoding */
02819         Octstr *encoding = octstr_create("");
02820 
02821         if (warn_agent == NULL)
02822             warn_agent = octstr_create("");
02823         if (warn_text == NULL)
02824             warn_text = octstr_create("");
02825 
02826         wsp_pack_short_integer(encoding, warn_code);
02827         wsp_pack_text(encoding, warn_agent);
02828         wsp_pack_text(encoding, warn_text);
02829         wsp_pack_value(packed, encoding);
02830         octstr_destroy(encoding);
02831     }
02832 
02833     octstr_destroy(warn_agent);
02834     octstr_destroy(warn_text);
02835     return 0;
02836 
02837 error:
02838     warning(0, "WSP: Cannot parse 'Warning: %s'.",
02839             octstr_get_cstr(value));
02840     octstr_destroy(warn_agent);
02841     octstr_destroy(warn_text);
02842     return -1;
02843 }
02844 
02845 void wsp_pack_separate_content_type(Octstr *packed, List *headers)
02846 {
02847     Octstr *content_type;
02848 
02849     /* Can't use http_header_get_content_type because it
02850      * does not parse all possible parameters. */
02851     content_type = http_header_find_first(headers, "Content-Type");
02852 
02853     if (content_type == NULL) {
02854         warning(0, "WSP: Missing Content-Type header in "
02855                 "response, guessing application/octet-stream");
02856         content_type = octstr_create("application/octet-stream");
02857     }
02858     octstr_strip_blanks(content_type);
02859     wsp_pack_content_type(packed, content_type);
02860     octstr_destroy(content_type);
02861 }
02862 
02863 int wsp_pack_list(Octstr *packed, long fieldnum, List *elements, int i)
02864 {
02865     long startpos;
02866     Octstr *element;
02867 
02868     while ((element = gwlist_consume(elements))) {
02869         startpos = octstr_len(packed);
02870 
02871         wsp_pack_short_integer(packed, fieldnum);
02872         if (headerinfo[i].func(packed, element) < 0) {
02873             /* Remove whatever we added */
02874             octstr_delete(packed, startpos,
02875                           octstr_len(packed) - startpos);
02876             /* But continue processing elements */
02877         }
02878         octstr_destroy(element);
02879     }
02880     return 0;
02881 }
02882 
02883 static int pack_known_header(Octstr *packed, long fieldnum, Octstr *value)
02884 {
02885     List *elements = NULL;
02886     long startpos;
02887     long i;
02888 
02889     octstr_strip_blanks(value);
02890 
02891     startpos = octstr_len(packed);
02892 
02893     for (i = 0; i < TABLE_SIZE(headerinfo); i++) {
02894         if (headerinfo[i].header == fieldnum)
02895             break;
02896     }
02897 
02898     if (i == TABLE_SIZE(headerinfo)) {
02899         error(0, "WSP: Do not know how to encode header type %ld",
02900               fieldnum);
02901         goto error;
02902     }
02903 
02904     if (headerinfo[i].allows_list == LIST)
02905         elements = http_header_split_value(value);
02906     else if (headerinfo[i].allows_list == BROKEN_LIST)
02907         elements = http_header_split_auth_value(value);
02908     else
02909         elements = NULL;
02910 
02911     if (elements != NULL) {
02912         if (wsp_pack_list(packed, fieldnum, elements, i) < 0)
02913             goto error;
02914     } else {
02915         wsp_pack_short_integer(packed, fieldnum);
02916         if (headerinfo[i].func(packed, value) < 0)
02917             goto error;
02918     }
02919 
02920     gwlist_destroy(elements, octstr_destroy_item);
02921     return 0;
02922 
02923 error:
02924     /* Remove whatever we added */
02925     octstr_delete(packed, startpos, octstr_len(packed) - startpos);
02926     gwlist_destroy(elements, octstr_destroy_item);
02927     return -1;
02928 }
02929 
02930 int wsp_pack_application_header(Octstr *packed,
02931                                    Octstr *fieldname, Octstr *value)
02932 {
02933     if (!is_token(fieldname)) {
02934         warning(0, "WSP headers: `%s' is not a valid HTTP token.",
02935                 octstr_get_cstr(fieldname));
02936         return -1;
02937     }
02938 
02939     /* We have to deal specially with the X-WAP.TOD header, because it
02940      * is the only case of a text-format header defined with a non-text
02941      * field value. */
02942     /* Normally this should be a case-insensitive comparison, but this
02943      * header will only be present if we generated it ourselves in the
02944      * application layer. */
02945     if (octstr_str_compare(fieldname, "X-WAP.TOD") == 0) {
02946         wsp_pack_text(packed, fieldname);
02947         return wsp_pack_date(packed, value);
02948     }
02949 
02950     wsp_pack_text(packed, fieldname);
02951     wsp_pack_text(packed, value);
02952     return 0;
02953 }
02954 
02955 Octstr *wsp_headers_pack(List *headers, int separate_content_type, int wsp_version)
02956 {
02957     Octstr *packed;
02958     long i, len;
02959     int errors;
02960 
02961     packed = octstr_create("");
02962     if (separate_content_type)
02963         wsp_pack_separate_content_type(packed, headers);
02964 
02965     len = gwlist_len(headers);
02966     for (i = 0; i < len; i++) {
02967         Octstr *fieldname;
02968         Octstr *value;
02969         long fieldnum;
02970 
02971         http_header_get(headers, i, &fieldname, &value);
02972         /* XXX we need to obey which WSP encoding-version to use */
02973         /* fieldnum = wsp_string_to_header(fieldname); */
02974         fieldnum = wsp_string_to_versioned_header(fieldname, wsp_version);
02975 
02976         errors = 0;
02977 
02978         if (separate_content_type && fieldnum == WSP_HEADER_CONTENT_TYPE) {
02979         /* already handled */
02980         } else if (fieldnum < 0) {
02981             if (wsp_pack_application_header(packed, fieldname, value) < 0)
02982                 errors = 1;
02983         } else {
02984             if (pack_known_header(packed, fieldnum, value) < 0)
02985                 errors = 1;
02986         }
02987 
02988         if (errors)
02989             warning(0, "Skipping header: %s: %s",
02990                     octstr_get_cstr(fieldname),
02991                     octstr_get_cstr(value));
02992 
02993         octstr_destroy(fieldname);
02994         octstr_destroy(value);
02995     }
02996 
02997     /*
02998     http_header_dump(headers);
02999     octstr_dump(packed, 0);
03000     */
03001 
03002     return packed;
03003 }
03004 
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.