blob: 135125287d864bfa474d7c0a1ea067e8d56a2452 [file] [log] [blame]
adamdunkelsca9ddcb2003-03-19 14:13:31 +00001/*
2 * Copyright (c) 2002, Adam Dunkels.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials provided
13 * with the distribution.
14 * 3. All advertising materials mentioning features or use of this
15 * software must display the following acknowledgement:
16 * This product includes software developed by Adam Dunkels.
17 * 4. The name of the author may not be used to endorse or promote
18 * products derived from this software without specific prior
19 * written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * This file is part of the Contiki desktop environment
34 *
adamdunkels30c449e2003-07-31 23:12:05 +000035 * $Id: www.c,v 1.11 2003/07/31 23:12:05 adamdunkels Exp $
adamdunkelsca9ddcb2003-03-19 14:13:31 +000036 *
37 */
38
39
40#include "ctk.h"
41#include "dispatcher.h"
42#include "webclient.h"
43#include "htmlparser.h"
44#include "http-strings.h"
45#include "resolv.h"
46
47#include "petsciiconv.h"
48
adamdunkels8af703e2003-04-08 11:50:20 +000049#include "loader.h"
50
adamdunkelsca9ddcb2003-03-19 14:13:31 +000051#include "www-conf.h"
52
adamdunkels30c449e2003-07-31 23:12:05 +000053#if 1
adamdunkelsca9ddcb2003-03-19 14:13:31 +000054#define PRINTF(x)
55#else
56#include <stdio.h>
57#define PRINTF(x) printf x
58#endif
59
60
61/* The array that holds the current URL. */
62static char url[WWW_CONF_MAX_URLLEN + 1];
63static char tmpurl[WWW_CONF_MAX_URLLEN + 1];
64
65/* The array that holds the web page text. */
adamdunkels17e84a32003-04-02 09:54:39 +000066static char webpage[WWW_CONF_WEBPAGE_WIDTH * WWW_CONF_WEBPAGE_HEIGHT + 1];
adamdunkelsca9ddcb2003-03-19 14:13:31 +000067
68/* The CTK widgets for the main window. */
69static struct ctk_window mainwindow;
70
71static struct ctk_button backbutton =
72 {CTK_BUTTON(0, 0, 4, "Back")};
73static struct ctk_button downbutton =
74 {CTK_BUTTON(10, 0, 4, "Down")};
75static struct ctk_button stopbutton =
76 {CTK_BUTTON(20, 0, 4, "Stop")};
77static struct ctk_button gobutton =
78 {CTK_BUTTON(32, 0, 2, "Go")};
79
80static struct ctk_separator sep1 =
81 {CTK_SEPARATOR(0, 2, 36)};
82
83static char editurl[WWW_CONF_MAX_URLLEN + 1];
84static struct ctk_textentry urlentry =
85 {CTK_TEXTENTRY(0, 1, 34, 1, editurl, WWW_CONF_MAX_URLLEN)};
86static struct ctk_label webpagelabel =
adamdunkels17e84a32003-04-02 09:54:39 +000087 {CTK_LABEL(0, 3, WWW_CONF_WEBPAGE_WIDTH, WWW_CONF_WEBPAGE_HEIGHT, webpage)};
adamdunkelsca9ddcb2003-03-19 14:13:31 +000088
89static char statustexturl[36];
90static struct ctk_label statustext =
91 {CTK_LABEL(0, 21, 36, 1, "")};
92static struct ctk_separator sep2 =
93 {CTK_SEPARATOR(0, 20, 36)};
94
95/* The char arrays that hold the history of visited URLs. */
adamdunkels17e84a32003-04-02 09:54:39 +000096static char history[WWW_CONF_HISTORY_SIZE][WWW_CONF_MAX_URLLEN];
adamdunkelsca9ddcb2003-03-19 14:13:31 +000097static char history_last, history_first;
98
99
100/* The CTK widget definitions for the hyperlinks and the char arrays
101 that hold the link URLs. */
102struct formattribs {
103 char formaction[WWW_CONF_MAX_FORMACTIONLEN];
104 char formname[WWW_CONF_MAX_FORMNAMELEN];
105#define FORMINPUTTYPE_SUBMITBUTTON 1
106#define FORMINPUTTYPE_INPUTFIELD 2
107 unsigned char inputtype;
108 char inputname[WWW_CONF_MAX_INPUTNAMELEN];
109 char *inputvalue;
110};
111
112union pagewidgetattrib {
113 char url[WWW_CONF_MAX_URLLEN];
114 struct formattribs form;
115};
adamdunkels17e84a32003-04-02 09:54:39 +0000116static struct ctk_widget pagewidgets[WWW_CONF_MAX_NUMPAGEWIDGETS];
117static union pagewidgetattrib pagewidgetattribs[WWW_CONF_MAX_NUMPAGEWIDGETS];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000118static unsigned char pagewidgetptr;
119
120
121/* The "scrolly" variable holds the line number (in the web page) of
122 the first line of text shown on screen. */
123static unsigned short scrolly;
124
adamdunkels30c449e2003-07-31 23:12:05 +0000125/* The "scrollend" variable contains the web page line number of the
126 last line that should be shown on screen, before the download
127 should stop. */
128#if WWW_CONF_PAGEVIEW
129static unsigned short scrollend;
130#endif /* WWW_CONF_PAGEVIEW */
131
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000132#if WWW_CONF_RENDERSTATE
133static unsigned char renderstate;
134#endif /* WWW_CONF_RENDERSTATE */
135
136/* The "run" flag is used to determine if the web page should be
137 continuosly scrolled upward with new data coming in from below. */
138static unsigned char run;
139
140#define ISO_nl 0x0a
141#define ISO_space 0x20
142#define ISO_ampersand 0x26
143#define ISO_plus 0x2b
144#define ISO_slash 0x2f
145#define ISO_eq 0x3d
146#define ISO_questionmark 0x3f
147
148/* The state of the rendering code. */
149static u8_t x;
150static u16_t starty;
adamdunkels17e84a32003-04-02 09:54:39 +0000151static char nextword[WWW_CONF_WEBPAGE_WIDTH + 1];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000152static unsigned char nextwordptr;
153
154static unsigned char count;
155static char receivingmsgs[4][23] = {
156 "Receiving web page ...",
157 "Receiving web page. ..",
158 "Receiving web page.. .",
159 "Receiving web page... "
160};
161
162
adamdunkels78c03dc2003-04-09 13:45:05 +0000163static DISPATCHER_SIGHANDLER(www_sighandler, s, data);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000164static struct dispatcher_proc p =
adamdunkels78c03dc2003-04-09 13:45:05 +0000165 {DISPATCHER_PROC("Web browser", NULL, www_sighandler, webclient_appcall)};
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000166static ek_id_t id;
167
168
169static void formsubmit(struct formattribs *attribs);
170/*-----------------------------------------------------------------------------------*/
171/* make_window()
172 *
173 * Creates the web browser's window.
174 */
175static void
176make_window(void)
177{
178
179 CTK_WIDGET_ADD(&mainwindow, &backbutton);
180 CTK_WIDGET_ADD(&mainwindow, &downbutton);
181 CTK_WIDGET_ADD(&mainwindow, &stopbutton);
182 CTK_WIDGET_ADD(&mainwindow, &gobutton);
183 CTK_WIDGET_ADD(&mainwindow, &urlentry);
184 CTK_WIDGET_ADD(&mainwindow, &sep1);
185 CTK_WIDGET_ADD(&mainwindow, &webpagelabel);
186 CTK_WIDGET_ADD(&mainwindow, &sep2);
187 CTK_WIDGET_ADD(&mainwindow, &statustext);
188
189 CTK_WIDGET_FOCUS(&mainwindow, &stopbutton);
190
191 pagewidgetptr = 0;
192}
193/*-----------------------------------------------------------------------------------*/
194/* redraw_window():
195 *
196 * Convenience function that calls upon CTK to redraw the browser
197 * window. */
198static void
199redraw_window(void)
200{
201 ctk_window_redraw(&mainwindow);
202}
203/*-----------------------------------------------------------------------------------*/
204/* www_init();
205 *
206 * Initializes and starts the web browser. Called either at startup or
207 * to open the browser window.
208 */
adamdunkels1cd53bd2003-04-08 19:24:55 +0000209LOADER_INIT_FUNC(www_init)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000210{
211 if(id == EK_ID_NONE) {
212 id = dispatcher_start(&p);
213
214 /* Create the main window. */
215 memset(webpage, 0, sizeof(webpage));
adamdunkels30c449e2003-07-31 23:12:05 +0000216 ctk_window_new(&mainwindow, 36, 22, "Web browser");
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000217 make_window();
218 CTK_WIDGET_FOCUS(&mainwindow, &urlentry);
219
220 /* Attach as a listener to a number of signals ("Button activate",
221 "Hyperlink activate" and "Hyperlink hover", and the resolver's
222 signal. */
adamdunkels62b64992003-04-08 07:20:11 +0000223 dispatcher_listen(ctk_signal_window_close);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000224 dispatcher_listen(ctk_signal_button_activate);
225 dispatcher_listen(ctk_signal_hyperlink_activate);
226 dispatcher_listen(ctk_signal_hyperlink_hover);
227 dispatcher_listen(resolv_signal_found);
228 }
229 ctk_window_open(&mainwindow);
230}
231/*-----------------------------------------------------------------------------------*/
232static void
233clear_page(void)
234{
235 if(ctk_window_isopen(&mainwindow)) {
236 ctk_window_close(&mainwindow);
237 }
238 ctk_window_clear(&mainwindow);
239 make_window();
240 ctk_window_open(&mainwindow);
adamdunkels17e84a32003-04-02 09:54:39 +0000241 memset(webpage, 0, WWW_CONF_WEBPAGE_WIDTH * WWW_CONF_WEBPAGE_HEIGHT);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000242}
243/*-----------------------------------------------------------------------------------*/
244static void
245show_url(void)
246{
247 memcpy(editurl, url, WWW_CONF_MAX_URLLEN);
248 strncpy(editurl, "http://", 7);
249 petsciiconv_topetscii(editurl + 7, WWW_CONF_MAX_URLLEN - 7);
250 CTK_WIDGET_REDRAW(&urlentry);
251}
252/*-----------------------------------------------------------------------------------*/
253static void
254show_statustext(char *text)
255{
256 ctk_label_set_text(&statustext, text);
257 CTK_WIDGET_REDRAW(&statustext);
258}
259/*-----------------------------------------------------------------------------------*/
260/* open_url():
261 *
262 * Called when the URL present in the global "url" variable should be
263 * opened. It will call the hostname resolver as well as the HTTP
264 * client requester.
265 */
266static void
267open_url(void)
268{
269 unsigned char i;
270 static char host[32];
271 char *file;
272 register char *urlptr;
273 unsigned short port;
274
275 /* Trim off any spaces in the end of the url. */
276 urlptr = url + strlen(url) - 1;
277 while(*urlptr == ' ' && urlptr > url) {
278 *urlptr = 0;
279 --urlptr;
280 }
281
282 /* Don't even try to go further if the URL is empty. */
283 if(urlptr == url) {
284 return;
285 }
286
287 /* See if the URL starts with http://, otherwise prepend it. */
288 if(strncmp(url, http_http, 7) != 0) {
289 while(urlptr >= url) {
290 *(urlptr + 7) = *urlptr;
291 --urlptr;
292 }
293 strncpy(url, http_http, 7);
294 }
295
296 /* Find host part of the URL. */
297 urlptr = &url[7];
298 for(i = 0; i < sizeof(host); ++i) {
299 if(*urlptr == 0 ||
300 *urlptr == '/' ||
301 *urlptr == ' ' ||
302 *urlptr == ':') {
303 host[i] = 0;
304 break;
305 }
306 host[i] = *urlptr;
307 ++urlptr;
308 }
309
310 /* XXX: Here we should find the port part of the URL, but this isn't
311 currently done because of laziness from the programmer's side
312 :-) */
313
314 /* Find file part of the URL. */
315 while(*urlptr != '/' && *urlptr != 0) {
316 ++urlptr;
317 }
318 if(*urlptr == '/') {
319 file = urlptr;
320 } else {
321 file = "/";
322 }
323
324 /* Try to lookup the hostname. If it fails, we initiate a hostname
325 lookup and print out an informative message on the statusbar. */
326 if(resolv_lookup(host) == NULL) {
327 resolv_query(host);
328 show_statustext("Resolving host...");
329 return;
330 }
331
332 /* The hostname we present in the hostname table, so we send out the
333 initial GET request. */
334 if(webclient_get(host, 80, file) == 0) {
335 show_statustext("Out of memory error.");
336 } else {
337 show_statustext("Connecting...");
338 }
339 redraw_window();
340}
341/*-----------------------------------------------------------------------------------*/
342/* open_link(link):
343 *
344 * Will format a link from the current web pages so that it suits the
345 * open_url() function and finally call it to open the requested URL.
346 */
347static void
348open_link(char *link)
349{
350 char *urlptr;
351
352 if(strncmp(link, http_http, 7) == 0) {
353 /* The link starts with http://. We just copy the contents of the
354 link into the url string and jump away. */
355 strncpy(url, link, WWW_CONF_MAX_URLLEN);
356 } else if(*link == ISO_slash &&
357 *(link + 1) == ISO_slash) {
358 /* The link starts with //, so we'll copy it into the url
359 variable, starting after the http (which already is present in
360 the url variable since we were able to open the web page on
361 which this link was found in the first place). */
362 strncpy(&url[5], link, WWW_CONF_MAX_URLLEN);
363 } else if(*link == ISO_slash) {
364 /* The link starts with a slash, so it is a non-relative link
365 within the same web site. We find the start of the filename of
366 the current URL and paste the contents of this link there, and
367 head off to the new URL. */
368 for(urlptr = &url[7];
369 *urlptr != 0 && *urlptr != ISO_slash;
370 ++urlptr);
371 strncpy(urlptr, link, WWW_CONF_MAX_URLLEN - (urlptr - url));
372 } else {
373 /* A fully relative link is found. We find the last slash in the
374 current URL and paste the link there. */
375
376 /* XXX: we should really parse any ../ in the link as well. */
377 for(urlptr = url + strlen(url);
378 urlptr != url && *urlptr != ISO_slash;
379 --urlptr);
380 ++urlptr;
381 strncpy(urlptr, link, WWW_CONF_MAX_URLLEN - (urlptr - url));
382 }
383
384 /* Open the URL. */
385 scrolly = 0;
386 show_url();
387 open_url();
388}
389/*-----------------------------------------------------------------------------------*/
390/* log_back():
391 *
392 * Copies the current URL from the url variable and into the log for
393 * the back button.
394 */
395static void
396log_back(void)
397{
adamdunkels30c449e2003-07-31 23:12:05 +0000398 memcpy(history[(int)history_last], url, WWW_CONF_MAX_URLLEN);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000399 ++history_last;
adamdunkelsc11c62d2003-04-15 21:24:14 +0000400 if(history_last >= WWW_CONF_HISTORY_SIZE) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000401 history_last = 0;
402 }
403}
404/*-----------------------------------------------------------------------------------*/
405/* www_dispatcher():
406 *
407 * The program's signal dispatcher function. Is called by the ek
408 * dispatcher whenever a signal arrives.
409 */
adamdunkels78c03dc2003-04-09 13:45:05 +0000410static
411DISPATCHER_SIGHANDLER(www_sighandler, s, data)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000412{
413 static struct ctk_widget *w;
414 static unsigned char i;
adamdunkels78c03dc2003-04-09 13:45:05 +0000415 DISPATCHER_SIGHANDLER_ARGS(s, data);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000416
417
418 w = (struct ctk_widget *)data;
419 if(s == ctk_signal_button_activate) {
420 if(w == (struct ctk_widget *)&backbutton) {
421 scrolly = 0;
422 run = 1;
423
424 --history_last;
adamdunkels17e84a32003-04-02 09:54:39 +0000425 if(history_last > WWW_CONF_HISTORY_SIZE) {
426 history_last = WWW_CONF_HISTORY_SIZE - 1;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000427 }
adamdunkels30c449e2003-07-31 23:12:05 +0000428 memcpy(url, history[(int)history_last], WWW_CONF_MAX_URLLEN);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000429 open_url();
430 CTK_WIDGET_FOCUS(&mainwindow, &backbutton);
431 } else if(w == (struct ctk_widget *)&downbutton) {
432 run = 1;
433 open_url();
434 CTK_WIDGET_FOCUS(&mainwindow, &downbutton);
435 } else if(w == (struct ctk_widget *)&gobutton) {
436 scrolly = 0;
adamdunkels30c449e2003-07-31 23:12:05 +0000437
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000438 run = 1;
439 log_back();
440 memcpy(url, editurl, WWW_CONF_MAX_URLLEN);
441 petsciiconv_toascii(url, WWW_CONF_MAX_URLLEN);
442 open_url();
443 CTK_WIDGET_FOCUS(&mainwindow, &gobutton);
444 } else if(w == (struct ctk_widget *)&stopbutton) {
445 run = 0;
446 webclient_close();
447#if WWW_CONF_FORMS
448 } else {
449 /* Check form buttons */
450 for(i = 0; i < pagewidgetptr; ++i) {
451 if(&pagewidgets[i] == w) {
452 formsubmit(&pagewidgetattribs[i].form);
453 /* show_statustext(pagewidgetattribs[i].form.formaction);*/
454 /* PRINTF(("Formaction %s formname %s inputname %s\n",
455 pagewidgetattribs[i].form.formaction,
456 pagewidgetattribs[i].form.formname,
457 pagewidgetattribs[i].form.inputname));*/
458 break;
459 }
460 }
461#endif /* WWW_CONF_FORMS */
462 }
463 } else if(s == ctk_signal_hyperlink_activate) {
464 log_back();
465 open_link(w->widget.hyperlink.url);
466 CTK_WIDGET_FOCUS(&mainwindow, &stopbutton);
adamdunkelsaceb9112003-03-28 12:09:13 +0000467 ctk_window_open(&mainwindow);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000468 run = 1;
469 } else if(s == ctk_signal_hyperlink_hover) {
adamdunkelsc4bf5ca2003-04-18 00:20:22 +0000470 if(CTK_WIDGET_TYPE((struct ctk_widget *)data) == CTK_WIDGET_HYPERLINK) {
471 strncpy(statustexturl, w->widget.hyperlink.url,
472 sizeof(statustexturl));
473 petsciiconv_topetscii(statustexturl, sizeof(statustexturl));
474 show_statustext(statustexturl);
475 }
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000476 } else if(s == resolv_signal_found) {
477 /* Either found a hostname, or not. */
478 if((char *)data != NULL &&
479 resolv_lookup((char *)data) != NULL) {
480 open_url();
481 } else {
482 show_statustext("Host not found.");
483 }
adamdunkels30c449e2003-07-31 23:12:05 +0000484 } else if(s == ctk_signal_window_close ||
485 s == dispatcher_signal_quit) {
486 ctk_window_close(&mainwindow);
adamdunkels62b64992003-04-08 07:20:11 +0000487 dispatcher_exit(&p);
adamdunkels95df2892003-04-08 19:37:35 +0000488 id = EK_ID_NONE;
adamdunkels8af703e2003-04-08 11:50:20 +0000489 LOADER_UNLOAD();
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000490 }
491}
492/*-----------------------------------------------------------------------------------*/
493/* set_url():
494 *
495 * Constructs an URL from the arguments and puts it into the global
496 * "url" variable and the visible "editurl" (which is shown in the URL
497 * text entry widget in the browser window).
498 */
499static void
500set_url(char *host, u16_t port, char *file)
501{
502 char *urlptr;
503
504 memset(url, 0, WWW_CONF_MAX_URLLEN);
505
506 if(strncmp(file, http_http, 7) == 0) {
507 strncpy(url, file, sizeof(url));
508 } else {
509 strncpy(url, http_http, 7);
510 urlptr = url + 7;
511 strcpy(urlptr, host);
512 urlptr += strlen(host);
513 strcpy(urlptr, file);
514 }
515
516 show_url();
517}
518/*-----------------------------------------------------------------------------------*/
519/* webclient_aborted():
520 *
521 * Callback function. Called from the webclient when the HTTP
522 * connection was abruptly aborted.
523 */
524void
525webclient_aborted(void)
526{
527 show_statustext("Connection reset by peer");
528}
529/*-----------------------------------------------------------------------------------*/
530/* webclient_timedout():
531 *
532 * Callback function. Called from the webclient when the HTTP
533 * connection timed out.
534 */
535void
536webclient_timedout(void)
537{
538 show_statustext("Connection timed out");
539}
540/*-----------------------------------------------------------------------------------*/
541/* webclient_closed():
542 *
543 * Callback function. Called from the webclient when the HTTP
544 * connection was closed after a request from the "webclient_close()"
545 * function. .
546 */
547void
548webclient_closed(void)
549{
550 show_statustext("Stopped.");
adamdunkels17e84a32003-04-02 09:54:39 +0000551 petsciiconv_topetscii(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
552 WWW_CONF_WEBPAGE_WIDTH], WWW_CONF_WEBPAGE_WIDTH);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000553 redraw_window();
554}
555/*-----------------------------------------------------------------------------------*/
556/* webclient_closed():
557 *
558 * Callback function. Called from the webclient when the HTTP
559 * connection is connected.
560 */
561void
562webclient_connected(void)
563{
564 x = nextwordptr = 0;
565 starty = scrolly;
adamdunkels30c449e2003-07-31 23:12:05 +0000566#if WWW_CONF_PAGEVIEW
567 if(starty == 0) {
568 scrollend = WWW_CONF_WEBPAGE_HEIGHT - 4;
569 } else {
570 scrollend = starty + WWW_CONF_WEBPAGE_HEIGHT - 4;
571 }
572#endif /* WWW_CONF_PAGEVIEW */
573
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000574 nextword[0] = 0;
575
576 if(scrolly == 0) {
577 clear_page();
578 redraw_window();
579 }
adamdunkels30c449e2003-07-31 23:12:05 +0000580
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000581 show_statustext("Request sent...");
582 set_url(webclient_hostname(), webclient_port(), webclient_filename());
583
584#if WWW_CONF_RENDERSTATE
585 renderstate = HTMLPARSER_RENDERSTATE_NONE;
586#endif /* WWW_CONF_RENDERSTATE */
587 htmlparser_init();
588}
589/*-----------------------------------------------------------------------------------*/
590/* scroll():
591 *
592 * Scrolls the entire web page display (text and hyperlinks) one line
593 * upwards.
594 */
595static void
596scroll(void)
597{
598 unsigned char i;
599 unsigned char lptr, linkptrtmp;
600 struct ctk_widget *linksptr;
601 char *statustexttext;
602 struct ctk_widget *focuswidget;
adamdunkels30c449e2003-07-31 23:12:05 +0000603
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000604 /* Scroll text up. */
adamdunkels17e84a32003-04-02 09:54:39 +0000605 memcpy(webpage, &webpage[WWW_CONF_WEBPAGE_WIDTH],
606 (WWW_CONF_WEBPAGE_HEIGHT - 1) * WWW_CONF_WEBPAGE_WIDTH);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000607
608 /* Clear last line of text. */
adamdunkels30c449e2003-07-31 23:12:05 +0000609 memset(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
610 WWW_CONF_WEBPAGE_WIDTH],
adamdunkels17e84a32003-04-02 09:54:39 +0000611 ' ', WWW_CONF_WEBPAGE_WIDTH);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000612
adamdunkels30c449e2003-07-31 23:12:05 +0000613 /* Scroll links and form widgets up. */
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000614
615 lptr = 0;
616 linksptr = pagewidgets;
617 for(i = 0; i < pagewidgetptr; ++i) {
618 /* First, check which links that scroll off the top of the page
619 and should be removed. */
620 if(CTK_WIDGET_YPOS(linksptr) == 3) {
621 lptr = i + 1;
622 } else {
623 /* Else, move them upward one notch. */
624
625 /* XXX: this is really a hack! These values should not be used
626 like this, but should be obtained and set using some CTK API
627 function. */
adamdunkels17e84a32003-04-02 09:54:39 +0000628 linksptr->widget.hyperlink.text -= WWW_CONF_WEBPAGE_WIDTH;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000629
630 --(linksptr->y);
631 }
632 ++linksptr;
633 }
634
635 /* See if there are any links that scroll off the top. */
636 if(lptr != 0) {
637 memcpy(pagewidgets, &pagewidgets[lptr],
adamdunkels17e84a32003-04-02 09:54:39 +0000638 sizeof(struct ctk_widget) * (WWW_CONF_MAX_NUMPAGEWIDGETS - lptr));
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000639 memcpy(pagewidgetattribs, &pagewidgetattribs[lptr],
adamdunkels17e84a32003-04-02 09:54:39 +0000640 sizeof(union pagewidgetattrib) * (WWW_CONF_MAX_NUMPAGEWIDGETS - lptr));
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000641
642 /* Compute new value of linkptr and tuck it away in
643 linkptrtmp. make_window() destroys linkptr, so we need to
644 restore it after the call. */
645 linkptrtmp = pagewidgetptr - lptr;
646
647 /* XXX: hack - these values should *not* be obtained this way, but
648 through some CTK API instead! */
649 statustexttext = statustext.text;
650 focuswidget = mainwindow.focused;
651 ctk_window_clear(&mainwindow);
652 make_window();
653 CTK_WIDGET_FOCUS(&mainwindow, focuswidget);
654 show_statustext(statustexttext);
655
656 pagewidgetptr = linkptrtmp;
657
658 linksptr = pagewidgets;
659 for(i = 0; i < pagewidgetptr; ++i) {
660 if(linksptr->type == CTK_WIDGET_HYPERLINK) {
661 linksptr->widget.hyperlink.url -= lptr *
662 sizeof(union pagewidgetattrib);
663 }
664 CTK_WIDGET_ADD(&mainwindow, linksptr);
665 ++linksptr;
666 }
667 }
668}
669/*-----------------------------------------------------------------------------------*/
adamdunkels17e84a32003-04-02 09:54:39 +0000670static char tmpcenterline[WWW_CONF_WEBPAGE_WIDTH];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000671/* inc_y():
672 *
673 * Called from the rendering code when it is time to move on line
674 * downwards.
675 */
676static void
677inc_y(void)
678{
679 unsigned char spaces, i;
680 char *cptr;
681 struct ctk_widget *linksptr;
682
683 if(starty > 0) {
684 --starty;
685 } else {
686#if WWW_CONF_RENDERSTATE
687 /* Check if current line should be centered and if so, center
688 it. */
689 if(renderstate & HTMLPARSER_RENDERSTATE_CENTER) {
adamdunkels30c449e2003-07-31 23:12:05 +0000690 cptr = &webpage[(WWW_CONF_WEBPAGE_HEIGHT - 0) *
691 WWW_CONF_WEBPAGE_WIDTH - 1];
adamdunkels17e84a32003-04-02 09:54:39 +0000692 for(spaces = 0; spaces < WWW_CONF_WEBPAGE_WIDTH; ++spaces) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000693 if(*cptr-- != ' ') {
694 break;
695 }
696 }
697
698 spaces = spaces / 2;
699
adamdunkels30c449e2003-07-31 23:12:05 +0000700 memcpy(tmpcenterline,
adamdunkels17e84a32003-04-02 09:54:39 +0000701 &webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
702 WWW_CONF_WEBPAGE_WIDTH],
703 WWW_CONF_WEBPAGE_WIDTH);
adamdunkels30c449e2003-07-31 23:12:05 +0000704 memcpy(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
adamdunkels17e84a32003-04-02 09:54:39 +0000705 WWW_CONF_WEBPAGE_WIDTH] + spaces,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000706 tmpcenterline,
adamdunkels17e84a32003-04-02 09:54:39 +0000707 WWW_CONF_WEBPAGE_WIDTH - spaces);
708 memset(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
709 WWW_CONF_WEBPAGE_WIDTH], ' ', spaces);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000710
711 linksptr = pagewidgets;
712 for(i = 0; i < pagewidgetptr; ++i) {
adamdunkels17e84a32003-04-02 09:54:39 +0000713 if(CTK_WIDGET_YPOS(linksptr) == 3 + WWW_CONF_WEBPAGE_HEIGHT - 1) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000714 linksptr->x += spaces;
715 linksptr->widget.hyperlink.text += spaces;
716 }
717 ++linksptr;
718 }
719 }
720#endif /* WWW_CONF_RENDERSTATE */
721
adamdunkels17e84a32003-04-02 09:54:39 +0000722 petsciiconv_topetscii(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
723 WWW_CONF_WEBPAGE_WIDTH], WWW_CONF_WEBPAGE_WIDTH);
adamdunkels30c449e2003-07-31 23:12:05 +0000724 /* redraw_window();*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000725 scroll();
726 ++scrolly;
adamdunkels30c449e2003-07-31 23:12:05 +0000727#if WWW_CONF_PAGEVIEW
728 if(scrolly == scrollend) {
729 run = 0;
730 webclient_close();
731 }
732#endif /* WWW_CONF_PAGEVIEW */
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000733 }
734}
735/*-----------------------------------------------------------------------------------*/
736/* webclient_datahandler():
737 *
738 * Callback function. Called from the webclient module when HTTP data
739 * has arrived.
740 */
741void
742webclient_datahandler(char *data, u16_t len)
743{
744 if(len > 0) {
745 /* if(strcmp(webclient_mimetype(), http_texthtml) == 0) {*/
adamdunkels30c449e2003-07-31 23:12:05 +0000746 count = (count + 1) & 3;
747 show_statustext(receivingmsgs[count]);
748 htmlparser_parse(data, len);
749 /* } else {
750 show_statustext("Receiving non-HTML data...");
751 }*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000752 } else {
753 /* Clear remaining parts of page. */
754 run = 0;
755 }
756
757 if(data == NULL) {
758 run = 0;
759 show_statustext("Done.");
adamdunkels17e84a32003-04-02 09:54:39 +0000760 petsciiconv_topetscii(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
761 WWW_CONF_WEBPAGE_WIDTH], WWW_CONF_WEBPAGE_WIDTH);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000762 redraw_window();
763 }
764}
765/*-----------------------------------------------------------------------------------*/
766/* output_word():
767 *
768 * Called from the rendering code when a full word has been received
769 * and should be put on screen.
770 */
771static void
772output_word(char c)
773{
774 char *webpageptr;
775
776 if(nextwordptr == 0) {
777 if(c == ISO_nl) {
778 x = 0;
779 inc_y();
780 }
781 return;
782 }
783
adamdunkels17e84a32003-04-02 09:54:39 +0000784 if(x + nextwordptr > WWW_CONF_WEBPAGE_WIDTH) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000785 inc_y();
786 x = 0;
787 }
788
789 nextword[nextwordptr] = 0;
790 if(starty == 0) {
adamdunkels17e84a32003-04-02 09:54:39 +0000791 webpageptr = &webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) * WWW_CONF_WEBPAGE_WIDTH + x];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000792 if(nextwordptr > 0) {
793 strcpy(webpageptr, nextword);
794 }
795 webpageptr[nextwordptr] = ' ';
796 }
797 if(c == ISO_nl) {
798 x = 0;
799 inc_y();
800 } else {
801 x += nextwordptr + 1;
802 }
803 nextwordptr = 0;
804}
805/*-----------------------------------------------------------------------------------*/
806static void *
807add_pagewidget(char *text, unsigned char type,
808 unsigned char border)
809{
810 static unsigned char len, maxwidth;
811 static unsigned char *webpageptr;
812 static void *dataptr;
813 register struct ctk_widget *lptr;
814
815 len = strlen(text);
816
817 if(len + border == 0) {
818 return NULL;
819 }
820
adamdunkels17e84a32003-04-02 09:54:39 +0000821 maxwidth = WWW_CONF_WEBPAGE_WIDTH - (1 + 2 * border);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000822
823 /* If the text of the link is too long so that it does not fit into
824 the width of the current window, counting from the current x
825 coordinate, we first try to jump to the next line. */
826 if(len + x > maxwidth) {
827 output_word(ISO_nl);
828 }
829
830 /* If the text of the link still is too long, we just chop it off!
831 XXX: this is not really the right thing to do, we should probably
832 either make the link a multiline button, or add multiple
833 buttons. But this will do for now. */
834 if(len > maxwidth) {
835 text[maxwidth] = 0;
836 len = maxwidth;
837 }
838
839 dataptr = NULL;
840
841 if(starty == 0) {
adamdunkels30c449e2003-07-31 23:12:05 +0000842 webpageptr = &webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
843 WWW_CONF_WEBPAGE_WIDTH + x];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000844 /* To save memory, we'll copy the widget text to the web page
845 drawing area and reference it from there. */
846 webpageptr[0] = 0;
847 webpageptr += border;
848 strncpy(webpageptr, text, len);
849 webpageptr[len] = 0;
850 webpageptr[len + border] = ' ';
adamdunkels17e84a32003-04-02 09:54:39 +0000851 if(pagewidgetptr < WWW_CONF_MAX_NUMPAGEWIDGETS) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000852 dataptr = &pagewidgetattribs[pagewidgetptr];
853 lptr = &pagewidgets[pagewidgetptr];
854
855 switch(type) {
856 case CTK_WIDGET_HYPERLINK:
857 CTK_HYPERLINK_NEW((struct ctk_hyperlink *)lptr, x,
adamdunkels17e84a32003-04-02 09:54:39 +0000858 WWW_CONF_WEBPAGE_HEIGHT + 2, len,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000859 webpageptr, dataptr);
860 break;
861 case CTK_WIDGET_BUTTON:
862 CTK_BUTTON_NEW((struct ctk_button *)lptr, x,
adamdunkels17e84a32003-04-02 09:54:39 +0000863 WWW_CONF_WEBPAGE_HEIGHT + 2, len,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000864 webpageptr);
865 ((struct formattribs *)dataptr)->inputvalue = webpageptr;
866 break;
867 case CTK_WIDGET_TEXTENTRY:
868 CTK_TEXTENTRY_NEW((struct ctk_textentry *)lptr,
adamdunkels17e84a32003-04-02 09:54:39 +0000869 x, WWW_CONF_WEBPAGE_HEIGHT + 2, len, 1,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000870 webpageptr, len);
871 ((struct formattribs *)dataptr)->inputvalue = webpageptr;
872 break;
873 }
874 CTK_WIDGET_ADD(&mainwindow, lptr);
875
876 ++pagewidgetptr;
877 }
878 }
879 /* Increase the x coordinate with the length of the link text plus
880 the extra space behind it and the CTK button markers. */
881 x += len + 1 + 2 * border;
882
adamdunkels17e84a32003-04-02 09:54:39 +0000883 if(x >= WWW_CONF_WEBPAGE_WIDTH) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000884 inc_y();
885 x = 0;
886 }
887
888 return dataptr;
889}
890/*-----------------------------------------------------------------------------------*/
891/* htmlparser_link:
892 *
893 * Callback function. Will be called when the HTML parser has parsed a
894 * link. We will put a CTK hyperlink widget at the appropriate position
895 * in the window.
896 */
897void
898htmlparser_link(char *text, char *url)
899{
900 static unsigned char *linkurlptr;
901
902 linkurlptr = add_pagewidget(text, CTK_WIDGET_HYPERLINK, 0);
903 if(linkurlptr != NULL &&
904 strlen(url) < WWW_CONF_MAX_URLLEN) {
905 strcpy(linkurlptr, url);
906 }
907}
908/*-----------------------------------------------------------------------------------*/
909/* htmlparser_char():
910 *
911 * Callback function. Called by the HTML parser module for every
912 * printable character in the HTML file.
913 */
914void
915htmlparser_char(char c)
916{
917 if(c == ' ' ||
918 c == ISO_nl) {
919 output_word(c);
adamdunkels30c449e2003-07-31 23:12:05 +0000920 } else if(c != 0 && (c & 0x80) == 0) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000921 nextword[nextwordptr] = c;
adamdunkels17e84a32003-04-02 09:54:39 +0000922 if(nextwordptr < WWW_CONF_WEBPAGE_WIDTH) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000923 ++nextwordptr;
924 }
925 }
926}
927/*-----------------------------------------------------------------------------------*/
928#if WWW_CONF_RENDERSTATE
929void
930htmlparser_renderstate(unsigned char s)
931{
932 if((s & HTMLPARSER_RENDERSTATE_STATUSMASK) ==
933 HTMLPARSER_RENDERSTATE_BEGIN) {
934 renderstate |= s & ~HTMLPARSER_RENDERSTATE_STATUSMASK;
935 } else {
936 renderstate &= ~(s & ~HTMLPARSER_RENDERSTATE_STATUSMASK);
937 }
938}
939#endif /* WWW_CONF_RENDERSTATE */
940
941#if WWW_CONF_FORMS
942/*-----------------------------------------------------------------------------------*/
943void
944htmlparser_submitbutton(char *text, char *name,
945 char *formname, char *formaction)
946{
947 register struct formattribs *form;
adamdunkels30c449e2003-07-31 23:12:05 +0000948
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000949 form = add_pagewidget(text, CTK_WIDGET_BUTTON, 1);
950 if(form != NULL) {
951 strncpy(form->formaction, formaction, WWW_CONF_MAX_FORMACTIONLEN);
952 strncpy(form->formname, formname, WWW_CONF_MAX_FORMNAMELEN);
953 strncpy(form->inputname, name, WWW_CONF_MAX_INPUTNAMELEN);
954 form->inputtype = FORMINPUTTYPE_SUBMITBUTTON;
955 }
adamdunkels30c449e2003-07-31 23:12:05 +0000956
957
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000958}
959/*-----------------------------------------------------------------------------------*/
960void
961htmlparser_inputfield(char *text, char *name,
962 char *formname, char *formaction)
963{
964 register struct formattribs *form;
965
966 form = add_pagewidget(text, CTK_WIDGET_TEXTENTRY, 1);
967 if(form != NULL) {
968 strncpy(form->formaction, formaction, WWW_CONF_MAX_FORMACTIONLEN);
969 strncpy(form->formname, formname, WWW_CONF_MAX_FORMNAMELEN);
970 strncpy(form->inputname, name, WWW_CONF_MAX_INPUTNAMELEN);
971 form->inputtype = FORMINPUTTYPE_INPUTFIELD;
972 }
973}
974/*-----------------------------------------------------------------------------------*/
975static void
976formsubmit(struct formattribs *attribs)
977{
978 unsigned char i, j;
979 char *urlptr, *valueptr;
980 struct formattribs *faptr;
981
982 urlptr = &tmpurl[0];
983
984 strncpy(urlptr, attribs->formaction, WWW_CONF_MAX_URLLEN);
985 tmpurl[WWW_CONF_MAX_URLLEN] = 0;
986 urlptr += strlen(urlptr);
987 *urlptr = ISO_questionmark;
988 ++urlptr;
989
990
991 /* Construct an URL by finding all input field forms with the same
992 formname as the current submit button, and add the submit button
993 URL stuff as well. */
994 for(i = 0; i < pagewidgetptr; ++i) {
995 if(urlptr - &tmpurl[0] >= WWW_CONF_MAX_URLLEN) {
996 break;
997 }
998
999 faptr = &pagewidgetattribs[i].form;
1000
1001 if(strcmp(attribs->formaction, faptr->formaction) == 0 &&
1002 strcmp(attribs->formname, faptr->formname) == 0 &&
1003 (faptr->inputtype == FORMINPUTTYPE_INPUTFIELD ||
1004 faptr == attribs)) {
1005
1006 /* Copy the name of the input field into the URL and append a
1007 questionmark. */
1008 strncpy(urlptr, faptr->inputname, WWW_CONF_MAX_URLLEN - strlen(tmpurl));
1009 tmpurl[WWW_CONF_MAX_URLLEN] = 0;
1010 urlptr += strlen(urlptr);
1011 *urlptr = ISO_eq;
1012 ++urlptr;
1013
1014 /* Convert and copy the contents of the input field to the URL
1015 and append an ampersand. */
1016 valueptr = pagewidgets[i].widget.textentry.text;
1017 petsciiconv_toascii(valueptr, WWW_CONF_MAX_INPUTVALUELEN);
1018 for(j = 0; j < WWW_CONF_MAX_INPUTVALUELEN; ++j) {
1019 if(urlptr - &tmpurl[0] >= WWW_CONF_MAX_URLLEN) {
1020 break;
1021 }
1022 *urlptr = *valueptr;
1023 if(*urlptr == ISO_space) {
1024 *urlptr = ISO_plus;
1025 }
1026 if(*urlptr == 0) {
1027 break;
1028 }
1029 ++urlptr;
1030 ++valueptr;
1031 }
1032
1033 *urlptr = ISO_ampersand;
1034 ++urlptr;
1035 }
1036 }
1037 --urlptr;
1038 *urlptr = 0;
1039 /* PRINTF(("formsubmit: URL '%s'\n", tmpurl));*/
1040 open_link(tmpurl);
1041}
1042/*-----------------------------------------------------------------------------------*/
1043#endif /* WWW_CONF_FORMS */