1 /*
2 * Copyright 2018-2021 Eduardo Chappa
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 */
11 #include <ctype.h>
12 #include <stdio.h>
13 #include <time.h>
14 #include "c-client.h" /* this includes http.h */
15 #include "flstring.h"
16 #include "netmsg.h"
17
18 unsigned long http_debug;
19
20 //char t[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-.^_`|~";
21 static char http_notok[] = "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\42\50\51\54\57\72\73\74\75\76\77\100\133\134\135\173\175\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377";
22 static char http_noparam_val[] = "\1\2\3\4\5\6\7\10\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\42\134\177";
23
24 #define HTP_NOVAL 0x001 /* the header accepts parameters without value */
25
26 #define HTP_UNLIMITED (-1) /* parse and infinite list */
27
28 #if 0
29 typedef struct http_val_param_s {
30 unsigned char *value;
31 PARAMETER *plist;
32 } HTTP_VAL_PARAM_S;
33
34 typedef struct http_param_list_s {
35 HTTP_VAL_PARAM_S *vp;
36 struct http_param_list_s *next;
37 } HTTP_PARAM_LIST_S;
38
39 typedef struct http_header_value_s {
40 unsigned char *data;
41 HTTP_PARAM_LIST_S *p;
42 } HTTP_HEADER_S;
43
44 typedef struct http_header_data_s {
45 HTTP_HEADER_S *accept, /* RFC 7231, Section 5.3.2 */
46 *accept_charset, /* RFC 7231, Section 5.3.3 */
47 *accept_encoding, /* RFC 7231, Section 5.3.4 */
48 *accept_language, /* RFC 7231, Section 5.3.5 */
49 *accept_ranges, /* RFC 7233, Section 2.3 */
50 *age, /* RFC 7234, Section 5.1 */
51 *allow, /* RFC 7231, Section 7.4.1 */
52 *cache_control, /* RFC 7234, Section 5.2 */
53 *connection, /* RFC 7230, Section 6.1 */
54 *content_encoding, /* RFC 7231, Section 3.1.2.2 */
55 *content_disposition, /* RFC 6266 */
56 *content_language, /* RFC 7231, Section 3.1.3.2 */
57 *content_length, /* RFC 7230, Section 3.3.2 */
58 *content_location, /* RFC 7231, Section 3.1.4.2 */
59 *content_type, /* RFC 7231, Section 3.1.1.5 */
60 *date, /* RFC 7231, Section 7.1.1.2 */
61 *etag, /* RFC 7232, Section 2.3 */
62 *expect, /* RFC 7231, Section 5.1.1 */
63 *expires, /* RFC 7234, Section 5.3 */
64 *from, /* RFC 7231, Section 5.5.1 */
65 *host, /* RFC 7230, Section 5.4 */
66 *last_modified, /* RFC 7232, Section 2.2 */
67 *location, /* RFC 7231, Section 7.1.2 */
68 *max_forwards, /* RFC 7231, Section 5.1.2 */
69 *mime_version, /* RFC 7231, Appendix A.1 */
70 *pragma, /* RFC 7234, Section 5.4 */
71 *proxy_authenticate, /* RFC 7235, Section 4.3 */
72 *referer, /* RFC 7231, Section 5.5.2 */
73 *retry_after, /* RFC 7231, Section 7.1.3 */
74 *server, /* RFC 7231, Section 7.4.2 */
75 *te, /* RFC 7230, Section 4.3 */
76 *trailer, /* RFC 7230, Section 4.4 */
77 *transfer_encoding, /* RFC 7230, Section 3.3.1 */
78 *upgrade, /* RFC 7230, Section 6.7 */
79 *user_agent, /* RFC 7231, Section 5.5.3 */
80 *via, /* RFC 7230, Section 5.7.1 */
81 *vary, /* RFC 7231, Section 7.1.4 */
82 *warning, /* RFC 7234, Section 5.5 */
83 *www_authenticate; /* RFC 7235, Section 4.1 */
84 } HTTP_HEADER_DATA_S;
85 #endif
86
87 /* helper functions */
88 HTTP_STATUS_S *http_status_line_get(unsigned char *);
89 void http_status_line_free(HTTP_STATUS_S **);
90 void buffer_add(unsigned char **, unsigned char *);
91 unsigned char *hex_escape_url_part(unsigned char *, unsigned char *);
92 unsigned char *encode_url_body_part(unsigned char *, unsigned char *);
93
94 /* HTTP function prototypes */
95 long http_reply (HTTPSTREAM *);
96 long http_fake (HTTPSTREAM *, unsigned char *);
97
98 void http_skipows(unsigned char **);
99 void http_remove_trailing_ows(unsigned char *);
100
101 int valid_dquote_text(unsigned char *);
102 #define valid_token_name(X) (strpbrk((X), http_notok) ? 0 : 1)
103 #define valid_parameter_value(X) \
104 ((valid_token_name((X)) || valid_dquote_text((X))) ? 1 : 0)
105
106 /* HTTP HEADER FUNCTIONS */
107 void http_add_header_data(HTTPSTREAM *, unsigned char *);
108 void http_add_data_to_header(HTTP_HEADER_S **, unsigned char *);
109
110 HTTP_PARAM_LIST_S *http_parse_token_parameter(unsigned char *, int);
111 HTTP_PARAM_LIST_S *http_parse_token_list(unsigned char *, int);
112 PARAMETER *http_parse_parameter(unsigned char *, int);
113
114 void http_parse_headers(HTTPSTREAM *);
115
116 void *
http_parameters(long function,void * value)117 http_parameters (long function,void *value)
118 {
119 void *ret = NIL;
120 switch((int) function){
121 case SET_HTTPDEBUG: http_debug = (long) value;
122 case GET_HTTPDEBUG: ret = (void *) http_debug;
123 break;
124 }
125 return ret;
126 }
127
128
129 unsigned char *
http_response_from_reply(HTTPSTREAM * stream)130 http_response_from_reply(HTTPSTREAM *stream)
131 {
132 unsigned char *rv = NULL, *s;
133
134 if(stream == NULL || stream->reply == NULL || stream->header == NULL)
135 return rv;
136
137 s = strstr(stream->reply, "\r\n\r\n");
138 if(s != NULL) rv = s + 4;
139
140 return s ? rv : NIL;
141 }
142
143 void
http_parse_headers(HTTPSTREAM * stream)144 http_parse_headers(HTTPSTREAM *stream)
145 {
146 HTTP_HEADER_DATA_S *hd;
147 HTTP_HEADER_S *h;
148
149 if(!stream || !stream->header) return;
150
151 hd = stream->header;
152
153 if(((h = hd->accept)) && h->data){ /* RFC 7231, Section 5.3.2 */
154 h->p = http_parse_token_parameter(h->data, HTP_NOVAL);
155 fs_give((void **) &h->data);
156 }
157
158 if(((h = hd->accept_charset)) && h->data){ /* RFC 7231, Section 5.3.3 */
159 h->p = http_parse_token_parameter(h->data, 0);
160 fs_give((void **) &h->data);
161 }
162
163 if(((h = hd->accept_encoding)) && h->data){ /* RFC 7231, Section 5.3.4 */
164 h->p = http_parse_token_parameter(h->data, 0);
165 fs_give((void **) &h->data);
166 }
167
168 if(((h = hd->accept_language)) && h->data){ /* RFC 7231, Section 5.3.5 */
169 h->p = http_parse_token_parameter(h->data, 0);
170 fs_give((void **) &h->data);
171 }
172
173 if(((h = hd->accept_ranges)) && h->data){ /* RFC 7233, Section 2.3 */
174 h->p = http_parse_token_parameter(h->data, 0);
175 fs_give((void **) &h->data);
176 }
177
178 if(((h = hd->age)) && h->data){ /* RFC 7234, Section 5.1 */
179 h->p = http_parse_token_list(h->data, 1);
180 fs_give((void **) &h->data);
181 }
182
183 if(((h = hd->allow)) && h->data){ /* RFC 7231, Section 7.4.1 */
184 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
185 fs_give((void **) &h->data);
186 }
187
188 if(((h = hd->cache_control)) && h->data){ /* RFC 7234, Section 5.2 */
189 h->p = http_parse_token_parameter(h->data, HTP_NOVAL);
190 fs_give((void **) &h->data);
191 }
192
193 if(((h = hd->connection)) && h->data){ /* RFC 7230, Section 6.1 */
194 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
195 fs_give((void **) &h->data);
196 }
197
198 if(((h = hd->content_encoding)) && h->data){ /* RFC 7231, Section 3.1.2.2 */
199 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
200 fs_give((void **) &h->data);
201 }
202
203 if(((h = hd->content_disposition)) && h->data){ /* RFC 6266 */
204 h->p = http_parse_token_parameter(h->data, HTP_NOVAL);
205 fs_give((void **) &h->data);
206 }
207
208 if(((h = hd->content_language)) && h->data){ /* RFC 7231, Section 3.1.3.2 */
209 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
210 fs_give((void **) &h->data);
211 }
212
213 if(((h = hd->content_length)) && h->data){ /* RFC 7230, Section 3.3.2 */
214 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
215 fs_give((void **) &h->data);
216 }
217
218 if(((h = hd->content_location)) && h->data){ /* RFC 7231, Section 3.1.4.2 */
219 h->p = http_parse_token_list(h->data, 1);
220 fs_give((void **) &h->data);
221 }
222
223 if(((h = hd->content_type)) && h->data){ /* RFC 7231, Section 3.1.1.5 */
224 h->p = http_parse_token_parameter(h->data, 0);
225 fs_give((void **) &h->data);
226 }
227
228 if(((h = hd->date)) && h->data){ /* RFC 7231, Section 7.1.1.2 */
229 h->p = http_parse_token_list(h->data, 1);
230 fs_give((void **) &h->data);
231 }
232
233 if(((h = hd->etag)) && h->data){ /* Rewrite this. RFC 7232, Section 2.3 */
234 h->p = http_parse_token_list(h->data, 1);
235 fs_give((void **) &h->data);
236 }
237
238 if(((h = hd->expect)) && h->data){ /* Rewrite this. RFC 7231, Section 5.1.1 */
239 h->p = http_parse_token_list(h->data, 1);
240 fs_give((void **) &h->data);
241 }
242
243 if(((h = hd->expires)) && h->data){ /* Rewrite this. RFC 7234, Section 5.3 */
244 h->p = http_parse_token_list(h->data, 1);
245 fs_give((void **) &h->data);
246 }
247
248 if(((h = hd->from)) && h->data){ /* Rewrite this. RFC 7231, Section 5.5.1 */
249 h->p = http_parse_token_list(h->data, 1);
250 fs_give((void **) &h->data);
251 }
252
253 if(((h = hd->host)) && h->data){ /* Rewrite this. RFC 7230, Section 5.4 */
254 h->p = http_parse_token_list(h->data, 1);
255 fs_give((void **) &h->data);
256 }
257
258 if(((h = hd->last_modified)) && h->data){ /* Rewrite this. RFC 7232, Section 2.2 */
259 h->p = http_parse_token_list(h->data, 1);
260 fs_give((void **) &h->data);
261 }
262
263 if(((h = hd->location)) && h->data){ /* Rewrite this. RFC 7231, Section 7.1.2 */
264 h->p = http_parse_token_list(h->data, 1);
265 fs_give((void **) &h->data);
266 }
267
268 if(((h = hd->max_forwards)) && h->data){ /* RFC 7231, Section 5.1.2 */
269 h->p = http_parse_token_list(h->data, 1);
270 fs_give((void **) &h->data);
271 }
272
273 if(((h = hd->mime_version)) && h->data){ /* Rewrite this. RFC 7231, Appendix A.1 */
274 h->p = http_parse_token_list(h->data, 1);
275 fs_give((void **) &h->data);
276 }
277
278 if(((h = hd->pragma)) && h->data){ /* RFC 7234, Section 5.4 */
279 h->p = http_parse_token_parameter(h->data, HTP_NOVAL);
280 fs_give((void **) &h->data);
281 }
282
283 if(((h = hd->proxy_authenticate)) && h->data){ /* Rewrite this. RFC 7235, Section 4.3 */
284 h->p = http_parse_token_parameter(h->data, 0);
285 fs_give((void **) &h->data);
286 }
287
288 if(((h = hd->referer)) && h->data){ /* Rewrite this. RFC 7231, Section 5.5.2 */
289 h->p = http_parse_token_list(h->data, 1);
290 fs_give((void **) &h->data);
291 }
292
293 if(((h = hd->retry_after)) && h->data){ /* Rewrite this. RFC 7231, Section 7.1.3 */
294 h->p = http_parse_token_list(h->data, 1);
295 fs_give((void **) &h->data);
296 }
297
298 if(((h = hd->server)) && h->data){ /* Rewrite this. RFC 7231, Section 7.4.2 */
299 h->p = http_parse_token_list(h->data, 1);
300 fs_give((void **) &h->data);
301 }
302
303 if(((h = hd->te)) && h->data){ /* Rewrite this. RFC 7230, Section 4.3 */
304 h->p = http_parse_token_parameter(h->data, 0);
305 fs_give((void **) &h->data);
306 }
307
308 if(((h = hd->trailer)) && h->data){ /* RFC 7230, Section 4.4 */
309 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
310 fs_give((void **) &h->data);
311 }
312
313 if(((h = hd->transfer_encoding)) && h->data){ /* Rewrite this. RFC 7230, Section 3.3.1 */
314 h->p = http_parse_token_parameter(h->data, 0);
315 fs_give((void **) &h->data);
316 }
317
318 if(((h = hd->upgrade)) && h->data){ /* Rewrite this. RFC 7230, Section 6.7 */
319 h->p = http_parse_token_list(h->data, 1);
320 fs_give((void **) &h->data);
321 }
322
323 if(((h = hd->user_agent)) && h->data){ /* Rewrite this. RFC 7231, Section 5.5.3 */
324 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
325 fs_give((void **) &h->data);
326 }
327
328 if(((h = hd->via)) && h->data){ /* Rewrite this. RFC 7230, Section 5.7.1 */
329 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
330 fs_give((void **) &h->data);
331 }
332
333 if(((h = hd->vary)) && h->data){ /* Rewrite this. RFC 7231, Section 7.1.4 */
334 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
335 fs_give((void **) &h->data);
336 }
337
338 if(((h = hd->warning)) && h->data){ /* Rewrite this. RFC 7234, Section 5.5 */
339 h->p = http_parse_token_list(h->data, HTP_UNLIMITED);
340 fs_give((void **) &h->data);
341 }
342
343 if(((h = hd->www_authenticate)) && h->data){ /* Rewrite this. RFC 7235, Section 4.1 */
344 h->p = http_parse_token_parameter(h->data, 0);
345 fs_give((void **) &h->data);
346 }
347 }
348
349
350 void
http_add_data_to_header(HTTP_HEADER_S ** headerp,unsigned char * data)351 http_add_data_to_header(HTTP_HEADER_S **headerp, unsigned char *data)
352 {
353 HTTP_HEADER_S *h = *headerp;
354
355 if(!h){
356 h = fs_get(sizeof(HTTP_HEADER_S));
357 memset((void *)h, 0, sizeof(HTTP_HEADER_S));
358 }
359
360 if(h->data) buffer_add(&h->data, ", ");
361 buffer_add(&h->data, data);
362 *headerp = h;
363 }
364
365 void
http_add_header_data(HTTPSTREAM * stream,unsigned char * hdata)366 http_add_header_data(HTTPSTREAM *stream, unsigned char *hdata)
367 {
368 unsigned char *hname, *h;
369 int found = 1;
370
371 if(!stream || !hdata || !*hdata) return;
372
373 if(!stream->header){
374 stream->header = fs_get(sizeof(HTTP_HEADER_DATA_S));
375 memset((void *) stream->header, 0, sizeof(HTTP_HEADER_DATA_S));
376 }
377
378
379 /* extract header name first */
380 if((h = strchr(hdata, ':'))){
381 *h = '\0';
382 hname = fs_get((h-hdata+2)*sizeof(char));
383 strncpy(hname, hdata, h-hdata);
384 hname[h-hdata] = '\0';
385 if(!valid_token_name(hname))
386 return;
387 hname[h-hdata] = ':';
388 hname[h-hdata+1] = '\0';
389 *h++ = ':';
390 }
391 else return;
392
393 switch(*hname){
394 case 'A':
395 case 'a': if(!compare_cstring(hname+1, "ccept:")) /* RFC 7231, Section 5.3.2 */
396 http_add_data_to_header(&stream->header->accept, h);
397 else if(!compare_cstring(hname+1, "ccept-charset:")) /* RFC 7231, Section 5.3.3 */
398 http_add_data_to_header(&stream->header->accept_charset, h);
399 else if(!compare_cstring(hname+1, "ccept-encoding:")) /* RFC 7231, Section 5.3.4 */
400 http_add_data_to_header(&stream->header->accept_encoding, h);
401 else if(!compare_cstring(hname+1, "ccept-language:")) /* RFC 7231, Section 5.3.5 */
402 http_add_data_to_header(&stream->header->accept_language, h);
403 else if(!compare_cstring(hname+1, "ccept-ranges:")) /* RFC 7233, Section 2.3 */
404 http_add_data_to_header(&stream->header->accept_ranges, h);
405 else if(!compare_cstring(hname+1, "ge:")) /* RFC 7234, Section 5.1 */
406 http_add_data_to_header(&stream->header->age, h);
407 else if(!compare_cstring(hname+1, "llow:")) /* RFC 7231, Section 7.4.1 */
408 http_add_data_to_header(&stream->header->allow, h);
409 else found = 0;
410 break;
411
412 case 'C':
413 case 'c': if(!compare_cstring(hname+1, "ache-control:")) /* RFC 7234, Section 5.2 */
414 http_add_data_to_header(&stream->header->cache_control, h);
415 else if(!compare_cstring(hname+1, "onnection:")) /* RFC 7230, Section 6.1 */
416 http_add_data_to_header(&stream->header->connection, h);
417 else if(!compare_cstring(hname+1, "ontent-disposition:")) /* RFC 6266 */
418 http_add_data_to_header(&stream->header->content_disposition, h);
419 else if(!compare_cstring(hname+1, "ontent-encoding:")) /* RFC 7231, Section 3.1.2.2 */
420 http_add_data_to_header(&stream->header->content_encoding, h);
421 else if(!compare_cstring(hname+1, "ontent-language:")) /* RFC 7231, Section 3.1.3.2 */
422 /* rewrite this */ http_add_data_to_header(&stream->header->content_language, h);
423 else if(!compare_cstring(hname+1, "ontent-length:")) /* RFC 7230, Section 3.3.2 */
424 http_add_data_to_header(&stream->header->content_length, h);
425 else if(!compare_cstring(hname+1, "ontent-location:")) /* RFC 7231, Section 3.1.4.2 */
426 /* rewrite this */ http_add_data_to_header(&stream->header->content_location, h);
427 else if(!compare_cstring(hname+1, "ontent-type:")) /* RFC 7231, Section 3.1.1.5 */
428 http_add_data_to_header(&stream->header->content_type, h);
429 else found = 0;
430 break;
431
432 case 'D':
433 case 'd': if(!compare_cstring(hname+1, "ate:")) /* RFC 7231, Section 7.1.1.2 */
434 /* revise this */ http_add_data_to_header(&stream->header->date, h);
435 else found = 0;
436 break;
437
438 case 'E':
439 case 'e': if(!compare_cstring(hname+1, "tag:")) /* RFC 7232, Section 2.3 */
440 /* rewrite this */ http_add_data_to_header(&stream->header->etag, h);
441 else if(!compare_cstring(hname+1, "xpect:")) /* RFC 7231, Section 5.1.1 */
442 /* rewrite this */ http_add_data_to_header(&stream->header->expect, h);
443 else if(!compare_cstring(hname+1, "xpires:")) /* RFC 7234, Section 5.3 */
444 /* rewrite this */ http_add_data_to_header(&stream->header->expires, h);
445 else found = 0;
446 break;
447
448 case 'F':
449 case 'f': if(!compare_cstring(hname+1, "rom:")) /* RFC 7231, Section 5.5.1 */
450 /* rewrite this */ http_add_data_to_header(&stream->header->from, h);
451 else found = 0;
452 break;
453
454 case 'H':
455 case 'h': if(!compare_cstring(hname+1, "ost:")) /* RFC 7230, Section 5.4 */
456 http_add_data_to_header(&stream->header->host, h);
457 else found = 0;
458 break;
459
460 case 'L':
461 case 'l': if(!compare_cstring(hname+1, "ast-modified:")) /* RFC 7232, Section 2.2 */
462 http_add_data_to_header(&stream->header->last_modified, h);
463 else if(!compare_cstring(hname+1, "ocation:")) /* RFC 7231, Section 7.1.2 */
464 http_add_data_to_header(&stream->header->location, h);
465 else found = 0;
466 break;
467
468 case 'M':
469 case 'm': if(!compare_cstring(hname+1, "ax-forwards:")) /* RFC 7231, Section 5.1.2 */
470 http_add_data_to_header(&stream->header->max_forwards, h);
471 else if(!compare_cstring(hname+1, "ime-version:")) /* RFC 7231, Appendix A.1 */
472 http_add_data_to_header(&stream->header->mime_version, h);
473 else found = 0;
474 break;
475
476 case 'P':
477 case 'p': if(!compare_cstring(hname+1, "ragma:")) /* RFC 7234, Section 5.4 */
478 http_add_data_to_header(&stream->header->pragma, h);
479 else if(!compare_cstring(hname+1, "roxy-authenticate:")) /* RFC 7235, Section 4.3 */
480 http_add_data_to_header(&stream->header->proxy_authenticate, h);
481 else found = 0;
482 break;
483
484 case 'R':
485 case 'r': if(!compare_cstring(hname+1, "eferer:")) /* RFC 7231, Section 5.5.2 */
486 http_add_data_to_header(&stream->header->referer, h);
487 else if(!compare_cstring(hname+1, "etry-after:")) /* RFC 7231, Section 7.1.3 */
488 http_add_data_to_header(&stream->header->retry_after, h);
489 else found = 0;
490 break;
491
492 case 'S':
493 case 's': if(!compare_cstring(hname+1, "erver:")) /* RFC 7231, Section 7.4.2 */
494 http_add_data_to_header(&stream->header->server, h);
495 else found = 0;
496 break;
497
498 case 'T':
499 case 't': if(!compare_cstring(hname+1, "e:")) /* RFC 7230, Section 4.3 */
500 http_add_data_to_header(&stream->header->te, h);
501 else if(!compare_cstring(hname+1, "railer:")) /* RFC 7230, Section 4.4 */
502 http_add_data_to_header(&stream->header->trailer, h);
503 else if(!compare_cstring(hname+1, "ransfer-encoding:")) /* RFC 7230, Section 3.3.1 */
504 http_add_data_to_header(&stream->header->transfer_encoding, h);
505 else found = 0;
506 break;
507 break;
508
509 case 'U':
510 case 'u': if(!compare_cstring(hname+1, "pgrade:")) /* RFC 7230, Section 6.7 */
511 http_add_data_to_header(&stream->header->upgrade, h);
512 else if(!compare_cstring(hname+1, "ser-agent:")) /* RFC 7231, Section 5.5.3 */
513 http_add_data_to_header(&stream->header->user_agent, h);
514 else found = 0;
515 break;
516
517 case 'V':
518 case 'v': if(!compare_cstring(hname+1, "ia:")) /* RFC 7230, Section 5.7.1 */
519 http_add_data_to_header(&stream->header->via, h);
520 else if(!compare_cstring(hname+1, "ary:")) /* RFC 7231, Section 7.1.4 */
521 http_add_data_to_header(&stream->header->vary, h);
522 else found = 0;
523 break;
524
525 case 'W':
526 case 'w': if(!compare_cstring(hname+1, "arning:")) /* RFC 7234, Section 5.5 */
527 http_add_data_to_header(&stream->header->warning, h);
528 else if(!compare_cstring(hname+1, "ww-authenticate:")) /* RFC 7235, Section 4.1 */
529 http_add_data_to_header(&stream->header->www_authenticate, h);
530 else found = 0;
531 break;
532
533 default: break;
534 }
535 }
536
537
538 /* parse a list of tokens. If num is positive, parse at most
539 * num members in the list. Set num to HTP_UNLIMITED for a list
540 * without bounds
541 */
542 HTTP_PARAM_LIST_S *
http_parse_token_list(unsigned char * h,int num)543 http_parse_token_list(unsigned char *h, int num)
544 {
545 unsigned char *s = h, *t, c;
546 HTTP_PARAM_LIST_S *rv = NIL;
547
548 if(!s || !*s || num == 0) return NIL;
549 http_skipows(&s);
550 if(!*s) return NIL;
551 for(t = s; *t != '\0' && *t != ','; t++);
552 c = *t; *t = '\0';
553 http_remove_trailing_ows(s);
554
555 if(!valid_token_name(s))
556 return c == ',' ? http_parse_token_list(t+1, num) : NIL;
557
558 if(num > 0) num--; /* this one counts! */
559 rv = fs_get(sizeof(HTTP_PARAM_LIST_S));
560 memset((void *) rv, 0, sizeof(HTTP_PARAM_LIST_S));
561 rv->vp = fs_get(sizeof(HTTP_VAL_PARAM_S));
562 memset((void *) rv->vp, 0, sizeof(HTTP_VAL_PARAM_S));
563 rv->vp->value = cpystr(s);
564 *t = c;
565 if(c == ',')
566 rv->next = http_parse_token_list(t+1, num);
567
568 return rv;
569 }
570
571
572 /*
573 * parse a list of tokens with optional parameters
574 * into a HEADER_DATA structure. Do not parse into
575 * it anything invalid.
576 */
577 HTTP_PARAM_LIST_S *
http_parse_token_parameter(unsigned char * h,int flag)578 http_parse_token_parameter(unsigned char *h, int flag)
579 {
580 unsigned char *s = h, *t, *u, c, d;
581 HTTP_PARAM_LIST_S *rv = NIL;
582
583 /*
584 * Step 1:
585 * isolate first list element from list and remove
586 * leading and trailing white space.
587 */
588 if(!s) return NIL;
589 http_skipows(&s);
590 if(!*s) return NIL;
591 for(t = s; *t != '\0' && *t != ','; t++);
592 c = *t; *t = '\0';
593 http_remove_trailing_ows(s);
594
595 /*
596 * Step 2:
597 * isolate token name from its parameters. Remove
598 * any trailing spaces. If not valid token, move
599 * to the next entry in the list.
600 */
601 for(u = s; *u != '\0' && *u != ';'; u++);
602 d = *u; *u = '\0';
603 http_remove_trailing_ows(s);
604 if(!valid_token_name(s))
605 return c == ',' ? http_parse_token_parameter(t+1, flag) : NIL;
606
607 /*
608 * Step 3:
609 * If we make it this far, create a non-null reply
610 * and parse the token and parameters into a
611 * HTTP_HEADER_DATA_S structure
612 */
613 rv = fs_get(sizeof(HTTP_PARAM_LIST_S));
614 memset((void *) rv, 0, sizeof(HTTP_PARAM_LIST_S));
615 rv->vp = fs_get(sizeof(HTTP_VAL_PARAM_S));
616 memset((void *) rv->vp, 0, sizeof(HTTP_VAL_PARAM_S));
617 rv->vp->value = cpystr(s);
618 if(d == ';')
619 rv->vp->plist = http_parse_parameter(u+1, flag);
620 *u = d;
621 *t = c;
622 if(c == ',')
623 rv->next = http_parse_token_parameter(t+1, flag);
624
625 return rv;
626 }
627
628 int
valid_dquote_text(unsigned char * s)629 valid_dquote_text(unsigned char *s)
630 {
631 unsigned char *t;
632
633 if(!s || *s != '\"') return 0;
634
635 t = strchr(s+1, '\"');
636 return (t && !t[1]) ? 1 : 0;
637 }
638
639
640 void
http_skipows(unsigned char ** sp)641 http_skipows(unsigned char **sp)
642 {
643 unsigned char *s = *sp;
644 for(; *s == ' ' || *s == '\t'; s++);
645 *sp = s;
646 }
647
648 void
http_remove_trailing_ows(unsigned char * s)649 http_remove_trailing_ows(unsigned char *s)
650 {
651 unsigned char *t;
652 for(t = s; strlen(t) > 0 ;)
653 if(t[strlen(t)-1] == ' ' || t[strlen(t)-1] == '\t')
654 t[strlen(t)-1] = '\0';
655 else
656 break;
657 }
658
659 PARAMETER *
http_parse_parameter(unsigned char * s,int flag)660 http_parse_parameter(unsigned char *s, int flag)
661 {
662 PARAMETER *p;
663 unsigned char *t, *u, c;
664
665 /* Step 1:
666 * separate the parameters into a list separated by ";"
667 */
668 if(!s || !*s) return NIL;
669 http_skipows(&s);
670 if(!*s) return NIL;
671 for(t = s; *t != '\0' && *t != ';'; t++);
672 c = *t; *t = '\0';
673
674 /* Now we look for separation of attribute and value */
675 u = strchr(s, '=');
676
677 if(u){
678 *u = '\0';
679 http_remove_trailing_ows(s); http_remove_trailing_ows(u+1);
680 if(!valid_token_name(s) || !valid_parameter_value(u+1))
681 return c == ';' ? http_parse_parameter(t+1, flag) : NIL;
682 p = mail_newbody_parameter();
683 p->attribute = cpystr(s);
684 p->value = cpystr(u+1);
685 p->next = c == ';' ? http_parse_parameter(t+1, flag) : NIL;
686 *u = '=';
687 }
688 else if(flag & HTP_NOVAL){
689 /* this is a parameter with attribute but no value. RFC 7231
690 * section 5.3.2 allows this.
691 */
692 http_remove_trailing_ows(s);
693 if(!valid_token_name(s))
694 return c == ';' ? http_parse_parameter(t+1, flag) : NIL;
695 p = mail_newbody_parameter();
696 p->attribute = cpystr(s);
697 p->next = c == ';' ? http_parse_parameter(t+1, flag) : NIL;
698 } else
699 p = c == ';' ? http_parse_parameter(t+1, flag) : NIL;
700
701 return p;
702 }
703
704 unsigned char *
http_get_param_url(unsigned char * url,HTTP_PARAM_S * param)705 http_get_param_url(unsigned char *url, HTTP_PARAM_S *param)
706 {
707 int i;
708 unsigned char *rv = NULL;
709 HTTP_PARAM_S enc_param;
710
711 buffer_add(&rv, url);
712 for(i = 0; param[i].name != NULL; i++){
713 enc_param.name = hex_escape_url_part(param[i].name, NULL);
714 enc_param.value = hex_escape_url_part(param[i].value, NULL);
715 buffer_add(&rv, i == 0 ? "?" : "&");
716 buffer_add(&rv, enc_param.name);
717 buffer_add(&rv, "=");
718 buffer_add(&rv, enc_param.value);
719 fs_give((void **) &enc_param.name);
720 fs_give((void **) &enc_param.value);
721 }
722
723 return rv;
724 }
725
726 HTTP_REQUEST_S *
http_request_get(void)727 http_request_get(void)
728 {
729 HTTP_REQUEST_S *rv = fs_get(sizeof(HTTP_REQUEST_S));
730 memset((void *) rv, 0, sizeof(HTTP_REQUEST_S));
731
732 return rv;
733 }
734
735 void
http_request_free(HTTP_REQUEST_S ** hr)736 http_request_free(HTTP_REQUEST_S **hr)
737 {
738 if(!hr) return;
739
740 if((*hr)->request) fs_give((void **) &(*hr)->request);
741 if((*hr)->header) fs_give((void **) &(*hr)->header);
742 if((*hr)->body) fs_give((void **) &(*hr)->body);
743 fs_give((void **) hr);
744 }
745
746 unsigned char *
http_request_line(unsigned char * method,unsigned char * target,unsigned char * version)747 http_request_line(unsigned char *method, unsigned char *target, unsigned char *version)
748 {
749 int len = strlen(method) + strlen(target) + strlen(version) + 2 + 1;
750 unsigned char *line = fs_get(len*sizeof(char));
751
752 sprintf(line, "%s %s %s", method, target, version);
753 return line;
754 }
755
756 void
http_add_header(HTTP_REQUEST_S ** reqp,unsigned char * name,unsigned char * value)757 http_add_header(HTTP_REQUEST_S **reqp, unsigned char *name, unsigned char *value)
758 {
759 int len, hlen;
760
761 if(!reqp) return;
762
763 if(!*reqp) *reqp = http_request_get();
764
765 len = strlen(name) + 2 + strlen(value) + 2 + 1;
766 hlen = (*reqp)->header ? strlen((*reqp)->header) : 0;
767 len += hlen;
768 fs_resize((void **) &(*reqp)->header, len*sizeof(char));
769 sprintf((*reqp)->header + hlen, "%s: %s\015\012", name, value);
770 }
771
772 void
buffer_add(unsigned char ** bufp,unsigned char * text)773 buffer_add(unsigned char **bufp, unsigned char *text)
774 {
775 int len;
776
777 if(!bufp || !text || !*text) return;
778
779 len = *bufp ? strlen(*bufp) : 0;
780 fs_resize((void **) bufp, (len + strlen(text) + 1)*sizeof(char));
781 (*bufp)[len] = '\0';
782 strcat(*bufp, text);
783 }
784
785 void
http_add_body(HTTP_REQUEST_S ** reqp,unsigned char * text)786 http_add_body(HTTP_REQUEST_S **reqp, unsigned char *text)
787 {
788 if(!reqp) return;
789
790 if(!*reqp) *reqp = http_request_get();
791
792 buffer_add(&(*reqp)->body, text);
793 }
794
795
796 /* NULL terminated list of HTTP_PARAM_S objects.
797 * If caller needs "x" parameters, call this function
798 * with argument "x+1".
799 */
800 HTTP_PARAM_S *
http_param_get(int len)801 http_param_get(int len)
802 {
803 HTTP_PARAM_S *http_params;
804
805 http_params = fs_get(len*sizeof(HTTP_PARAM_S));
806 memset((void *) http_params, 0, len*sizeof(HTTP_PARAM_S));
807 return http_params;
808 }
809
810 void
http_param_free(HTTP_PARAM_S ** param)811 http_param_free(HTTP_PARAM_S **param)
812 {
813 int i;
814
815 if(param == NULL) return;
816
817 for(i = 0; (*param)[i].name != NULL; i++)
818 fs_give((void **) &(*param)[i].name);
819
820 for(i = 0; (*param)[i].value != NULL; i++)
821 fs_give((void **) &(*param)[i].value);
822
823 fs_give((void **) param);
824 }
825
826
827 /* This encodes for a GET request */
828 unsigned char *
hex_escape_url_part(unsigned char * text,unsigned char * addsafe)829 hex_escape_url_part(unsigned char *text, unsigned char *addsafe)
830 {
831 char *safechars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-";
832 unsigned char *s = fs_get((3*strlen(text) + 1)*sizeof(char)), *t;
833
834 *s = '\0';
835 for(t = text; *t != '\0'; t++)
836 if(strchr(safechars, *t) != NULL
837 || (addsafe != NULL && strchr(addsafe, *t) != NULL))
838 sprintf(s + strlen(s), "%c", *t);
839 else
840 sprintf(s + strlen(s), "%%%X", *t);
841 fs_resize((void **) &s, (strlen(s)+1)*sizeof(char));
842 return s;
843 }
844
845 /* this encodes for a POST request */
846 unsigned char *
encode_url_body_part(unsigned char * text,unsigned char * addsafe)847 encode_url_body_part(unsigned char *text, unsigned char *addsafe)
848 {
849 char *safechars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-";
850 unsigned char *s = fs_get((3*strlen(text) + 1)*sizeof(char)), *t;
851
852 *s = '\0';
853 for(t = text; *t != '\0'; t++)
854 if(*t == ' ') /* ASCII 32 is never safe, must always be encoded */
855 sprintf(s + strlen(s), "%c", '+');
856 else if(strchr(safechars, *t) != NULL
857 || (addsafe != NULL && strchr(addsafe, *t) != NULL))
858 sprintf(s + strlen(s), "%c", *t);
859 else
860 sprintf(s + strlen(s), "%%%X", *t);
861 fs_resize((void **) &s, (strlen(s)+1)*sizeof(char));
862 return s;
863 }
864
865 int
http_valid_net_parse(unsigned char * url,NETMBX * mb)866 http_valid_net_parse (unsigned char *url, NETMBX *mb)
867 {
868 int i, len;
869 unsigned char *s;
870 char *p;
871
872 if((url == NIL)
873 || (url[0] != 'h' && url[0] != 'H')
874 || (url[1] == 't' && url[1] == 'T')
875 || (url[2] == 't' && url[2] == 'T')
876 || (url[3] == 'p' && url[3] == 'P'))
877 return 0;
878
879 if(url[i = 4] == 's' || url[i] == 'S')
880 mb->sslflag = mb->notlsflag = T;
881 else i = 3;
882
883 if(url[++i] != ':' || url[++i] != '/' || url[++i] != '/')
884 return 0;
885
886 strcpy(mb->service, "http");
887 s = strchr(url+i+1, '/');
888 len = s ? s - url - i - 1 : strlen(url+i+1);
889 strncpy(mb->orighost, url+i+1, len);
890 mb->orighost[len] = '\0';
891 if((p = strchr(mb->orighost, ':')) != NULL){
892 *p++ = '\0';
893 mb->port = strtoul(p, &p, 10);
894 if(mb->port == 0L || *p != '\0')
895 return NIL;
896 }
897 strcpy(mb->host, mb->orighost);
898 return T;
899 }
900
901 HTTPSTREAM *
http_open(unsigned char * url)902 http_open (unsigned char *url)
903 {
904 HTTPSTREAM *stream;
905 NETMBX mb;
906 unsigned char *s;
907
908 memset((void *) &mb, 0, sizeof(NETMBX));
909 if(http_valid_net_parse (url,&mb) == 0)
910 return NIL;
911
912 stream = fs_get(sizeof(HTTPSTREAM));
913 memset((void *) stream, 0, sizeof(HTTPSTREAM));
914
915 s = strchr((char *) url + 7 + (mb.trysslflag ? 1 : 0) + 1, '/'); /* 7 = strlen("http://") + 1 */
916 stream->url = cpystr(url);
917 stream->urlhost = cpystr(mb.orighost);
918 stream->urltail = cpystr(s ? (char *) s : "/");
919 stream->netstream = net_open (&mb, NIL, mb.port ? mb.port : HTTPTCPPORT,
920 (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
921 "https", mb.port ? mb.port : HTTPSSLPORT);
922 stream->debug = http_debug;
923 if(!stream->netstream){
924 http_close(stream);
925 stream = NIL;
926 }
927 return stream;
928 }
929
930 unsigned char *
http_post_param(HTTPSTREAM * stream,HTTP_PARAM_S * param)931 http_post_param(HTTPSTREAM *stream, HTTP_PARAM_S *param)
932 {
933 HTTP_PARAM_S enc_param;
934 HTTP_REQUEST_S *http_request;
935 unsigned char *response = NULL;
936 int i;
937
938 if(stream == NULL || param == NULL ) return response;
939
940 http_request = http_request_get();
941 http_request->request = http_request_line("POST", stream->urltail, HTTP_1_1_VERSION);
942 http_add_header(&http_request, "Host", stream->urlhost);
943 http_add_header(&http_request, "Content-Type", HTTP_MIME_URLENCODED);
944
945 for(i = 0; param[i].name != NULL; i++){
946 enc_param.name = encode_url_body_part(param[i].name, NULL);
947 enc_param.value = encode_url_body_part(param[i].value, NULL);
948 if(i > 0)
949 http_add_body(&http_request, "&");
950 http_add_body(&http_request, enc_param.name);
951 http_add_body(&http_request, "=");
952 http_add_body(&http_request, enc_param.value);
953 fs_give((void **) &enc_param.name);
954 fs_give((void **) &enc_param.value);
955 }
956
957 if(http_send(stream, http_request)){
958 unsigned char *s = http_response_from_reply(stream);
959 response = cpystr(s ? (char *) s : "");
960 }
961
962 http_request_free(&http_request);
963
964 return response;
965 }
966
967 unsigned char *
http_get(HTTPSTREAM * stream,HTTP_PARAM_S ** h)968 http_get(HTTPSTREAM *stream, HTTP_PARAM_S **h)
969 {
970 HTTP_REQUEST_S *http_request;
971 unsigned char *response = NIL;
972 int i;
973
974 if(!stream) return response;
975
976 http_request = http_request_get();
977 http_request->request = http_request_line("GET", stream->urltail, HTTP_1_1_VERSION);
978 http_add_header(&http_request, "Host", stream->urlhost);
979 for(i = 0; h && h[i]->name && h[i]->value; i++)
980 http_add_header(&http_request, h[i]->name, h[i]->value);
981
982 if(http_send(stream, http_request)){
983 unsigned char *s = http_response_from_reply(stream);
984 response = cpystr(s ? (char *) s : "");
985 }
986
987 http_request_free(&http_request);
988
989 return response;
990 }
991
992 void
http_close(HTTPSTREAM * stream)993 http_close (HTTPSTREAM *stream)
994 {
995 if(stream){
996 if (stream->netstream) net_close (stream->netstream);
997 stream->netstream = NIL;
998 if (stream->url) fs_give ((void **) &stream->url);
999 if (stream->urlhost) fs_give ((void **) &stream->urlhost);
1000 if (stream->urltail) fs_give ((void **) &stream->urltail);
1001 if (stream->response) fs_give ((void **) &stream->response);
1002 if (stream->reply) fs_give ((void **) &stream->reply);
1003 fs_give((void **) &stream);
1004 }
1005 }
1006
1007 long
http_send(HTTPSTREAM * stream,HTTP_REQUEST_S * req)1008 http_send (HTTPSTREAM *stream, HTTP_REQUEST_S *req)
1009 {
1010 long ret;
1011 unsigned char *s = NULL;
1012
1013 if (!stream->netstream)
1014 ret = http_fake (stream,"http connection lost");
1015 else {
1016 if(req->body){
1017 char length[20];
1018
1019 sprintf(length, "%lu", strlen(req->body));
1020 http_add_header(&req, "Content-Length", length);
1021 }
1022
1023 buffer_add(&s, req->request); buffer_add(&s, "\015\012");
1024 buffer_add(&s, req->header); buffer_add(&s, "\015\012");
1025 buffer_add(&s, req->body); buffer_add(&s, "\015\012");
1026
1027 if(stream->debug) mm_log(s, HTTPDEBUG);
1028
1029 ret = net_soutr (stream->netstream,s)
1030 ? http_reply (stream)
1031 : http_fake (stream,"http connection broken in command");
1032 fs_give ((void **) &s);
1033 }
1034 return ret;
1035 }
1036
1037 HTTP_STATUS_S *
http_status_line_get(unsigned char * status_line)1038 http_status_line_get(unsigned char *status_line)
1039 {
1040 HTTP_STATUS_S *rv = NULL;
1041 char *version, *s;
1042 int code;
1043
1044 if(!status_line) return NIL;
1045
1046 if((s = strchr(status_line, ' ')) != NIL){
1047 *s = '\0';
1048 version = cpystr(status_line);
1049 *s++ = ' ';
1050 code = strtoul(s, &s, 10);
1051 if(s && *s == ' ' && code >= 100 && code < 600){
1052 rv = fs_get(sizeof(HTTP_STATUS_S));
1053 rv->version = version;
1054 rv->code = code;
1055 rv->text = cpystr(++s);
1056 }
1057 else
1058 fs_give((void **) &version);
1059 }
1060 return rv;
1061 }
1062
1063 void
http_status_line_free(HTTP_STATUS_S ** status)1064 http_status_line_free(HTTP_STATUS_S **status)
1065 {
1066 if(status == NULL) return;
1067
1068 if((*status)->version) fs_give((void **) &(*status)->version);
1069 if((*status)->text) fs_give((void **) &(*status)->text);
1070 fs_give((void **) status);
1071 }
1072
1073
1074 long
http_reply(HTTPSTREAM * stream)1075 http_reply (HTTPSTREAM *stream)
1076 {
1077 int in_header = 1;
1078 unsigned long size;
1079
1080 if (stream->response) fs_give ((void **) &stream->response);
1081 stream->response = (unsigned char *) net_getline(stream->netstream);
1082
1083 if(stream->debug) mm_log(stream->response ? stream->response : (unsigned char *) "<NIL RESPONSE>", HTTPDEBUG);
1084
1085 if(stream->response){
1086 buffer_add(&stream->reply, stream->response);
1087 buffer_add(&stream->reply, "\015\012");
1088 }
1089
1090 if(stream->status) http_status_line_free(&stream->status);
1091 stream->status = http_status_line_get(stream->response);
1092
1093 if(!stream->status){
1094 http_fake(stream, "Invalid status line received. Closing connection");
1095 return NIL;
1096 }
1097
1098 while (in_header > 0){
1099 if (stream->response) fs_give ((void **) &stream->response);
1100 stream->response = (unsigned char *) net_getline (stream->netstream);
1101 if(stream->response){
1102 buffer_add(&stream->reply, stream->response);
1103 http_add_header_data(stream, stream->response);
1104 if(stream->debug) mm_log(stream->response, HTTPDEBUG);
1105 }
1106 buffer_add(&stream->reply, "\015\012");
1107 // save_header(stream->headers, stream->response);
1108 if(!stream->response || *stream->response == '\0')
1109 in_header--;
1110 }
1111
1112 http_parse_headers(stream);
1113 if(stream->header->content_length){
1114 size = atol(stream->header->content_length->p->vp->value);
1115 if (stream->response) fs_give ((void **) &stream->response);
1116 stream->response = (unsigned char *) net_getsize (stream->netstream, size);
1117 if(stream->response){
1118 buffer_add(&stream->reply, stream->response);
1119 if(stream->debug) mm_log(stream->response, HTTPDEBUG);
1120 }
1121 }
1122 else if (stream->header->transfer_encoding){
1123 HTTP_PARAM_LIST_S *p = stream->header->transfer_encoding->p;
1124 for(; p ; p = p->next){
1125 if(!compare_cstring(p->vp->value, "chunked"))
1126 break;
1127 }
1128 if(p && p->vp->value){ /* chunked transfer */
1129 unsigned char *s = NIL;
1130 do {
1131 if (s) fs_give ((void **) &s);
1132 size = 0L;
1133 if((s = (unsigned char *) net_getline (stream->netstream)) != NIL){
1134 if(stream->debug) mm_log(s, HTTPDEBUG);
1135 size = strtol(s, NIL, 16);
1136 fs_give ((void **) &stream->response);
1137 if(size > 0){
1138 stream->response = (unsigned char *) net_getsize (stream->netstream, size);
1139 buffer_add(&stream->reply, stream->response);
1140 if(stream->debug) mm_log(stream->response, HTTPDEBUG);
1141 }
1142 }
1143 } while (stream && stream->netstream && s && (size > 0 || !*s ));
1144 }
1145 }
1146
1147 if(!stream->netstream)
1148 http_fake(stream, "Connection to HTTP server closed");
1149 return stream->netstream ? T : NIL;
1150 }
1151
1152 long
http_fake(HTTPSTREAM * stream,unsigned char * text)1153 http_fake (HTTPSTREAM *stream, unsigned char *text)
1154 {
1155 if (stream->netstream) net_close (stream->netstream);
1156 stream->netstream = NIL;
1157 if (stream->response) fs_give ((void **) &stream->response);
1158 /* add *text to the log, to pass this to the client */
1159 return NIL;
1160 }
1161