MegaGlest/mk/linux/mojosetup/gui_www.c

1117 lines
28 KiB
C

/**
* MojoSetup; a portable, flexible installation application.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#if !SUPPORT_GUI_WWW
#error Something is wrong in the build system.
#endif
#define BUILDING_EXTERNAL_PLUGIN 1
#include "gui.h"
MOJOGUI_PLUGIN(www)
#if !GUI_STATIC_LINK_WWW
CREATE_MOJOGUI_ENTRY_POINT(www)
#endif
#include <stdarg.h>
#define FREE_AND_NULL(x) { free(x); x = NULL; }
// tapdance between things WinSock and BSD Sockets define differently...
#if PLATFORM_WINDOWS
#include <winsock.h>
typedef int socklen_t;
#define setprotoent(x) assert(x == 0)
#define sockErrno() WSAGetLastError()
#define wouldBlockError(err) (err == WSAEWOULDBLOCK)
#define intrError(err) (err == WSAEINTR)
static inline void setBlocking(SOCKET s, boolean blocking)
{
u_long val = (blocking) ? 0 : 1;
ioctlsocket(s, FIONBIO, &val);
} // setBlocking
static const char *sockStrErrVal(int val)
{
STUBBED("Windows strerror");
return "sockStrErrVal() is unimplemented.";
} // sockStrErrVal
static boolean initSocketSupport(void)
{
WSADATA data;
int rc = WSAStartup(MAKEWORD(1, 1), &data);
if (rc != 0)
{
logError("www: WSAStartup() failed: %0", sockStrErrVal(rc));
return false;
} // if
logInfo("www: WinSock initialized (want %0.%1, got %2.%3).",
numstr((int) (LOBYTE(data.wVersion))),
numstr((int) (HIBYTE(data.wVersion))),
numstr((int) (LOBYTE(data.wHighVersion))),
numstr((int) (HIBYTE(data.wHighVersion))));
logInfo("www: WinSock description: %0", data.szDescription);
logInfo("www: WinSock system status: %0", data.szSystemStatus);
logInfo("www: WinSock max sockets: %0", numstr((int) data.iMaxSockets));
return true;
} // initSocketSupport
#define deinitSocketSupport() WSACleanup()
#else
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netdb.h>
#include <fcntl.h>
typedef int SOCKET;
#define SOCKET_ERROR (-1)
#define INVALID_SOCKET (-1)
#define closesocket(x) close(x)
#define sockErrno() (errno)
#define sockStrErrVal(val) strerror(val)
#define intrError(err) (err == EINTR)
#define initSocketSupport() (true)
#define deinitSocketSupport()
static inline boolean wouldBlockError(int err)
{
return ((err == EWOULDBLOCK) || (err == EAGAIN));
} // wouldBlockError
static void setBlocking(SOCKET s, boolean blocking)
{
int flags = fcntl(s, F_GETFL, 0);
if (blocking)
flags &= ~O_NONBLOCK;
else
flags |= O_NONBLOCK;
fcntl(s, F_SETFL, flags);
} // setBlocking
#endif
#define sockStrError() (sockStrErrVal(sockErrno()))
typedef struct _S_WebRequest
{
char *key;
char *value;
struct _S_WebRequest *next;
} WebRequest;
static char *output = NULL;
static char *lastProgressType = NULL;
static char *lastComponent = NULL;
static char *baseUrl = NULL;
static WebRequest *webRequest = NULL;
static uint32 percentTicks = 0;
static SOCKET listenSocket = INVALID_SOCKET;
static SOCKET clientSocket = INVALID_SOCKET;
static uint8 MojoGui_www_priority(boolean istty)
{
return MOJOGUI_PRIORITY_TRY_LAST;
} // MojoGui_www_priority
static void freeWebRequest(void)
{
while (webRequest)
{
WebRequest *next = webRequest->next;
free(webRequest->key);
free(webRequest->value);
free(webRequest);
webRequest = next;
} // while
} // freeWebRequest
static void addWebRequest(const char *key, const char *val)
{
if ((key != NULL) && (*key != '\0'))
{
WebRequest *req = (WebRequest *) xmalloc(sizeof (WebRequest));
req->key = xstrdup(key);
req->value = xstrdup(val);
req->next = webRequest;
webRequest = req;
logDebug("www: request element '%0' = '%1'", key, val);
} // if
} // addWebRequest
static int hexVal(char ch)
{
if ((ch >= 'a') && (ch <= 'f'))
return (ch - 'a') + 10;
else if ((ch >= 'A') && (ch <= 'F'))
return (ch - 'A') + 10;
else if ((ch >= '0') && (ch <= '9'))
return (ch - '0');
return -1;
} // hexVal
static void unescapeUri(char *uri)
{
char *ptr = uri;
while ((ptr = strchr(ptr, '%')) != NULL)
{
int a, b;
if ((a = hexVal(ptr[1])) != -1)
{
if ((b = hexVal(ptr[2])) != -1)
{
*(ptr++) = (char) ((a * 16) + b);
memmove(ptr, ptr+2, strlen(ptr+1));
} // if
else
{
*(ptr++) = '?';
memmove(ptr, ptr+1, strlen(ptr));
} // else
} // if
else
{
*(ptr++) = '?';
} // else
} // while
} // unescapeUri
static int strAdd(char **ptr, size_t *len, size_t *alloc, const char *fmt, ...)
{
size_t bw = 0;
size_t avail = *alloc - *len;
va_list ap;
va_start(ap, fmt);
bw = vsnprintf(*ptr + *len, avail, fmt, ap);
va_end(ap);
if (bw >= avail)
{
const size_t add = (*alloc + (bw + 1)); // double plus the new len.
*alloc += add;
avail += add;
*ptr = xrealloc(*ptr, *alloc);
va_start(ap, fmt);
bw = vsnprintf(*ptr + *len, avail, fmt, ap);
va_end(ap);
} // if
*len += bw;
return bw;
} // strAdd
static char *htmlescape(const char *str)
{
size_t len = 0, alloc = 0;
char *retval = NULL;
char ch;
while ((ch = *(str++)) != '\0')
{
switch (ch)
{
case '&': strAdd(&retval, &len, &alloc, "&amp;"); break;
case '<': strAdd(&retval, &len, &alloc, "&lt;"); break;
case '>': strAdd(&retval, &len, &alloc, "&gt;"); break;
case '"': strAdd(&retval, &len, &alloc, "&quot;"); break;
case '\'': strAdd(&retval, &len, &alloc, "&#39;"); break;
default: strAdd(&retval, &len, &alloc, "%c", ch); break;
} // switch
} // while
return retval;
} // htmlescape
static const char *standardResponseHeaders =
"Content-Type: text/html; charset=utf-8\n"
"Accept-Ranges: none\n"
"Cache-Control: no-cache\n"
"Connection: close\n\n";
static void setHtmlString(char **str, int responseCode,
const char *responseString,
const char *title, const char *html)
{
size_t len = 0, alloc = 0;
FREE_AND_NULL(*str);
strAdd(str, &len, &alloc,
"HTTP/1.1 %d %s\n" // responseCode, responseString
"%s" // standardResponseHeaders
"<html>"
"<head>"
"<title>%s</title>" // title
"</head>"
"<body>%s</body>" // html
"</html>\n",
responseCode, responseString,
standardResponseHeaders,
title, html);
} // setHtmlString
static void setHtml(const char *title, const char *html)
{
setHtmlString(&output, 200, "OK", title, html);
} // setHtml
static void sendStringAndDrop(SOCKET *_s, const char *str)
{
SOCKET s = *_s;
int outlen = 0;
if (str == NULL)
str = "";
else
outlen = strlen(str);
setBlocking(s, true);
while (outlen > 0)
{
int rc = send(s, str, outlen, 0);
if (rc != SOCKET_ERROR)
{
str += rc;
outlen -= rc;
} // if
else
{
const int err = sockErrno();
if (!intrError(err))
{
logError("www: send() failed: %0", sockStrErrVal(err));
break;
} // if
} // else
} // while
closesocket(s);
*_s = INVALID_SOCKET;
} // sendStringAndDrop
static void respond404(SOCKET *s)
{
char *text = htmlescape(_("Not Found"));
char *str = NULL;
size_t len = 0, alloc = 0;
char *html = NULL;
strAdd(&html, &len, &alloc, "<center><h1>%s</h1></center>", text);
setHtmlString(&str, 404, text, text, html);
free(html);
free(text);
sendStringAndDrop(s, str);
free(str);
} // respond404
static boolean parseGet(char *get)
{
char *uri = NULL;
char *ver = NULL;
uri = strchr(get, ' ');
if (uri == NULL) return false;
*(uri++) = '\0';
ver = strchr(uri, ' ');
if (ver == NULL) return false;
*(ver++) = '\0';
if (strcmp(get, "GET") != 0) return false;
if (uri[0] != '/') return false;
uri++; // skip dirsep.
// !!! FIXME: we may want to feed stock files (<img> tags, etc)
// !!! FIXME: at some point in the future.
if ((uri[0] != '?') && (uri[0] != '\0')) return false;
if (strncmp(ver, "HTTP/", 5) != 0) return false;
if (*uri == '?')
uri++; // skip initial argsep.
do
{
char *next = strchr(uri, '&');
char *val = NULL;
if (next != NULL)
*(next++) = '\0';
val = strchr(uri, '=');
if (val == NULL)
val = "";
else
*(val++) = '\0';
unescapeUri(uri);
unescapeUri(val);
addWebRequest(uri, val);
uri = next;
} while (uri != NULL);
return true;
} // parseGet
static boolean parseRequest(char *reqstr)
{
do
{
char *next = strchr(reqstr, '\n');
char *val = NULL;
if (next != NULL)
*(next++) = '\0';
val = strchr(reqstr, ':');
if (val == NULL)
val = "";
else
{
*(val++) = '\0';
while (*val == ' ')
val++;
} // else
if (*reqstr != '\0')
{
size_t len = 0, alloc = 0;
char *buf = NULL;
strAdd(&buf, &len, &alloc, "HTTP-%s", reqstr);
addWebRequest(buf, val);
free(buf);
} // if
reqstr = next;
} while (reqstr != NULL);
return true;
} // parseRequest
static WebRequest *servePage(boolean blocking)
{
int newline = 0;
char ch = 0;
struct sockaddr_in addr;
socklen_t addrlen = 0;
int s = 0;
char *reqstr = NULL;
size_t len = 0, alloc = 0;
int err = 0;
freeWebRequest();
if (listenSocket == INVALID_SOCKET)
return NULL;
if (clientSocket != INVALID_SOCKET) // response to feed to client.
sendStringAndDrop(&clientSocket, output);
if (blocking)
setBlocking(listenSocket, true);
do
{
s = accept(listenSocket, (struct sockaddr *) &addr, &addrlen);
err = sockErrno();
} while ( (s == INVALID_SOCKET) && (intrError(err)) );
if (blocking)
setBlocking(listenSocket, false); // reset what we toggled up there.
if (s == INVALID_SOCKET)
{
if (wouldBlockError(err))
assert(!blocking);
else
{
logError("www: accept() failed: %0", sockStrErrVal(err));
closesocket(listenSocket); // make all future i/o fail too.
listenSocket = INVALID_SOCKET;
} // else
return NULL;
} // if
setBlocking(s, true);
// Doing this one char at a time isn't efficient, but it's easy.
while (1)
{
if (recv(s, &ch, 1, 0) == SOCKET_ERROR)
{
const int err = sockErrno();
if (!intrError(err)) // just try again on interrupt.
{
logError("www: recv() failed: %0", sockStrErrVal(err));
FREE_AND_NULL(reqstr);
closesocket(s);
s = INVALID_SOCKET;
break;
} // if
} // if
else if (ch == '\n') // newline
{
if (++newline == 2)
break; // end of request.
strAdd(&reqstr, &len, &alloc, "\n");
} // if
else if (ch != '\r')
{
newline = 0;
strAdd(&reqstr, &len, &alloc, "%c", ch);
} // else if
} // while
if (reqstr != NULL)
{
char *get = NULL;
char *ptr = strchr(reqstr, '\n');
if (ptr != NULL)
{
*ptr = '\0';
ptr++;
} // if
// reqstr is the GET (or whatever) request, ptr is the rest.
get = xstrdup(reqstr);
if (ptr == NULL)
{
*ptr = '\0';
len = 0;
} // if
else
{
len = strlen(ptr);
memmove(reqstr, ptr, len+1);
} // else
logDebug("www: request '%0'", get);
// okay, now (get) and (reqptr) are separate strings.
// These parse*() functions update (webRequest).
if ( (parseGet(get)) && (parseRequest(reqstr)) )
logDebug("www: accepted request");
else
{
logError("www: rejected bogus request");
freeWebRequest();
respond404(&s);
} // else
free(reqstr);
free(get);
} // if
clientSocket = s;
return webRequest;
} // servePage
static SOCKET create_listen_socket(short portnum)
{
SOCKET s = INVALID_SOCKET;
int protocol = 0; // pray this is right.
struct protoent *prot;
setprotoent(0);
prot = getprotobyname("tcp");
if (prot != NULL)
protocol = prot->p_proto;
s = socket(PF_INET, SOCK_STREAM, protocol);
if (s == INVALID_SOCKET)
logInfo("www: socket() failed ('%0')", sockStrError());
else
{
boolean success = false;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(portnum);
addr.sin_addr.s_addr = INADDR_ANY; // !!! FIXME: bind to localhost.
// So we can bind this socket over and over in debug runs...
#if ((!defined _NDEBUG) && (!defined NDEBUG))
{
int on = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &on, sizeof (on));
}
#endif
if (bind(s, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR)
logError("www: bind() failed ('%0')", sockStrError());
else if (listen(s, 5) == SOCKET_ERROR)
logError("www: listen() failed ('%0')", sockStrError());
else
{
logInfo("www: socket created on port %0",
numstr(portnum));
success = true;
} // else
if (!success)
{
closesocket(s);
s = INVALID_SOCKET;
} // if
} // if
return s;
} // create_listen_socket
static boolean MojoGui_www_init(void)
{
size_t len = 0, alloc = 0;
short portnum = 7341; // !!! FIXME: try some random ports.
percentTicks = 0;
if (!initSocketSupport())
{
logInfo("www: socket subsystem init failed, use another UI.");
return false;
} // if
listenSocket = create_listen_socket(portnum);
if (listenSocket < 0)
{
logInfo("www: no listen socket, use another UI.");
return false;
} // if
setBlocking(listenSocket, false);
strAdd(&baseUrl, &len, &alloc, "http://localhost:%d/", (int) portnum);
return true;
} // MojoGui_www_init
static void MojoGui_www_deinit(void)
{
// Catch any waiting browser connections...and tell them to buzz off! :)
char *donetitle = htmlescape(_("Shutting down..."));
char *donetext = htmlescape(_("You can close this browser now."));
size_t len = 0, alloc = 0;
char *html = NULL;
strAdd(&html, &len, &alloc, "<hr><center>%s</center><hr>", donetext);
setHtml(donetitle, html);
free(html);
free(donetitle);
free(donetext);
while (servePage(false) != NULL) { /* no-op. */ }
freeWebRequest();
FREE_AND_NULL(output);
FREE_AND_NULL(lastProgressType);
FREE_AND_NULL(lastComponent);
FREE_AND_NULL(baseUrl);
if (clientSocket != INVALID_SOCKET)
{
closesocket(clientSocket);
clientSocket = INVALID_SOCKET;
} // if
if (listenSocket != INVALID_SOCKET)
{
closesocket(listenSocket);
listenSocket = INVALID_SOCKET;
} // if
deinitSocketSupport();
} // MojoGui_www_deinit
static int doPromptPage(const char *title, const char *text, boolean centertxt,
const char *pagename,
const char **buttons, const char **locButtons,
int bcount)
{
char *htmltitle = htmlescape(title);
boolean sawPage = false;
int answer = -1;
int i = 0;
char *html = NULL;
size_t len = 0, alloc = 0;
const char *align = ((centertxt) ? " align='center'" : "");
strAdd(&html, &len, &alloc,
"<center>"
"<form name='form_%s' method='get'>" // pagename
"<input type='hidden' name='page' value='%s'>" // pagename
"<table>"
"<tr><td%s>%s</td></tr>" // align, text
"<tr>"
"<td align='center'>", pagename, pagename, align, text);
for (i = 0; i < bcount; i++)
{
const char *button = buttons[i];
const char *loc = locButtons[i];
strAdd(&html, &len, &alloc,
"<input type='submit' name='%s' value='%s'>", button, loc);
} // for
strAdd(&html, &len, &alloc,
"</td>"
"</tr>"
"</table>"
"</form>"
"</center>");
setHtml(htmltitle, html);
free(htmltitle);
free(html);
while ((!sawPage) || (answer == -1))
{
WebRequest *req = servePage(true);
sawPage = false;
answer = -1;
while (req != NULL)
{
const char *k = req->key;
const char *v = req->value;
if ( (strcmp(k, "page") == 0) && (strcmp(v, pagename) == 0) )
sawPage = true;
else
{
for (i = 0; i < bcount; i++)
{
if (strcmp(k, buttons[i]) == 0)
{
answer = i;
break;
} // if
} // for
} // else
req = req->next;
} // while
} // while
return answer;
} // doPromptPage
static void MojoGui_www_msgbox(const char *title, const char *text)
{
const char *buttons[] = { "ok" };
const char *locButtons[] = { htmlescape(_("OK")) };
char *htmltext = htmlescape(text);
doPromptPage(title, htmltext, true, "msgbox", buttons, locButtons, 1);
free(htmltext);
free((void *) locButtons[0]);
} // MojoGui_www_msgbox
static boolean MojoGui_www_promptyn(const char *title, const char *text,
boolean defval)
{
// !!! FIXME:
// We currently ignore defval
int i, rc;
char *htmltext = htmlescape(text);
const char *buttons[] = { "no", "yes" };
const char *locButtons[] = { htmlescape(_("No")), htmlescape(_("Yes")) };
assert(STATICARRAYLEN(buttons) == STATICARRAYLEN(locButtons));
rc = doPromptPage(title, htmltext, true, "promptyn", buttons, locButtons,
STATICARRAYLEN(buttons));
free(htmltext);
for (i = 0; i < STATICARRAYLEN(locButtons); i++)
free((void *) locButtons[i]);
return (rc == 1);
} // MojoGui_www_promptyn
static MojoGuiYNAN MojoGui_www_promptynan(const char *title, const char *text,
boolean defval)
{
// !!! FIXME:
// We currently ignore defval
int i, rc;
char *htmltext = htmlescape(text);
const char *buttons[] = { "no", "yes", "always", "never" };
const char *locButtons[] = {
htmlescape(_("No")),
htmlescape(_("Yes")),
htmlescape(_("Always")),
htmlescape(_("Never")),
};
assert(STATICARRAYLEN(buttons) == STATICARRAYLEN(locButtons));
rc = doPromptPage(title, htmltext, true, "promptynan", buttons, locButtons,
STATICARRAYLEN(buttons));
free(htmltext);
for (i = 0; i < STATICARRAYLEN(locButtons); i++)
free((void *) locButtons[i]);
return (MojoGuiYNAN) rc;
} // MojoGui_www_promptynan
static boolean MojoGui_www_start(const char *title,
const MojoGuiSplash *splash)
{
return true;
} // MojoGui_www_start
static void MojoGui_www_stop(void)
{
// no-op.
} // MojoGui_www_stop
static int MojoGui_www_readme(const char *name, const uint8 *data,
size_t datalen, boolean can_back,
boolean can_fwd)
{
char *text = NULL;
size_t len = 0, alloc = 0;
char *htmldata = htmlescape((const char *) data);
int i, rc;
int cancelbutton = -1;
int backbutton = -1;
int fwdbutton = -1;
int bcount = 0;
const char *buttons[4] = { NULL, NULL, NULL, NULL };
const char *locButtons[4] = { NULL, NULL, NULL, NULL };
assert(STATICARRAYLEN(buttons) == STATICARRAYLEN(locButtons));
cancelbutton = bcount++;
buttons[cancelbutton] = "cancel";
locButtons[cancelbutton] = xstrdup(_("Cancel"));
if (can_back)
{
backbutton = bcount++;
buttons[backbutton] = "back";
locButtons[backbutton] = xstrdup(_("Back"));
} // if
if (can_fwd)
{
fwdbutton = bcount++;
buttons[fwdbutton] = "next";
locButtons[fwdbutton] = xstrdup(_("Next"));
} // if
strAdd(&text, &len, &alloc, "<pre>\n%s\n</pre>", htmldata);
free(htmldata);
rc = doPromptPage(name, text, false, "readme", buttons, locButtons, bcount);
free(text);
for (i = 0; i < STATICARRAYLEN(locButtons); i++)
free((void *) locButtons[i]);
if (rc == backbutton)
return -1;
else if (rc == cancelbutton)
return 0;
return 1;
} // MojoGui_www_readme
static int MojoGui_www_options(MojoGuiSetupOptions *opts,
boolean can_back, boolean can_fwd)
{
// !!! FIXME: write me.
STUBBED("www options");
return 1;
} // MojoGui_www_options
static char *MojoGui_www_destination(const char **recommends, int recnum,
int *command, boolean can_back,
boolean can_fwd)
{
char *retval = NULL;
char *title = xstrdup(_("Destination"));
char *html = NULL;
size_t len = 0, alloc = 0;
boolean checked = true;
int cancelbutton = -1;
int backbutton = -1;
int fwdbutton = -1;
int bcount = 0;
int rc = 0;
int i = 0;
const char *buttons[4] = { NULL, NULL, NULL, NULL };
const char *locButtons[4] = { NULL, NULL, NULL, NULL };
assert(STATICARRAYLEN(buttons) == STATICARRAYLEN(locButtons));
cancelbutton = bcount++;
buttons[cancelbutton] = "cancel";
locButtons[cancelbutton] = xstrdup(_("Cancel"));
if (can_back)
{
backbutton = bcount++;
buttons[backbutton] = "back";
locButtons[backbutton] = xstrdup(_("Back"));
} // if
if (can_fwd)
{
fwdbutton = bcount++;
buttons[fwdbutton] = "next";
locButtons[fwdbutton] = xstrdup(_("Next"));
} // if
strAdd(&html, &len, &alloc,
"<form name='form_destination' method='get'>"
"<table>");
for (i = 0; i < recnum; i++)
{
strAdd(&html, &len, &alloc,
"<tr>"
"<td>"
"<input type='radio' name='dest' %s value='%s'>%s"
"</td>"
"</tr>",
((checked) ? "checked='true'" : ""), recommends[i], recommends[i]);
checked = false;
} // for
strAdd(&html, &len, &alloc,
"<tr>"
"<td>"
"<input type='radio' name='dest' %s value='*'>"
"<input type='text' name='customdest' value=''>"
"</td>"
"</tr>"
"</table>"
"</form>", ((checked) ? "checked='true'" : ""));
rc = doPromptPage(title, html, true, "destination",
buttons, locButtons, bcount);
free(title);
free(html);
for (i = 0; i < STATICARRAYLEN(locButtons); i++)
free((void *) locButtons[i]);
if (rc == backbutton)
*command = -1;
else if (rc == cancelbutton)
*command = 0;
else
{
const char *dest = NULL;
const char *customdest = NULL;
WebRequest *req = webRequest;
while (req != NULL)
{
const char *k = req->key;
const char *v = req->value;
if (strcmp(k, "dest") == 0)
dest = v;
else if (strcmp(k, "customdest") == 0)
customdest = v;
req = req->next;
} // while
if (dest != NULL)
{
if (strcmp(dest, "*") == 0)
dest = customdest;
} // if
if (dest == NULL)
*command = 0; // !!! FIXME: maybe loop with doPromptPage again.
else
{
retval = xstrdup(dest);
*command = 1;
} // else
} // else
return retval;
} // MojoGui_www_destination
static int MojoGui_www_productkey(const char *desc, const char *fmt,
char *buf, const int buflen,
boolean can_back, boolean can_fwd)
{
char *prompt = xstrdup(_("Please enter your product key"));
int retval = -1;
char *html = NULL;
size_t len = 0, alloc = 0;
int cancelbutton = -1;
int backbutton = -1;
int fwdbutton = -1;
int bcount = 0;
int rc = 0;
int i = 0;
const char *buttons[4] = { NULL, NULL, NULL, NULL };
const char *locButtons[4] = { NULL, NULL, NULL, NULL };
assert(STATICARRAYLEN(buttons) == STATICARRAYLEN(locButtons));
cancelbutton = bcount++;
buttons[cancelbutton] = "cancel";
locButtons[cancelbutton] = xstrdup(_("Cancel"));
if (can_back)
{
backbutton = bcount++;
buttons[backbutton] = "back";
locButtons[backbutton] = xstrdup(_("Back"));
} // if
if (can_fwd)
{
fwdbutton = bcount++;
buttons[fwdbutton] = "next";
locButtons[fwdbutton] = xstrdup(_("Next"));
} // if
strAdd(&html, &len, &alloc,
"<form name='form_productkey' method='get'>"
"%s<br/>"
"<input type='text' name='productkey' value='%s'>"
"</form>", prompt, ((*buf) ? buf : ""));
free(prompt);
rc = doPromptPage(desc, html, true, "productkey",
buttons, locButtons, bcount);
free(html);
for (i = 0; i < STATICARRAYLEN(locButtons); i++)
free((void *) locButtons[i]);
if (rc == backbutton)
retval = -1;
else if (rc == cancelbutton)
retval = 0;
else
{
WebRequest *req = webRequest;
const char *keyval = NULL;
while (req != NULL)
{
const char *k = req->key;
const char *v = req->value;
if (strcmp(k, "productkey") == 0)
keyval = v;
req = req->next;
} // while
if (keyval == NULL)
retval = 0; // !!! FIXME: maybe loop with doPromptPage again.
else
{
snprintf(buf, buflen, "%s", keyval);
if (isValidProductKey(fmt, buf))
retval = 1;
// !!! FIXME: must try again if invalid key.
} // else
} // else
return retval;
} // MojoGui_www_productkey
static boolean MojoGui_www_insertmedia(const char *medianame)
{
char *htmltext = NULL;
char *text = NULL;
size_t len = 0, alloc = 0;
int i, rc;
const char *buttons[] = { "cancel", "ok" };
const char *locButtons[] = { htmlescape(_("Cancel")), htmlescape(_("OK")) };
char *title = xstrdup(_("Media change"));
char *fmt = xstrdup(_("Please insert '%0'"));
char *msg = format(fmt, medianame);
strAdd(&text, &len, &alloc, msg);
free(msg);
free(fmt);
htmltext = htmlescape(text);
free(text);
assert(STATICARRAYLEN(buttons) == STATICARRAYLEN(locButtons));
rc = doPromptPage(title, htmltext, true, "insertmedia", buttons,
locButtons, STATICARRAYLEN(buttons));
free(title);
free(htmltext);
for (i = 0; i < STATICARRAYLEN(locButtons); i++)
free((void *) locButtons[i]);
return (rc == 1);
} // MojoGui_www_insertmedia
static void MojoGui_www_progressitem(void)
{
// no-op in this UI target.
} // MojoGui_www_progressitem
static boolean MojoGui_www_progress(const char *type, const char *component,
int percent, const char *item,
boolean can_cancel)
{
return true;
} // MojoGui_www_progress
static void MojoGui_www_final(const char *msg)
{
MojoGui_www_msgbox(_("Finish"), msg);
} // MojoGui_www_final
// end of gui_www.c ...