blob: 1f81826884bcddf05ff10742c014b8a68271a8b3 [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 *
adamdunkelsc4bf5ca2003-04-18 00:20:22 +000035 * $Id: www.c,v 1.10 2003/04/18 00:20:22 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
53#if 0
54#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
125#if WWW_CONF_RENDERSTATE
126static unsigned char renderstate;
127#endif /* WWW_CONF_RENDERSTATE */
128
129/* The "run" flag is used to determine if the web page should be
130 continuosly scrolled upward with new data coming in from below. */
131static unsigned char run;
132
133#define ISO_nl 0x0a
134#define ISO_space 0x20
135#define ISO_ampersand 0x26
136#define ISO_plus 0x2b
137#define ISO_slash 0x2f
138#define ISO_eq 0x3d
139#define ISO_questionmark 0x3f
140
141/* The state of the rendering code. */
142static u8_t x;
143static u16_t starty;
adamdunkels17e84a32003-04-02 09:54:39 +0000144static char nextword[WWW_CONF_WEBPAGE_WIDTH + 1];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000145static unsigned char nextwordptr;
146
147static unsigned char count;
148static char receivingmsgs[4][23] = {
149 "Receiving web page ...",
150 "Receiving web page. ..",
151 "Receiving web page.. .",
152 "Receiving web page... "
153};
154
155
adamdunkels78c03dc2003-04-09 13:45:05 +0000156static DISPATCHER_SIGHANDLER(www_sighandler, s, data);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000157static struct dispatcher_proc p =
adamdunkels78c03dc2003-04-09 13:45:05 +0000158 {DISPATCHER_PROC("Web browser", NULL, www_sighandler, webclient_appcall)};
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000159static ek_id_t id;
160
161
162static void formsubmit(struct formattribs *attribs);
163/*-----------------------------------------------------------------------------------*/
164/* make_window()
165 *
166 * Creates the web browser's window.
167 */
168static void
169make_window(void)
170{
171
172 CTK_WIDGET_ADD(&mainwindow, &backbutton);
173 CTK_WIDGET_ADD(&mainwindow, &downbutton);
174 CTK_WIDGET_ADD(&mainwindow, &stopbutton);
175 CTK_WIDGET_ADD(&mainwindow, &gobutton);
176 CTK_WIDGET_ADD(&mainwindow, &urlentry);
177 CTK_WIDGET_ADD(&mainwindow, &sep1);
178 CTK_WIDGET_ADD(&mainwindow, &webpagelabel);
179 CTK_WIDGET_ADD(&mainwindow, &sep2);
180 CTK_WIDGET_ADD(&mainwindow, &statustext);
181
182 CTK_WIDGET_FOCUS(&mainwindow, &stopbutton);
183
184 pagewidgetptr = 0;
185}
186/*-----------------------------------------------------------------------------------*/
187/* redraw_window():
188 *
189 * Convenience function that calls upon CTK to redraw the browser
190 * window. */
191static void
192redraw_window(void)
193{
194 ctk_window_redraw(&mainwindow);
195}
196/*-----------------------------------------------------------------------------------*/
197/* www_init();
198 *
199 * Initializes and starts the web browser. Called either at startup or
200 * to open the browser window.
201 */
adamdunkels1cd53bd2003-04-08 19:24:55 +0000202LOADER_INIT_FUNC(www_init)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000203{
204 if(id == EK_ID_NONE) {
205 id = dispatcher_start(&p);
206
207 /* Create the main window. */
208 memset(webpage, 0, sizeof(webpage));
209 ctk_window_new(&mainwindow, 36, 22, "WWW");
210 make_window();
211 CTK_WIDGET_FOCUS(&mainwindow, &urlentry);
212
213 /* Attach as a listener to a number of signals ("Button activate",
214 "Hyperlink activate" and "Hyperlink hover", and the resolver's
215 signal. */
adamdunkels62b64992003-04-08 07:20:11 +0000216 dispatcher_listen(ctk_signal_window_close);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000217 dispatcher_listen(ctk_signal_button_activate);
218 dispatcher_listen(ctk_signal_hyperlink_activate);
219 dispatcher_listen(ctk_signal_hyperlink_hover);
220 dispatcher_listen(resolv_signal_found);
221 }
222 ctk_window_open(&mainwindow);
223}
224/*-----------------------------------------------------------------------------------*/
225static void
226clear_page(void)
227{
228 if(ctk_window_isopen(&mainwindow)) {
229 ctk_window_close(&mainwindow);
230 }
231 ctk_window_clear(&mainwindow);
232 make_window();
233 ctk_window_open(&mainwindow);
adamdunkels17e84a32003-04-02 09:54:39 +0000234 memset(webpage, 0, WWW_CONF_WEBPAGE_WIDTH * WWW_CONF_WEBPAGE_HEIGHT);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000235}
236/*-----------------------------------------------------------------------------------*/
237static void
238show_url(void)
239{
240 memcpy(editurl, url, WWW_CONF_MAX_URLLEN);
241 strncpy(editurl, "http://", 7);
242 petsciiconv_topetscii(editurl + 7, WWW_CONF_MAX_URLLEN - 7);
243 CTK_WIDGET_REDRAW(&urlentry);
244}
245/*-----------------------------------------------------------------------------------*/
246static void
247show_statustext(char *text)
248{
249 ctk_label_set_text(&statustext, text);
250 CTK_WIDGET_REDRAW(&statustext);
251}
252/*-----------------------------------------------------------------------------------*/
253/* open_url():
254 *
255 * Called when the URL present in the global "url" variable should be
256 * opened. It will call the hostname resolver as well as the HTTP
257 * client requester.
258 */
259static void
260open_url(void)
261{
262 unsigned char i;
263 static char host[32];
264 char *file;
265 register char *urlptr;
266 unsigned short port;
267
268 /* Trim off any spaces in the end of the url. */
269 urlptr = url + strlen(url) - 1;
270 while(*urlptr == ' ' && urlptr > url) {
271 *urlptr = 0;
272 --urlptr;
273 }
274
275 /* Don't even try to go further if the URL is empty. */
276 if(urlptr == url) {
277 return;
278 }
279
280 /* See if the URL starts with http://, otherwise prepend it. */
281 if(strncmp(url, http_http, 7) != 0) {
282 while(urlptr >= url) {
283 *(urlptr + 7) = *urlptr;
284 --urlptr;
285 }
286 strncpy(url, http_http, 7);
287 }
288
289 /* Find host part of the URL. */
290 urlptr = &url[7];
291 for(i = 0; i < sizeof(host); ++i) {
292 if(*urlptr == 0 ||
293 *urlptr == '/' ||
294 *urlptr == ' ' ||
295 *urlptr == ':') {
296 host[i] = 0;
297 break;
298 }
299 host[i] = *urlptr;
300 ++urlptr;
301 }
302
303 /* XXX: Here we should find the port part of the URL, but this isn't
304 currently done because of laziness from the programmer's side
305 :-) */
306
307 /* Find file part of the URL. */
308 while(*urlptr != '/' && *urlptr != 0) {
309 ++urlptr;
310 }
311 if(*urlptr == '/') {
312 file = urlptr;
313 } else {
314 file = "/";
315 }
316
317 /* Try to lookup the hostname. If it fails, we initiate a hostname
318 lookup and print out an informative message on the statusbar. */
319 if(resolv_lookup(host) == NULL) {
320 resolv_query(host);
321 show_statustext("Resolving host...");
322 return;
323 }
324
325 /* The hostname we present in the hostname table, so we send out the
326 initial GET request. */
327 if(webclient_get(host, 80, file) == 0) {
328 show_statustext("Out of memory error.");
329 } else {
330 show_statustext("Connecting...");
331 }
332 redraw_window();
333}
334/*-----------------------------------------------------------------------------------*/
335/* open_link(link):
336 *
337 * Will format a link from the current web pages so that it suits the
338 * open_url() function and finally call it to open the requested URL.
339 */
340static void
341open_link(char *link)
342{
343 char *urlptr;
344
345 if(strncmp(link, http_http, 7) == 0) {
346 /* The link starts with http://. We just copy the contents of the
347 link into the url string and jump away. */
348 strncpy(url, link, WWW_CONF_MAX_URLLEN);
349 } else if(*link == ISO_slash &&
350 *(link + 1) == ISO_slash) {
351 /* The link starts with //, so we'll copy it into the url
352 variable, starting after the http (which already is present in
353 the url variable since we were able to open the web page on
354 which this link was found in the first place). */
355 strncpy(&url[5], link, WWW_CONF_MAX_URLLEN);
356 } else if(*link == ISO_slash) {
357 /* The link starts with a slash, so it is a non-relative link
358 within the same web site. We find the start of the filename of
359 the current URL and paste the contents of this link there, and
360 head off to the new URL. */
361 for(urlptr = &url[7];
362 *urlptr != 0 && *urlptr != ISO_slash;
363 ++urlptr);
364 strncpy(urlptr, link, WWW_CONF_MAX_URLLEN - (urlptr - url));
365 } else {
366 /* A fully relative link is found. We find the last slash in the
367 current URL and paste the link there. */
368
369 /* XXX: we should really parse any ../ in the link as well. */
370 for(urlptr = url + strlen(url);
371 urlptr != url && *urlptr != ISO_slash;
372 --urlptr);
373 ++urlptr;
374 strncpy(urlptr, link, WWW_CONF_MAX_URLLEN - (urlptr - url));
375 }
376
377 /* Open the URL. */
378 scrolly = 0;
379 show_url();
380 open_url();
381}
382/*-----------------------------------------------------------------------------------*/
383/* log_back():
384 *
385 * Copies the current URL from the url variable and into the log for
386 * the back button.
387 */
388static void
389log_back(void)
390{
391 memcpy(history[history_last], url, WWW_CONF_MAX_URLLEN);
392 ++history_last;
adamdunkelsc11c62d2003-04-15 21:24:14 +0000393 if(history_last >= WWW_CONF_HISTORY_SIZE) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000394 history_last = 0;
395 }
396}
397/*-----------------------------------------------------------------------------------*/
398/* www_dispatcher():
399 *
400 * The program's signal dispatcher function. Is called by the ek
401 * dispatcher whenever a signal arrives.
402 */
adamdunkels78c03dc2003-04-09 13:45:05 +0000403static
404DISPATCHER_SIGHANDLER(www_sighandler, s, data)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000405{
406 static struct ctk_widget *w;
407 static unsigned char i;
adamdunkels78c03dc2003-04-09 13:45:05 +0000408 DISPATCHER_SIGHANDLER_ARGS(s, data);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000409
410
411 w = (struct ctk_widget *)data;
412 if(s == ctk_signal_button_activate) {
413 if(w == (struct ctk_widget *)&backbutton) {
414 scrolly = 0;
415 run = 1;
416
417 --history_last;
adamdunkels17e84a32003-04-02 09:54:39 +0000418 if(history_last > WWW_CONF_HISTORY_SIZE) {
419 history_last = WWW_CONF_HISTORY_SIZE - 1;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000420 }
421 memcpy(url, history[history_last], WWW_CONF_MAX_URLLEN);
422 open_url();
423 CTK_WIDGET_FOCUS(&mainwindow, &backbutton);
424 } else if(w == (struct ctk_widget *)&downbutton) {
425 run = 1;
426 open_url();
427 CTK_WIDGET_FOCUS(&mainwindow, &downbutton);
428 } else if(w == (struct ctk_widget *)&gobutton) {
429 scrolly = 0;
430 run = 1;
431 log_back();
432 memcpy(url, editurl, WWW_CONF_MAX_URLLEN);
433 petsciiconv_toascii(url, WWW_CONF_MAX_URLLEN);
434 open_url();
435 CTK_WIDGET_FOCUS(&mainwindow, &gobutton);
436 } else if(w == (struct ctk_widget *)&stopbutton) {
437 run = 0;
438 webclient_close();
439#if WWW_CONF_FORMS
440 } else {
441 /* Check form buttons */
442 for(i = 0; i < pagewidgetptr; ++i) {
443 if(&pagewidgets[i] == w) {
444 formsubmit(&pagewidgetattribs[i].form);
445 /* show_statustext(pagewidgetattribs[i].form.formaction);*/
446 /* PRINTF(("Formaction %s formname %s inputname %s\n",
447 pagewidgetattribs[i].form.formaction,
448 pagewidgetattribs[i].form.formname,
449 pagewidgetattribs[i].form.inputname));*/
450 break;
451 }
452 }
453#endif /* WWW_CONF_FORMS */
454 }
455 } else if(s == ctk_signal_hyperlink_activate) {
456 log_back();
457 open_link(w->widget.hyperlink.url);
458 CTK_WIDGET_FOCUS(&mainwindow, &stopbutton);
adamdunkelsaceb9112003-03-28 12:09:13 +0000459 ctk_window_open(&mainwindow);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000460 run = 1;
461 } else if(s == ctk_signal_hyperlink_hover) {
adamdunkelsc4bf5ca2003-04-18 00:20:22 +0000462 if(CTK_WIDGET_TYPE((struct ctk_widget *)data) == CTK_WIDGET_HYPERLINK) {
463 strncpy(statustexturl, w->widget.hyperlink.url,
464 sizeof(statustexturl));
465 petsciiconv_topetscii(statustexturl, sizeof(statustexturl));
466 show_statustext(statustexturl);
467 }
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000468 } else if(s == resolv_signal_found) {
469 /* Either found a hostname, or not. */
470 if((char *)data != NULL &&
471 resolv_lookup((char *)data) != NULL) {
472 open_url();
473 } else {
474 show_statustext("Host not found.");
475 }
adamdunkels62b64992003-04-08 07:20:11 +0000476 } else if(s == ctk_signal_window_close) {
477 dispatcher_exit(&p);
adamdunkels95df2892003-04-08 19:37:35 +0000478 id = EK_ID_NONE;
adamdunkels8af703e2003-04-08 11:50:20 +0000479 LOADER_UNLOAD();
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000480 }
481}
482/*-----------------------------------------------------------------------------------*/
483/* set_url():
484 *
485 * Constructs an URL from the arguments and puts it into the global
486 * "url" variable and the visible "editurl" (which is shown in the URL
487 * text entry widget in the browser window).
488 */
489static void
490set_url(char *host, u16_t port, char *file)
491{
492 char *urlptr;
493
494 memset(url, 0, WWW_CONF_MAX_URLLEN);
495
496 if(strncmp(file, http_http, 7) == 0) {
497 strncpy(url, file, sizeof(url));
498 } else {
499 strncpy(url, http_http, 7);
500 urlptr = url + 7;
501 strcpy(urlptr, host);
502 urlptr += strlen(host);
503 strcpy(urlptr, file);
504 }
505
506 show_url();
507}
508/*-----------------------------------------------------------------------------------*/
509/* webclient_aborted():
510 *
511 * Callback function. Called from the webclient when the HTTP
512 * connection was abruptly aborted.
513 */
514void
515webclient_aborted(void)
516{
517 show_statustext("Connection reset by peer");
518}
519/*-----------------------------------------------------------------------------------*/
520/* webclient_timedout():
521 *
522 * Callback function. Called from the webclient when the HTTP
523 * connection timed out.
524 */
525void
526webclient_timedout(void)
527{
528 show_statustext("Connection timed out");
529}
530/*-----------------------------------------------------------------------------------*/
531/* webclient_closed():
532 *
533 * Callback function. Called from the webclient when the HTTP
534 * connection was closed after a request from the "webclient_close()"
535 * function. .
536 */
537void
538webclient_closed(void)
539{
540 show_statustext("Stopped.");
adamdunkels17e84a32003-04-02 09:54:39 +0000541 petsciiconv_topetscii(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
542 WWW_CONF_WEBPAGE_WIDTH], WWW_CONF_WEBPAGE_WIDTH);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000543 redraw_window();
544}
545/*-----------------------------------------------------------------------------------*/
546/* webclient_closed():
547 *
548 * Callback function. Called from the webclient when the HTTP
549 * connection is connected.
550 */
551void
552webclient_connected(void)
553{
554 x = nextwordptr = 0;
555 starty = scrolly;
556 nextword[0] = 0;
557
558 if(scrolly == 0) {
559 clear_page();
560 redraw_window();
561 }
562
563 show_statustext("Request sent...");
564 set_url(webclient_hostname(), webclient_port(), webclient_filename());
565
566#if WWW_CONF_RENDERSTATE
567 renderstate = HTMLPARSER_RENDERSTATE_NONE;
568#endif /* WWW_CONF_RENDERSTATE */
569 htmlparser_init();
570}
571/*-----------------------------------------------------------------------------------*/
572/* scroll():
573 *
574 * Scrolls the entire web page display (text and hyperlinks) one line
575 * upwards.
576 */
577static void
578scroll(void)
579{
580 unsigned char i;
581 unsigned char lptr, linkptrtmp;
582 struct ctk_widget *linksptr;
583 char *statustexttext;
584 struct ctk_widget *focuswidget;
585
586 /* Scroll text up. */
adamdunkels17e84a32003-04-02 09:54:39 +0000587 memcpy(webpage, &webpage[WWW_CONF_WEBPAGE_WIDTH],
588 (WWW_CONF_WEBPAGE_HEIGHT - 1) * WWW_CONF_WEBPAGE_WIDTH);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000589
590 /* Clear last line of text. */
adamdunkels17e84a32003-04-02 09:54:39 +0000591 memset(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) * WWW_CONF_WEBPAGE_WIDTH],
592 ' ', WWW_CONF_WEBPAGE_WIDTH);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000593
594 /* Scroll links up. */
595
596 lptr = 0;
597 linksptr = pagewidgets;
598 for(i = 0; i < pagewidgetptr; ++i) {
599 /* First, check which links that scroll off the top of the page
600 and should be removed. */
601 if(CTK_WIDGET_YPOS(linksptr) == 3) {
602 lptr = i + 1;
603 } else {
604 /* Else, move them upward one notch. */
605
606 /* XXX: this is really a hack! These values should not be used
607 like this, but should be obtained and set using some CTK API
608 function. */
adamdunkels17e84a32003-04-02 09:54:39 +0000609 linksptr->widget.hyperlink.text -= WWW_CONF_WEBPAGE_WIDTH;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000610
611 --(linksptr->y);
612 }
613 ++linksptr;
614 }
615
616 /* See if there are any links that scroll off the top. */
617 if(lptr != 0) {
618 memcpy(pagewidgets, &pagewidgets[lptr],
adamdunkels17e84a32003-04-02 09:54:39 +0000619 sizeof(struct ctk_widget) * (WWW_CONF_MAX_NUMPAGEWIDGETS - lptr));
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000620 memcpy(pagewidgetattribs, &pagewidgetattribs[lptr],
adamdunkels17e84a32003-04-02 09:54:39 +0000621 sizeof(union pagewidgetattrib) * (WWW_CONF_MAX_NUMPAGEWIDGETS - lptr));
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000622
623 /* Compute new value of linkptr and tuck it away in
624 linkptrtmp. make_window() destroys linkptr, so we need to
625 restore it after the call. */
626 linkptrtmp = pagewidgetptr - lptr;
627
628 /* XXX: hack - these values should *not* be obtained this way, but
629 through some CTK API instead! */
630 statustexttext = statustext.text;
631 focuswidget = mainwindow.focused;
632 ctk_window_clear(&mainwindow);
633 make_window();
634 CTK_WIDGET_FOCUS(&mainwindow, focuswidget);
635 show_statustext(statustexttext);
636
637 pagewidgetptr = linkptrtmp;
638
639 linksptr = pagewidgets;
640 for(i = 0; i < pagewidgetptr; ++i) {
641 if(linksptr->type == CTK_WIDGET_HYPERLINK) {
642 linksptr->widget.hyperlink.url -= lptr *
643 sizeof(union pagewidgetattrib);
644 }
645 CTK_WIDGET_ADD(&mainwindow, linksptr);
646 ++linksptr;
647 }
648 }
649}
650/*-----------------------------------------------------------------------------------*/
adamdunkels17e84a32003-04-02 09:54:39 +0000651static char tmpcenterline[WWW_CONF_WEBPAGE_WIDTH];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000652/* inc_y():
653 *
654 * Called from the rendering code when it is time to move on line
655 * downwards.
656 */
657static void
658inc_y(void)
659{
660 unsigned char spaces, i;
661 char *cptr;
662 struct ctk_widget *linksptr;
663
664 if(starty > 0) {
665 --starty;
666 } else {
667#if WWW_CONF_RENDERSTATE
668 /* Check if current line should be centered and if so, center
669 it. */
670 if(renderstate & HTMLPARSER_RENDERSTATE_CENTER) {
adamdunkels17e84a32003-04-02 09:54:39 +0000671 cptr = &webpage[(WWW_CONF_WEBPAGE_HEIGHT - 0) * WWW_CONF_WEBPAGE_WIDTH - 1];
672 for(spaces = 0; spaces < WWW_CONF_WEBPAGE_WIDTH; ++spaces) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000673 if(*cptr-- != ' ') {
674 break;
675 }
676 }
677
678 spaces = spaces / 2;
679
680 strncpy(tmpcenterline,
adamdunkels17e84a32003-04-02 09:54:39 +0000681 &webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
682 WWW_CONF_WEBPAGE_WIDTH],
683 WWW_CONF_WEBPAGE_WIDTH);
684 strncpy(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
685 WWW_CONF_WEBPAGE_WIDTH] + spaces,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000686 tmpcenterline,
adamdunkels17e84a32003-04-02 09:54:39 +0000687 WWW_CONF_WEBPAGE_WIDTH - spaces);
688 memset(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
689 WWW_CONF_WEBPAGE_WIDTH], ' ', spaces);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000690
691 linksptr = pagewidgets;
692 for(i = 0; i < pagewidgetptr; ++i) {
adamdunkels17e84a32003-04-02 09:54:39 +0000693 if(CTK_WIDGET_YPOS(linksptr) == 3 + WWW_CONF_WEBPAGE_HEIGHT - 1) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000694 linksptr->x += spaces;
695 linksptr->widget.hyperlink.text += spaces;
696 }
697 ++linksptr;
698 }
699 }
700#endif /* WWW_CONF_RENDERSTATE */
701
adamdunkels17e84a32003-04-02 09:54:39 +0000702 petsciiconv_topetscii(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
703 WWW_CONF_WEBPAGE_WIDTH], WWW_CONF_WEBPAGE_WIDTH);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000704 redraw_window();
705 scroll();
706 ++scrolly;
707 }
708}
709/*-----------------------------------------------------------------------------------*/
710/* webclient_datahandler():
711 *
712 * Callback function. Called from the webclient module when HTTP data
713 * has arrived.
714 */
715void
716webclient_datahandler(char *data, u16_t len)
717{
718 if(len > 0) {
719 /* if(strcmp(webclient_mimetype(), http_texthtml) == 0) {*/
720 count = (count + 1) & 3;
721 show_statustext(receivingmsgs[count]);
722 htmlparser_parse(data, len);
723 /* } else {
724 show_statustext("Receiving non-HTML data...");
725 }*/
726 } else {
727 /* Clear remaining parts of page. */
728 run = 0;
729 }
730
731 if(data == NULL) {
732 run = 0;
733 show_statustext("Done.");
adamdunkels17e84a32003-04-02 09:54:39 +0000734 petsciiconv_topetscii(&webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) *
735 WWW_CONF_WEBPAGE_WIDTH], WWW_CONF_WEBPAGE_WIDTH);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000736 redraw_window();
737 }
738}
739/*-----------------------------------------------------------------------------------*/
740/* output_word():
741 *
742 * Called from the rendering code when a full word has been received
743 * and should be put on screen.
744 */
745static void
746output_word(char c)
747{
748 char *webpageptr;
749
750 if(nextwordptr == 0) {
751 if(c == ISO_nl) {
752 x = 0;
753 inc_y();
754 }
755 return;
756 }
757
adamdunkels17e84a32003-04-02 09:54:39 +0000758 if(x + nextwordptr > WWW_CONF_WEBPAGE_WIDTH) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000759 inc_y();
760 x = 0;
761 }
762
763 nextword[nextwordptr] = 0;
764 if(starty == 0) {
adamdunkels17e84a32003-04-02 09:54:39 +0000765 webpageptr = &webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) * WWW_CONF_WEBPAGE_WIDTH + x];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000766 if(nextwordptr > 0) {
767 strcpy(webpageptr, nextword);
768 }
769 webpageptr[nextwordptr] = ' ';
770 }
771 if(c == ISO_nl) {
772 x = 0;
773 inc_y();
774 } else {
775 x += nextwordptr + 1;
776 }
777 nextwordptr = 0;
778}
779/*-----------------------------------------------------------------------------------*/
780static void *
781add_pagewidget(char *text, unsigned char type,
782 unsigned char border)
783{
784 static unsigned char len, maxwidth;
785 static unsigned char *webpageptr;
786 static void *dataptr;
787 register struct ctk_widget *lptr;
788
789 len = strlen(text);
790
791 if(len + border == 0) {
792 return NULL;
793 }
794
adamdunkels17e84a32003-04-02 09:54:39 +0000795 maxwidth = WWW_CONF_WEBPAGE_WIDTH - (1 + 2 * border);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000796
797 /* If the text of the link is too long so that it does not fit into
798 the width of the current window, counting from the current x
799 coordinate, we first try to jump to the next line. */
800 if(len + x > maxwidth) {
801 output_word(ISO_nl);
802 }
803
804 /* If the text of the link still is too long, we just chop it off!
805 XXX: this is not really the right thing to do, we should probably
806 either make the link a multiline button, or add multiple
807 buttons. But this will do for now. */
808 if(len > maxwidth) {
809 text[maxwidth] = 0;
810 len = maxwidth;
811 }
812
813 dataptr = NULL;
814
815 if(starty == 0) {
adamdunkels17e84a32003-04-02 09:54:39 +0000816 webpageptr = &webpage[(WWW_CONF_WEBPAGE_HEIGHT - 1) * WWW_CONF_WEBPAGE_WIDTH + x];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000817 /* To save memory, we'll copy the widget text to the web page
818 drawing area and reference it from there. */
819 webpageptr[0] = 0;
820 webpageptr += border;
821 strncpy(webpageptr, text, len);
822 webpageptr[len] = 0;
823 webpageptr[len + border] = ' ';
adamdunkels17e84a32003-04-02 09:54:39 +0000824 if(pagewidgetptr < WWW_CONF_MAX_NUMPAGEWIDGETS) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000825 dataptr = &pagewidgetattribs[pagewidgetptr];
826 lptr = &pagewidgets[pagewidgetptr];
827
828 switch(type) {
829 case CTK_WIDGET_HYPERLINK:
830 CTK_HYPERLINK_NEW((struct ctk_hyperlink *)lptr, x,
adamdunkels17e84a32003-04-02 09:54:39 +0000831 WWW_CONF_WEBPAGE_HEIGHT + 2, len,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000832 webpageptr, dataptr);
833 break;
834 case CTK_WIDGET_BUTTON:
835 CTK_BUTTON_NEW((struct ctk_button *)lptr, x,
adamdunkels17e84a32003-04-02 09:54:39 +0000836 WWW_CONF_WEBPAGE_HEIGHT + 2, len,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000837 webpageptr);
838 ((struct formattribs *)dataptr)->inputvalue = webpageptr;
839 break;
840 case CTK_WIDGET_TEXTENTRY:
841 CTK_TEXTENTRY_NEW((struct ctk_textentry *)lptr,
adamdunkels17e84a32003-04-02 09:54:39 +0000842 x, WWW_CONF_WEBPAGE_HEIGHT + 2, len, 1,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000843 webpageptr, len);
844 ((struct formattribs *)dataptr)->inputvalue = webpageptr;
845 break;
846 }
847 CTK_WIDGET_ADD(&mainwindow, lptr);
848
849 ++pagewidgetptr;
850 }
851 }
852 /* Increase the x coordinate with the length of the link text plus
853 the extra space behind it and the CTK button markers. */
854 x += len + 1 + 2 * border;
855
adamdunkels17e84a32003-04-02 09:54:39 +0000856 if(x >= WWW_CONF_WEBPAGE_WIDTH) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000857 inc_y();
858 x = 0;
859 }
860
861 return dataptr;
862}
863/*-----------------------------------------------------------------------------------*/
864/* htmlparser_link:
865 *
866 * Callback function. Will be called when the HTML parser has parsed a
867 * link. We will put a CTK hyperlink widget at the appropriate position
868 * in the window.
869 */
870void
871htmlparser_link(char *text, char *url)
872{
873 static unsigned char *linkurlptr;
874
875 linkurlptr = add_pagewidget(text, CTK_WIDGET_HYPERLINK, 0);
876 if(linkurlptr != NULL &&
877 strlen(url) < WWW_CONF_MAX_URLLEN) {
878 strcpy(linkurlptr, url);
879 }
880}
881/*-----------------------------------------------------------------------------------*/
882/* htmlparser_char():
883 *
884 * Callback function. Called by the HTML parser module for every
885 * printable character in the HTML file.
886 */
887void
888htmlparser_char(char c)
889{
890 if(c == ' ' ||
891 c == ISO_nl) {
892 output_word(c);
893 } else if(c != 0) {
894 nextword[nextwordptr] = c;
adamdunkels17e84a32003-04-02 09:54:39 +0000895 if(nextwordptr < WWW_CONF_WEBPAGE_WIDTH) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000896 ++nextwordptr;
897 }
898 }
899}
900/*-----------------------------------------------------------------------------------*/
901#if WWW_CONF_RENDERSTATE
902void
903htmlparser_renderstate(unsigned char s)
904{
905 if((s & HTMLPARSER_RENDERSTATE_STATUSMASK) ==
906 HTMLPARSER_RENDERSTATE_BEGIN) {
907 renderstate |= s & ~HTMLPARSER_RENDERSTATE_STATUSMASK;
908 } else {
909 renderstate &= ~(s & ~HTMLPARSER_RENDERSTATE_STATUSMASK);
910 }
911}
912#endif /* WWW_CONF_RENDERSTATE */
913
914#if WWW_CONF_FORMS
915/*-----------------------------------------------------------------------------------*/
916void
917htmlparser_submitbutton(char *text, char *name,
918 char *formname, char *formaction)
919{
920 register struct formattribs *form;
921 form = add_pagewidget(text, CTK_WIDGET_BUTTON, 1);
922 if(form != NULL) {
923 strncpy(form->formaction, formaction, WWW_CONF_MAX_FORMACTIONLEN);
924 strncpy(form->formname, formname, WWW_CONF_MAX_FORMNAMELEN);
925 strncpy(form->inputname, name, WWW_CONF_MAX_INPUTNAMELEN);
926 form->inputtype = FORMINPUTTYPE_SUBMITBUTTON;
927 }
928}
929/*-----------------------------------------------------------------------------------*/
930void
931htmlparser_inputfield(char *text, char *name,
932 char *formname, char *formaction)
933{
934 register struct formattribs *form;
935
936 form = add_pagewidget(text, CTK_WIDGET_TEXTENTRY, 1);
937 if(form != NULL) {
938 strncpy(form->formaction, formaction, WWW_CONF_MAX_FORMACTIONLEN);
939 strncpy(form->formname, formname, WWW_CONF_MAX_FORMNAMELEN);
940 strncpy(form->inputname, name, WWW_CONF_MAX_INPUTNAMELEN);
941 form->inputtype = FORMINPUTTYPE_INPUTFIELD;
942 }
943}
944/*-----------------------------------------------------------------------------------*/
945static void
946formsubmit(struct formattribs *attribs)
947{
948 unsigned char i, j;
949 char *urlptr, *valueptr;
950 struct formattribs *faptr;
951
952 urlptr = &tmpurl[0];
953
954 strncpy(urlptr, attribs->formaction, WWW_CONF_MAX_URLLEN);
955 tmpurl[WWW_CONF_MAX_URLLEN] = 0;
956 urlptr += strlen(urlptr);
957 *urlptr = ISO_questionmark;
958 ++urlptr;
959
960
961 /* Construct an URL by finding all input field forms with the same
962 formname as the current submit button, and add the submit button
963 URL stuff as well. */
964 for(i = 0; i < pagewidgetptr; ++i) {
965 if(urlptr - &tmpurl[0] >= WWW_CONF_MAX_URLLEN) {
966 break;
967 }
968
969 faptr = &pagewidgetattribs[i].form;
970
971 if(strcmp(attribs->formaction, faptr->formaction) == 0 &&
972 strcmp(attribs->formname, faptr->formname) == 0 &&
973 (faptr->inputtype == FORMINPUTTYPE_INPUTFIELD ||
974 faptr == attribs)) {
975
976 /* Copy the name of the input field into the URL and append a
977 questionmark. */
978 strncpy(urlptr, faptr->inputname, WWW_CONF_MAX_URLLEN - strlen(tmpurl));
979 tmpurl[WWW_CONF_MAX_URLLEN] = 0;
980 urlptr += strlen(urlptr);
981 *urlptr = ISO_eq;
982 ++urlptr;
983
984 /* Convert and copy the contents of the input field to the URL
985 and append an ampersand. */
986 valueptr = pagewidgets[i].widget.textentry.text;
987 petsciiconv_toascii(valueptr, WWW_CONF_MAX_INPUTVALUELEN);
988 for(j = 0; j < WWW_CONF_MAX_INPUTVALUELEN; ++j) {
989 if(urlptr - &tmpurl[0] >= WWW_CONF_MAX_URLLEN) {
990 break;
991 }
992 *urlptr = *valueptr;
993 if(*urlptr == ISO_space) {
994 *urlptr = ISO_plus;
995 }
996 if(*urlptr == 0) {
997 break;
998 }
999 ++urlptr;
1000 ++valueptr;
1001 }
1002
1003 *urlptr = ISO_ampersand;
1004 ++urlptr;
1005 }
1006 }
1007 --urlptr;
1008 *urlptr = 0;
1009 /* PRINTF(("formsubmit: URL '%s'\n", tmpurl));*/
1010 open_link(tmpurl);
1011}
1012/*-----------------------------------------------------------------------------------*/
1013#endif /* WWW_CONF_FORMS */