Merge branch 'pull-request-56'
[oweals/u-boot_mod.git] / u-boot / httpd / httpd.c
1 #include "uip.h"
2 #include "httpd.h"
3 #include "fs.h"
4 #include "fsdata.h"
5
6 #define STATE_NONE                              0               // empty state (waiting for request...)
7 #define STATE_FILE_REQUEST              1               // remote host sent GET request
8 #define STATE_UPLOAD_REQUEST    2               // remote host sent POST request
9
10 // ASCII characters
11 #define ISO_G                                   0x47    // GET
12 #define ISO_E                                   0x45
13 #define ISO_T                                   0x54
14 #define ISO_P                                   0x50    // POST
15 #define ISO_O                                   0x4f
16 #define ISO_S                                   0x53
17 #define ISO_T                                   0x54
18 #define ISO_slash                               0x2f    // control and other characters
19 #define ISO_space                               0x20
20 #define ISO_nl                                  0x0a
21 #define ISO_cr                                  0x0d
22 #define ISO_tab                                 0x09
23
24 // we use this so that we can do without the ctype library
25 #define is_digit(c)                             ((c) >= '0' && (c) <= '9')
26
27 // debug
28 //#define DEBUG_UIP
29
30 // html files
31 extern const struct fsdata_file file_index_html;
32 extern const struct fsdata_file file_404_html;
33 extern const struct fsdata_file file_flashing_html;
34 extern const struct fsdata_file file_fail_html;
35
36 extern int webfailsafe_ready_for_upgrade;
37 extern int webfailsafe_upgrade_type;
38 extern ulong NetBootFileXferSize;
39 extern unsigned char *webfailsafe_data_pointer;
40
41 extern flash_info_t flash_info[];
42
43 // http app state
44 struct httpd_state *hs;
45
46 static int webfailsafe_post_done = 0;
47 static int webfailsafe_upload_failed = 0;
48 static int data_start_found = 0;
49
50 static unsigned char post_packet_counter = 0;
51
52 // 0x0D -> CR 0x0A -> LF
53 static char eol[3] = { 0x0d, 0x0a, 0x00 };
54 static char eol2[5] = { 0x0d, 0x0a, 0x0d, 0x0a, 0x00 };
55
56 static char *boundary_value;
57
58 // str to int
59 static int atoi(const char *s){
60         int i = 0;
61
62         while(is_digit(*s)){
63                 i = i * 10 + *(s++) - '0';
64         }
65
66         return(i);
67 }
68
69 // print downloading progress
70 static void httpd_download_progress(void){
71         if(post_packet_counter == 39){
72                 puts("\n         ");
73                 post_packet_counter = 0;
74         }
75
76         puts("#");
77         post_packet_counter++;
78 }
79
80 // http server init
81 void httpd_init(void){
82         fs_init();
83         uip_listen(HTONS(80));
84 }
85
86 // reset app state
87 static void httpd_state_reset(void){
88         hs->state = STATE_NONE;
89         hs->count = 0;
90         hs->dataptr = 0;
91         hs->upload = 0;
92         hs->upload_total = 0;
93
94         data_start_found = 0;
95         post_packet_counter = 0;
96
97         if(boundary_value){
98                 free(boundary_value);
99         }
100 }
101
102 // find and get first chunk of data
103 static int httpd_findandstore_firstchunk(void){
104         char *start = NULL;
105         char *end = NULL;
106         flash_info_t *info = &flash_info[0];
107
108         if(!boundary_value){
109                 return(0);
110         }
111
112         // chek if we have data in packet
113         start = (char *)strstr((char *)uip_appdata, (char *)boundary_value);
114
115         if(start){
116
117                 // ok, we have data in this packet!
118                 // find upgrade type
119
120                 end = (char *)strstr((char *)start, "name=\"firmware\"");
121
122                 if(end){
123
124                         printf("Upgrade type: firmware\n");
125                         webfailsafe_upgrade_type = WEBFAILSAFE_UPGRADE_TYPE_FIRMWARE;
126
127                 } else {
128
129                         end = (char *)strstr((char *)start, "name=\"uboot\"");
130
131                         if(end){
132 #if defined(WEBFAILSAFE_DISABLE_UBOOT_UPGRADE)
133                                 printf("## Error: U-Boot upgrade is not allowed on this board!\n");
134                                 webfailsafe_upload_failed = 1;
135 #else
136                                 webfailsafe_upgrade_type = WEBFAILSAFE_UPGRADE_TYPE_UBOOT;
137                                 printf("Upgrade type: U-Boot\n");
138 #endif /* if defined(WEBFAILSAFE_DISABLE_UBOOT_UPGRADE) */
139                         } else {
140
141                                 end = (char *)strstr((char *)start, "name=\"art\"");
142
143                                 if(end){
144 #if defined(WEBFAILSAFE_DISABLE_ART_UPGRADE)
145                                         printf("## Error: U-Boot upgrade is not allowed on this board!\n");
146                                         webfailsafe_upload_failed = 1;
147 #else
148                                         printf("Upgrade type: ART\n");
149                                         webfailsafe_upgrade_type = WEBFAILSAFE_UPGRADE_TYPE_ART;
150
151                                         // if we don't have ART partition offset, it means that it should be
152                                         // stored on the last 64 KiB block -> in most supported board
153                                         // the ART partition occupies last 64 KiB block
154 #if !defined(WEBFAILSAFE_UPLOAD_ART_ADDRESS)
155                                         // if we don't know the flash type, we won't allow to update ART,
156                                         // because we don't know flash size
157                                         if(info->flash_id == FLASH_CUSTOM){
158                                                 printf("## Error: unknown FLASH type, can't update ART!\n");
159                                                 webfailsafe_upload_failed = 1;
160                                         }
161 #endif
162 #endif /* if defined(WEBFAILSAFE_DISABLE_ART_UPGRADE) */
163                                 } else {
164
165                                         printf("## Error: input name not found!\n");
166                                         return(0);
167
168                                 }
169
170                         }
171
172                 }
173
174                 end = NULL;
175
176                 // find start position of the data!
177                 end = (char *)strstr((char *)start, eol2);
178
179                 if(end){
180
181                         if((end - (char *)uip_appdata) < uip_len){
182
183                                 // move pointer over CR LF CR LF
184                                 end += 4;
185
186                                 // how much data we expect?
187                                 // last part (magic value 6): [CR][LF](boundary length)[-][-][CR][LF]
188                                 hs->upload_total = hs->upload_total - (int)(end - start) - strlen(boundary_value) - 6;
189
190                                 printf("Upload file size: %d bytes\n", hs->upload_total);
191
192                                 // We need to check if file which we are going to download
193                                 // has correct size (for every type of upgrade)
194
195                                 // U-Boot
196                                 if((webfailsafe_upgrade_type == WEBFAILSAFE_UPGRADE_TYPE_UBOOT) && (hs->upload_total != WEBFAILSAFE_UPLOAD_UBOOT_SIZE_IN_BYTES)){
197
198                                         printf("## Error: wrong file size, should be: %d bytes!\n", WEBFAILSAFE_UPLOAD_UBOOT_SIZE_IN_BYTES);
199                                         webfailsafe_upload_failed = 1;
200
201                                 // ART
202                                 } else if((webfailsafe_upgrade_type == WEBFAILSAFE_UPGRADE_TYPE_ART) && (hs->upload_total != WEBFAILSAFE_UPLOAD_ART_SIZE_IN_BYTES)){
203
204                                         printf("## Error: wrong file size, should be: %d bytes!\n", WEBFAILSAFE_UPLOAD_ART_SIZE_IN_BYTES);
205                                         webfailsafe_upload_failed = 1;
206
207                                 // firmware can't exceed: (FLASH_SIZE -  WEBFAILSAFE_UPLOAD_LIMITED_AREA_IN_BYTES)
208                                 } else if(hs->upload_total > (info->size - WEBFAILSAFE_UPLOAD_LIMITED_AREA_IN_BYTES)){
209
210                                         printf("## Error: file too big!\n");
211                                         webfailsafe_upload_failed = 1;
212
213                                 }
214
215                                 printf("Loading: ");
216
217                                 // how much data we are storing now?
218                                 hs->upload = (unsigned int)(uip_len - (end - (char *)uip_appdata));
219
220                                 memcpy((void *)webfailsafe_data_pointer, (void *)end, hs->upload);
221                                 webfailsafe_data_pointer += hs->upload;
222
223                                 httpd_download_progress();
224
225                                 return(1);
226
227                         }
228
229                 } else {
230                         printf("## Error: couldn't find start of data!\n");
231                 }
232
233         }
234
235         return(0);
236 }
237
238 // called for http server app
239 void httpd_appcall(void){
240         struct fs_file fsfile;
241         unsigned int i;
242
243         switch(uip_conn->lport){
244
245                 case HTONS(80):
246
247                         // app state
248                         hs = (struct httpd_state *)(uip_conn->appstate);
249
250                         // closed connection
251                         if(uip_closed()){
252                                 httpd_state_reset();
253                                 uip_close();
254                                 return;
255                         }
256
257                         // aborted connection or time out occured
258                         if(uip_aborted() || uip_timedout()){
259                                 httpd_state_reset();
260                                 uip_abort();
261                                 return;
262                         }
263
264                         // if we are pooled
265                         if(uip_poll()){
266                                 if(hs->count++ >= 100){
267                                         httpd_state_reset();
268                                         uip_abort();
269                                 }
270                                 return;
271                         }
272
273                         // new connection
274                         if(uip_connected()){
275                                 httpd_state_reset();
276                                 return;
277                         }
278
279                         // new data in STATE_NONE
280                         if(uip_newdata() && hs->state == STATE_NONE){
281
282                                 // GET or POST request?
283                                 if(uip_appdata[0] == ISO_G && uip_appdata[1] == ISO_E && uip_appdata[2] == ISO_T && (uip_appdata[3] == ISO_space || uip_appdata[3] == ISO_tab)){
284                                         hs->state = STATE_FILE_REQUEST;
285                                 } else if(uip_appdata[0] == ISO_P && uip_appdata[1] == ISO_O && uip_appdata[2] == ISO_S && uip_appdata[3] == ISO_T && (uip_appdata[4] == ISO_space || uip_appdata[4] == ISO_tab)){
286                                         hs->state = STATE_UPLOAD_REQUEST;
287                                 }
288
289                                 // anything else -> abort the connection!
290                                 if(hs->state == STATE_NONE){
291                                         httpd_state_reset();
292                                         uip_abort();
293                                         return;
294                                 }
295
296                                 // get file or firmware upload?
297                                 if(hs->state == STATE_FILE_REQUEST){
298
299                                         // we are looking for GET file name
300                                         for(i = 4; i < 30; i++){
301                                                 if(uip_appdata[i] == ISO_space || uip_appdata[i] == ISO_cr || uip_appdata[i] == ISO_nl || uip_appdata[i] == ISO_tab){
302                                                         uip_appdata[i] = 0;
303                                                         i = 0;
304                                                         break;
305                                                 }
306                                         }
307
308                                         if(i != 0){
309                                                 printf("## Error: request file name too long!\n");
310                                                 httpd_state_reset();
311                                                 uip_abort();
312                                                 return;
313                                         }
314
315                                         printf("Request for: ");
316                                         printf("%s\n", &uip_appdata[4]);
317
318                                         // request for /
319                                         if(uip_appdata[4] == ISO_slash && uip_appdata[5] == 0){
320                                                 fs_open(file_index_html.name, &fsfile);
321                                         } else {
322                                                 // check if we have requested file
323                                                 if(!fs_open((const char *)&uip_appdata[4], &fsfile)){
324                                                         printf("## Error: file not found!\n");
325                                                         fs_open(file_404_html.name, &fsfile);
326                                                 }
327                                         }
328
329                                         hs->state = STATE_FILE_REQUEST;
330                                         hs->dataptr = (u8_t *)fsfile.data;
331                                         hs->upload = fsfile.len;
332
333                                         // send first (and maybe the last) chunk of data
334                                         uip_send(hs->dataptr, (hs->upload > uip_mss() ? uip_mss() : hs->upload));
335                                         return;
336
337                                 } else if(hs->state == STATE_UPLOAD_REQUEST){
338
339                                         char *start = NULL;
340                                         char *end = NULL;
341
342                                         // end bufor data with NULL
343                                         uip_appdata[uip_len] = '\0';
344
345                                         /*
346                                          * We got first packet with POST request
347                                          *
348                                          * Some browsers don't include first chunk of data in the first
349                                          * POST request packet (like Google Chrome, IE and Safari)!
350                                          * So we must now find two values:
351                                          * - Content-Length
352                                          * - boundary
353                                          * Headers with these values can be in any order!
354                                          * If we don't find these values in first packet, connection will be aborted!
355                                          *
356                                          */
357
358                                         // Content-Length pos
359                                         start = (char *)strstr((char*)uip_appdata, "Content-Length:");
360
361                                         if(start){
362
363                                                 start += sizeof("Content-Length:");
364
365                                                 // find end of the line with "Content-Length:"
366                                                 end = (char *)strstr(start, eol);
367
368                                                 if(end){
369
370                                                         hs->upload_total = atoi(start);
371 #ifdef DEBUG_UIP
372                                                         printf("Expecting %d bytes in body request message\n", hs->upload_total);
373 #endif
374
375                                                 } else {
376                                                         printf("## Error: couldn't find \"Content-Length\"!\n");
377                                                         httpd_state_reset();
378                                                         uip_abort();
379                                                         return;
380                                                 }
381
382                                         } else {
383                                                 printf("## Error: couldn't find \"Content-Length\"!\n");
384                                                 httpd_state_reset();
385                                                 uip_abort();
386                                                 return;
387                                         }
388
389                                         // we don't support very small files (< 10 KB)
390                                         if(hs->upload_total < 10240){
391                                                 printf("## Error: request for upload < 10 KB data!\n");
392                                                 httpd_state_reset();
393                                                 uip_abort();
394                                                 return;
395                                         }
396
397                                         // boundary value
398                                         start = NULL;
399                                         end = NULL;
400
401                                         start = (char *)strstr((char *)uip_appdata, "boundary=");
402
403                                         if(start){
404
405                                                 // move pointer over "boundary="
406                                                 start += 9;
407
408                                                 // find end of line with boundary value
409                                                 end = (char *)strstr((char *)start, eol);
410
411                                                 if(end){
412
413                                                         // malloc space for boundary value + '--' and '\0'
414                                                         boundary_value = (char*)malloc(end - start + 3);
415
416                                                         if(boundary_value){
417
418                                                                 memcpy(&boundary_value[2], start, end - start);
419
420                                                                 // add -- at the begin and 0 at the end
421                                                                 boundary_value[0] = '-';
422                                                                 boundary_value[1] = '-';
423                                                                 boundary_value[end - start + 2] = 0;
424
425 #ifdef DEBUG_UIP
426                                                                 printf("Found boundary value: \"%s\"\n", boundary_value);
427 #endif
428
429                                                         } else {
430                                                                 printf("## Error: couldn't allocate memory for boundary!\n");
431                                                                 httpd_state_reset();
432                                                                 uip_abort();
433                                                                 return;
434                                                         }
435
436                                                 } else {
437                                                         printf("## Error: couldn't find boundary!\n");
438                                                         httpd_state_reset();
439                                                         uip_abort();
440                                                         return;
441                                                 }
442
443                                         } else {
444                                                 printf("## Error: couldn't find boundary!\n");
445                                                 httpd_state_reset();
446                                                 uip_abort();
447                                                 return;
448                                         }
449
450                                         /*
451                                          * OK, if we are here, it means that we found
452                                          * Content-Length and boundary values in headers
453                                          *
454                                          * We can now try to 'allocate memory' and
455                                          * find beginning of the data in first packet
456                                          */
457
458                                         webfailsafe_data_pointer = (u8_t *)WEBFAILSAFE_UPLOAD_RAM_ADDRESS;
459
460                                         if(!webfailsafe_data_pointer){
461                                                 printf("## Error: couldn't allocate RAM for data!\n");
462                                                 httpd_state_reset();
463                                                 uip_abort();
464                                                 return;
465                                         } else {
466                                                 printf("Data will be downloaded at 0x%X in RAM\n", WEBFAILSAFE_UPLOAD_RAM_ADDRESS);
467                                         }
468
469                                         if(httpd_findandstore_firstchunk()){
470                                                 data_start_found = 1;
471                                         } else {
472                                                 data_start_found = 0;
473                                         }
474
475                                         return;
476
477                                 } /* else if(hs->state == STATE_UPLOAD_REQUEST) */
478
479                         } /* uip_newdata() && hs->state == STATE_NONE */
480
481                         // if we got ACK from remote host
482                         if(uip_acked()){
483
484                                 // if we are in STATE_FILE_REQUEST state
485                                 if(hs->state == STATE_FILE_REQUEST){
486
487                                         // data which we send last time was received (we got ACK)
488                                         // if we send everything last time -> gently close the connection
489                                         if(hs->upload <= uip_mss()){
490
491                                                 // post upload completed?
492                                                 if(webfailsafe_post_done){
493
494                                                         if(!webfailsafe_upload_failed){
495                                                                 webfailsafe_ready_for_upgrade = 1;
496                                                         }
497
498                                                         webfailsafe_post_done = 0;
499                                                         webfailsafe_upload_failed = 0;
500                                                 }
501
502                                                 httpd_state_reset();
503                                                 uip_close();
504                                                 return;
505                                         }
506
507                                         // otherwise, send another chunk of data
508                                         // last time we sent uip_conn->len size of data
509                                         hs->dataptr += uip_conn->len;
510                                         hs->upload -= uip_conn->len;
511
512                                         uip_send(hs->dataptr, (hs->upload > uip_mss() ? uip_mss() : hs->upload));
513                                 }
514
515                                 return;
516
517                         }
518
519                         // if we need to retransmit
520                         if(uip_rexmit()){
521
522                                 // if we are in STATE_FILE_REQUEST state
523                                 if(hs->state == STATE_FILE_REQUEST){
524                                         // send again chunk of data without changing pointer and length of data left to send
525                                         uip_send(hs->dataptr, (hs->upload > uip_mss() ? uip_mss() : hs->upload));
526                                 }
527
528                                 return;
529
530                         }
531
532                         // if we got new data frome remote host
533                         if(uip_newdata()){
534
535                                 // if we are in STATE_UPLOAD_REQUEST state
536                                 if(hs->state == STATE_UPLOAD_REQUEST){
537
538                                         // end bufor data with NULL
539                                         uip_appdata[uip_len] = '\0';
540
541                                         // do we have to find start of data?
542                                         if(!data_start_found){
543
544                                                 if(!httpd_findandstore_firstchunk()){
545                                                         printf("## Error: couldn't find start of data in next packet!\n");
546                                                         httpd_state_reset();
547                                                         uip_abort();
548                                                         return;
549                                                 } else {
550                                                         data_start_found = 1;
551                                                 }
552
553                                                 return;
554                                         }
555
556                                         hs->upload += (unsigned int)uip_len;
557
558                                         if(!webfailsafe_upload_failed){
559                                                 memcpy((void *)webfailsafe_data_pointer, (void *)uip_appdata, uip_len);
560                                                 webfailsafe_data_pointer += uip_len;
561                                         }
562
563                                         httpd_download_progress();
564
565                                         // if we have collected all data
566                                         if(hs->upload >= hs->upload_total){
567
568                                                 printf("\n\n");
569
570                                                 // end of post upload
571                                                 webfailsafe_post_done = 1;
572                                                 NetBootFileXferSize = (ulong)hs->upload_total;
573
574                                                 // which website will be returned
575                                                 if(!webfailsafe_upload_failed){
576                                                         fs_open(file_flashing_html.name, &fsfile);
577                                                 } else {
578                                                         fs_open(file_fail_html.name, &fsfile);
579                                                 }
580
581                                                 httpd_state_reset();
582
583                                                 hs->state = STATE_FILE_REQUEST;
584                                                 hs->dataptr = (u8_t *)fsfile.data;
585                                                 hs->upload = fsfile.len;
586
587                                                 uip_send(hs->dataptr, (hs->upload > uip_mss() ? uip_mss() : hs->upload));
588                                         }
589
590                                 }
591
592                                 return;
593                         }
594
595                         break;
596
597                 default:
598                         // we shouldn't get here... we are listening only on port 80
599                         uip_abort();
600                         break;
601         }
602 }