Tizen 2.1 base
[platform/upstream/hplip.git] / scan / sane / http.c
1 /************************************************************************************\
2   http.c - HTTP/1.1 feeder and consumer
3
4   (c) 2008 Copyright Hewlett-Packard Development Company, LP
5
6   Permission is hereby granted, free of charge, to any person obtaining a copy 
7   of this software and associated documentation files (the "Software"), to deal 
8   in the Software without restriction, including without limitation the rights 
9   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
10   of the Software, and to permit persons to whom the Software is furnished to do 
11   so, subject to the following conditions:
12
13   The above copyright notice and this permission notice shall be included in all
14   copies or substantial portions of the Software.
15
16   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
17   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 
18   FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 
19   COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 
20   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
21   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23   In order to support state-less connections, each HTTP/1.1 connection or session 
24   must start with http_open and end with http_close. 
25
26   Author: Naga Samrat Chowdary, Narla
27   Contributing Author: Sarbeswar Meher
28 \************************************************************************************/
29
30 #ifndef _GNU_SOURCE
31 #define _GNU_SOURCE
32 #endif
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <syslog.h>
38 #include <ctype.h>
39 #include "hpmud.h"
40 #include "http.h"
41
42 //#define HTTP_DEBUG
43
44 #define _STRINGIZE(x) #x
45 #define STRINGIZE(x) _STRINGIZE(x)
46
47 #define BUG(args...) syslog(LOG_ERR, __FILE__ " " STRINGIZE(__LINE__) ": " args)
48
49 #ifdef HTTP_DEBUG
50    #define DBG(args...) syslog(LOG_INFO, __FILE__ " " STRINGIZE(__LINE__) ": " args)
51    #define DBG_DUMP(data, size) sysdump((data), (size))
52    #define DBG_SZ(args...) syslog(LOG_INFO, args)
53 #else
54    #define DBG(args...)
55    #define DBG_DUMP(data, size)
56    #define DBG_SZ(args...)
57 #endif
58
59 #define EXCEPTION_TIMEOUT 45 /* seconds */
60
61 enum HTTP_STATE
62 {
63    HS_ACTIVE = 1,
64    HS_EOF,
65 };
66
67 struct stream_buffer
68 {
69    char buf[4096];
70    int index;
71    int cnt;   
72 };
73
74 struct http_session
75 {
76    enum HTTP_STATE state;
77    int http_status;
78    int footer;         /* current footer */
79    int total;
80    HPMUD_DEVICE dd;  /* hpiod device descriptor */
81    HPMUD_CHANNEL cd;  /* hpiod soap channel descriptor */
82    struct stream_buffer s;
83 };
84
85 #if 0
86 static char *strnstr(const char *haystack, const char *needle, size_t n)
87 {
88    int i, len=strlen(needle);
89
90    for (i=0; *haystack && i<n; ++haystack, i++)
91    {
92       if (strncmp(haystack, needle, len) == 0)
93       {
94          return ((char *)haystack);
95       }
96    }
97    return 0;
98 }
99 #endif
100
101 #if 0
102 static void sysdump(const void *data, int size)
103 {
104     /* Dump size bytes of *data. Output looks like:
105      * [0000] 75 6E 6B 6E 6F 77 6E 20 30 FF 00 00 00 00 39 00 unknown 0.....9.
106      */
107
108     unsigned char *p = (unsigned char *)data;
109     unsigned char c;
110     int n;
111     char bytestr[4] = {0};
112     char addrstr[10] = {0};
113     char hexstr[16*3 + 5] = {0};
114     char charstr[16*1 + 5] = {0};
115     for(n=1;n<=size;n++) {
116         if (n%16 == 1) {
117             /* store address for this line */
118             snprintf(addrstr, sizeof(addrstr), "%.4d", (int)((p-(unsigned char *)data) & 0xffff));
119         }
120             
121         c = *p;
122         if (isprint(c) == 0) {
123             c = '.';
124         }
125
126         /* store hex str (for left side) */
127         snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
128         strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);
129
130         /* store char str (for right side) */
131         snprintf(bytestr, sizeof(bytestr), "%c", c);
132         strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);
133
134         if(n%16 == 0) { 
135             /* line completed */
136             DBG_SZ("[%4.4s]   %-50.50s  %s\n", addrstr, hexstr, charstr);
137             hexstr[0] = 0;
138             charstr[0] = 0;
139         }
140         p++; /* next byte */
141     }
142
143     if (strlen(hexstr) > 0) {
144         /* print rest of buffer if not empty */
145         DBG_SZ("[%4.4s]   %-50.50s  %s\n", addrstr, hexstr, charstr);
146     }
147 }
148 #endif
149
150 /* Read data into stream buffer. Return specified "size" or less. Unused data is left in the stream. */
151 static int read_stream(struct http_session *ps, char *data, int size, int sec_timeout, int *bytes_read)
152 {
153    int len, stat=1;
154    int tmo=sec_timeout;        /* initial timeout */
155    int max=sizeof(ps->s.buf);
156    enum HPMUD_RESULT ret;
157
158    DBG("read_stream() ps=%p data=%p size=%d timeout=%d s.index=%d s.cnt=%d\n", ps, data, size, sec_timeout, ps->s.index, ps->s.cnt);
159
160    *bytes_read = 0;
161
162    /* Return any data in the stream first. */
163    if (ps->s.cnt)
164    {
165       if (ps->s.cnt > size)
166       {
167          /* Return part of stream buffer. */
168          len = size;
169          memcpy(data, &ps->s.buf[ps->s.index], len);
170          ps->s.index += len;
171          ps->s.cnt -= len;
172       }
173       else
174       {
175          /* Return all of rbuf. */
176          len = ps->s.cnt;
177          memcpy(data, &ps->s.buf[ps->s.index], len);
178          ps->s.index = ps->s.cnt = 0;       /* stream is empty reset */
179       }
180       *bytes_read = len;
181       DBG("-read_stream() bytes_read=%d s.index=%d s.cnt=%d\n", len, ps->s.index, ps->s.cnt);
182       return 0;
183    }
184
185    /* Stream is empty read more data from device. */
186    ret = hpmud_read_channel(ps->dd, ps->cd, &ps->s.buf[ps->s.index], max-(ps->s.index + ps->s.cnt), tmo, &len);
187    if (ret == HPMUD_R_IO_TIMEOUT)
188    {
189       BUG("timeout reading data sec_timeout=%d\n", tmo);
190       goto bugout;
191    }
192    if (ret != HPMUD_R_OK)
193    {
194       BUG("read_stream error stat=%d\n", ret);
195       goto bugout;
196    }
197    if (len==0)
198    {
199       BUG("read_stream error len=0\n");   /* shouldn't happen, but it does with jetdirect */
200       goto bugout;
201    }
202
203    DBG("read_channel len=%d\n", len);
204    ps->s.cnt += len;
205
206    if (ps->s.cnt > size)
207    {
208       /* Return part of stream buffer. */
209       len = size;
210       memcpy(data, &ps->s.buf[ps->s.index], len);
211       ps->s.index += len;
212       ps->s.cnt -= len;
213    }
214    else
215    {
216       /* Return all of rbuf. */
217       len = ps->s.cnt;
218       memcpy(data, &ps->s.buf[ps->s.index], len);
219       ps->s.index = ps->s.cnt = 0;       /* stream is empty reset */
220    }
221
222    *bytes_read = len;
223    stat = 0;
224    DBG("-read_stream() bytes_read=%d s.index=%d s.cnt=%d\n", len, ps->s.index, ps->s.cnt);
225
226 bugout:
227    return stat;
228 }
229
230 static int read_char(struct http_session *ps, int sec_timeout)
231 {
232    unsigned char ch;
233    int len;
234    if (read_stream(ps, (char *)&ch, 1, sec_timeout, &len))
235       return -1;
236    else
237       return ch;  
238 }
239
240 /* Read a line of data. Line length is not known. */
241 static int read_line(struct http_session *ps, char *line, int line_size, int sec_timeout, int *bytes_read)
242 {
243    int total=0, stat=1;
244    int ch, cr=0, lf=0;
245    int tmo=sec_timeout;        /* initial timeout */
246
247    *bytes_read = 0;
248
249    while (total < (line_size-1))
250    {
251       ch = read_char(ps, tmo);
252       line[total++]=ch;
253
254       if (ch == '\r')
255          cr=1;
256       else if (ch == '\n' && cr)
257          break;   /* done, found CRLF */
258       else if (ch == '\n' && lf)
259          break;   /* done, found LFLF (for kiwi "501 Not Implemented") */
260       else if (ch == '\n')
261          lf=1;
262       else if (ch == -1)
263          goto bugout;  /* error */
264       else
265       {
266         cr=0;
267         lf=0;
268       }
269       tmo=3;  /* changed 1 to 3 for 1200dpi uncompressed, DES 8/20/08. */ 
270    }
271    stat = 0;
272
273 bugout:
274    line[total]=0;
275    *bytes_read=total;   /* length does not include null termination */
276    DBG("read_line len=%d index=%d cnt=%d\n", total, ps->s.index, ps->s.cnt);
277    return stat;
278 }
279
280 /* Http_open must be called for each HTTP/1.1 connection or session. */
281 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_open(HPMUD_DEVICE dd, const char *channel, HTTP_HANDLE *handle)
282 {
283    struct http_session *ps;
284    enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
285    
286    DBG("http_open() dd=%d channel=%s handle=%p\n", dd, channel, handle);
287
288    *handle = NULL;
289
290    if ((ps = malloc(sizeof(struct http_session))) == NULL)
291    {
292       BUG("malloc failed: %m\n");
293       return HTTP_R_MALLOC_ERROR;
294    }
295    memset(ps, 0, sizeof(struct http_session));
296
297    ps->dd = dd;
298    if (hpmud_open_channel(ps->dd, channel, &ps->cd) != HPMUD_R_OK)
299    {
300       BUG("unable to open %s channel\n", channel);
301       goto bugout;
302    }
303
304    ps->state = HS_ACTIVE;
305    *handle = ps;
306    stat = HTTP_R_OK;
307
308 bugout:
309    if (stat != HTTP_R_OK)
310       free(ps);
311    return stat;
312 }
313
314 /* Http_close must be called after the HTTP/1.1 connection closes. */
315 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_close(HTTP_HANDLE handle)
316 {
317    struct http_session *ps = (struct http_session *)handle;
318    DBG("http_close() handle=%p\n", handle);
319    if (ps->cd > 0)
320       hpmud_close_channel(ps->dd, ps->cd);
321    free(ps);
322    return HTTP_R_OK;
323 }
324
325 /* Read HTTP/1.1 header. Blocks until header is read or timeout. */
326 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_header(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
327 {
328    struct http_session *ps = (struct http_session *)handle;
329    int len, total; 
330    int tmo=sec_timeout;   /* set initial timeout */
331    enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
332
333    DBG("http_read_header() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
334
335    *bytes_read = 0;
336
337    /* Read initial HTTP/1.1 header status line. */
338    if (read_line(ps, data, max_size, tmo, &len))
339       goto bugout;
340    ps->http_status = strtol(data+9, NULL, 10);
341    *bytes_read = total = len;
342
343    /* Check for good status, ignore 400 (no job id found for JobCancelRequest) */                    
344    if (!((ps->http_status >= 200 && ps->http_status < 300) || ps->http_status == 400))
345    {
346       BUG("invalid http_status=%d\n", ps->http_status);
347
348       /* Dump any outstanding payload here. */
349       while (!read_stream(ps, data, max_size, 1, &len))
350          BUG("dumping len=%d\n", len);
351       goto bugout;                
352    }
353
354    /* Read rest of header. Look for blank line. */
355    *bytes_read = total = len;
356    while (len > 2)
357    {
358       if (read_line(ps, data+total, max_size-total, tmo, &len))
359          goto bugout;
360       total += len;
361      *bytes_read += len;
362         DBG("http_read_header data=%s len=%d total=%d\n", (char*)data+total, len, total);
363    }
364    stat = HTTP_R_OK;
365
366    DBG("-http_read_header() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
367
368 bugout:
369    return stat;
370 };
371
372 /* Reads data from HTTP/1.1 chunked data stream until EOF. Returns max_size or less. */
373 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_payload(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
374 {
375    struct http_session *ps = (struct http_session *)handle;
376    char line[128];
377    int len; 
378    int tmo=sec_timeout;   /* set initial timeout */
379    enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
380
381    DBG("http_read_payload() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
382
383    *bytes_read = 0;
384
385    if (ps->state == HS_EOF)
386    {
387       stat = HTTP_R_EOF;
388    }
389    else
390    {
391       if (ps->footer)
392       {
393          /* Footer is not complete. Continue reading payload. */
394          if (read_stream(ps, data, ps->footer < max_size ? ps->footer : max_size, tmo, &len))
395             goto bugout;
396
397          ps->total += len;
398          ps->footer -= len;
399          *bytes_read = len;
400
401          if (ps->footer == 0)
402             if (read_line(ps, line, sizeof(line), tmo, &len))   /* footer is complete, eat CRLF */
403                goto bugout;
404
405          stat = HTTP_R_OK;
406       }
407       else
408       {
409          /* Read new footer. */
410          if (read_line(ps, line, sizeof(line), tmo, &len))
411             goto bugout;
412          ps->footer = strtol(line, NULL, 16);
413
414          /* Check for zero footer. */
415          if (ps->footer == 0)
416          {
417             /* Done eat blank line. */
418             read_line(ps, line, sizeof(line), 1, &len);
419             ps->state = HS_EOF;
420             stat = HTTP_R_EOF;
421          }
422          else
423          {
424             /* Got a valid footer, continue reading payload. */
425             if (read_stream(ps, data, ps->footer < max_size ? ps->footer : max_size, tmo, &len))
426                goto bugout;
427
428             ps->total += len;
429             ps->footer -= len;
430             *bytes_read = len;
431
432             if (ps->footer == 0)
433                if (read_line(ps, line, sizeof(line), tmo, &len))   /* footer is complete, eat CRLF */
434                   goto bugout;
435
436             stat = HTTP_R_OK;
437          }
438       }
439    }  /* if (ps->state == HS_EOF) */
440
441    DBG("-http_read_payload() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
442
443 bugout:
444    return stat;
445 };
446
447 /* Reads data from HTTP/1.1 chunked data stream until EOF. Returns max_size or less. */
448 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
449 {
450    struct http_session *ps = (struct http_session *)handle;
451    char line[128] ={0,};
452    int len = 0, ret;
453    int tmo=sec_timeout;   /* set initial timeout */
454    enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
455    int total_payload_length=*bytes_read;
456
457    DBG("http_read() handle=%p data=%p size=%d sectime=%d total_payload_length=%d\n", handle, data, max_size, sec_timeout, total_payload_length);
458
459    ps->footer=total_payload_length;
460
461    *bytes_read = 0;
462
463       /* Read new footer. */
464       if (ps->footer) //Payload length is known
465       {
466          while(ps->footer)
467          {
468               if (read_line(ps, line, sizeof(line), tmo, &len))
469                        {
470                                  *bytes_read = (ps->footer) * (-1) + 12;
471                                     goto bugout; 
472                            }
473                      strcpy(data, line);
474                      data=data+len;
475                      ps->footer -= len;
476             *bytes_read += len;
477          }
478       } 
479       else
480       {
481          while(1)
482          {
483             ret = read_line (ps, line, sizeof(line), tmo, &len);
484             *bytes_read += len;
485             if(ret) //failed to read line 
486                     {
487                             ps->footer = 0;
488                             break;
489                     }
490             strcpy(data, line);
491             data = data + len;
492             DBG("http_read len=%d datalen=%d data=%s\n", len, strlen((char*)data), (char*)data);
493             //Check for the footer
494             if (strncmp(data-7, ZERO_FOOTER, sizeof(ZERO_FOOTER)-1) == 0)
495             {
496                ps->footer = 0;
497                break;
498             }
499          }//end while(1)
500       }//end else
501       stat = HTTP_R_OK;
502       if(ps->footer == 0) stat=HTTP_R_EOF;
503
504    DBG("-http_read() handle=%p data=%p bytes_read=%d size=%d status=%d\n", handle, data, *bytes_read, max_size, stat);
505
506 bugout:
507    return stat;
508 };
509
510 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_read_size(HTTP_HANDLE handle, void *data, int max_size, int sec_timeout, int *bytes_read)
511 {
512   struct http_session *ps = (struct http_session *)handle;
513   enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
514
515   if(ps && ps->state == HTTP_R_EOF) return HTTP_R_EOF;
516   if(max_size == -1) 
517   { 
518     ps->state = HTTP_R_EOF; 
519     return HTTP_R_EOF; 
520   }
521
522   DBG("http_read_size() handle=%p data=%p size=%d sectime=%d\n", handle, data, max_size, sec_timeout);
523
524   *bytes_read=0;
525   while(*bytes_read < max_size)
526   {
527     *((char*)data + (*bytes_read)) = read_char(ps, sec_timeout);
528     *bytes_read = *bytes_read+1;
529   }
530
531   return stat = HTTP_R_OK;
532 }
533
534 /* Write data to HTTP/1.1 connection. Blocks until all data is written or timeout. Caller formats header, footer and payload. */
535 enum HTTP_RESULT __attribute__ ((visibility ("hidden"))) http_write(HTTP_HANDLE handle, void *data, int size, int sec_timeout)
536 {
537    struct http_session *ps = (struct http_session *)handle;
538    int len; 
539    int tmo=sec_timeout;   /* set initial timeout */
540    enum HTTP_RESULT stat = HTTP_R_IO_ERROR;
541
542    DBG("http_write() handle=%p data=%p size=%d sectime=%d\n", handle, data, size, sec_timeout);
543
544    if (hpmud_write_channel(ps->dd, ps->cd, data, size, tmo, &len) != HPMUD_R_OK)
545    {
546       BUG("unable to write channel data\n");
547       goto bugout;
548    }
549
550    stat = HTTP_R_OK;
551
552 bugout:
553    return stat;
554 }
555
556 void __attribute__ ((visibility ("hidden"))) http_unchunk_data(char *buffer)
557 {
558   char *temp=buffer;
559   char *p=buffer;
560   int chunklen = 0;
561
562   //Here buffer starts like "<?xml....". There is no chunklen, only buffer
563   if (*p == '<')
564   {
565       while(*p)
566       {
567         if (!(*p == '\n' || *p == '\r' || *p =='\t'))
568         {
569          *temp = *p;
570          temp++;
571         }
572         p++;
573           }
574           *temp = '\0';
575           return;
576   }
577   /*Here buffer looks like "chunklen data chunklen data 0"
578   e.g "FE3 <?xml....  8E8 ... 0"*/
579   while(1)
580   {
581     while(*p != '\n' && *p != '\r') 
582     {
583          chunklen = chunklen << 4 ; //Multiply 16
584          if ('0' <= *p &&  *p<='9')
585            chunklen += *p - '0';
586          else if ('A' <= *p && *p <= 'F')
587             chunklen += 10  - 'A' + *p;
588          else if ('a' <= *p && *p <= 'f')
589             chunklen += 10 + *p - 'a';
590          else
591          {
592             chunklen = chunklen >> 4;
593                 break;
594          }
595          p++;
596     }//end while()
597     if (chunklen == 0)
598        break ;
599     while(*p == '\n' || *p == '\r' || *p =='\t') p++;
600     //copy the data till chunklen
601     while(chunklen > 0)
602     {
603            if (!(*p == '\n' || *p == '\r' || *p =='\t'))
604        {
605         *temp = *p ;
606         temp++;
607        }
608        p++;
609        chunklen--;
610     }
611     while(*p == '\n' || *p == '\r' || *p =='\t') p++;
612   }//end while(1)
613   *temp = '\0';
614 }
615
616
617
618