示例#1
0
// ----------------------------------------------------------------------------
// the callback used to format data (into text, json, html, etc.) for clients
// ----------------------------------------------------------------------------
// HTTP/1.1 200 OK
// Content-type:text/plain
// Transfer-Encoding: chunked
// 
// 3c;     
// KEY:time VAL:Mon, 12 Mar 2012 13:11:49 GMT|KEY:00 VAL:14.03
// 3c;     
// KEY:time VAL:Mon, 12 Mar 2012 13:11:49 GMT|KEY:00 VAL:40.75
// 3c;     
// KEY:time VAL:Mon, 12 Mar 2012 13:11:49 GMT|KEY:00 VAL:45.02
// 0   
//
static int push_fn(char *argv[], xbuf_t *reply)
{
   reply->len = 0; // empty buffer
   xbuf_xcat(reply, "        \r\n" // room for chunck size: "1a;     "
             "KEY:time VAL:%s|", (char*)get_env(argv, SERVER_DATE));
             
   // fill the 2D HTML <table> cells with our 1D array
   // (we randomly skip some cells to avoid updating all of them)
   {
      int i = 0, r = 0, c = 0;
      while(i < CELLS)
      {
          if(!(sw_rand(&rnd) & 3)) // skip some cells
             xbuf_xcat(reply, "KEY:%c%c VAL:%.02f|",
                              '0' + r, '0' + c, s_data[i++]);

          if(c + 1 < COLS) c++; 
          else
          {
             c = 0;
             if(r + 1 < ROWS) r++; else r = 0;
          }
      }
   }   
   reply->ptr[--reply->len] = 0; // remove the ending '|'
   
   // now we know it, setup the chunk size
   char *p = reply->ptr;
   int i = s_snprintf(p, 8, "%x;", reply->len - (sizeof("        \r\n") - 2));
   p[i] = ' '; // useful for tracing, when we puts(reply->ptr); later
   
   xbuf_ncat(reply, "\r\n", sizeof("\r\n") - 1); // close this chunk
   return 1;   
}
示例#2
0
int main(int argc, char *argv[])
{
   xbuf_t *reply = get_reply(argv);

   xbuf_xcat(reply, "<h3>main()'s argv[0-%d] listed:</h3>", 
             argc ? argc - 1 : 0);

   u32 i = 0;
   while(i < argc)
   {
      xbuf_xcat(reply, "argv[%u] '%s'<br>", i, argv[i]);
      i++;
   }
   
   return 200;
}
示例#3
0
int main(int argc, char *argv[])
{
   static char msg[] = "Throttling Enabled @ %u KB/s!";
   
   xbuf_xcat(get_reply(argv), msg, SPEED);
   
   throttle_reply(argv, SPEED, SPEED, 1); // 1: global setting, 0:this connection
   
   return 200; // return an HTTP code (200:'OK')
}
示例#4
0
int main(int argc, char *argv[])
{ 
   xbuf_xcat(get_reply(argv), 
             "<br>"
             "<b>Servlet name</b>:<br>'%s'<br><br>"
             "<b>Client address</b>:<br>'%s'<br>",
             argv[-2],
             argv[-1]);

   return 200;
}
示例#5
0
文件: base64.c 项目: joh-m/gwan
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
   xbuf_t *reply = get_reply(argv);

   // encode string
   char buf[80], user[80] = "*****@*****.**";
   int ret = s_snprintf(buf, 255, "%B", user);
   xbuf_xcat(reply, "<p>plain text %s to base64:<br> &nbsp; %s (len: %d)</p>",
             user, buf, ret);

   // decode string
   ret = s_snprintf(user, 255, "%-B", buf);
   xbuf_xcat(reply, "<p>base64 %s to plain text:<br> &nbsp; %s (len: %d)</p>",
             buf, user, ret);

   // encode binary data
   memset(user, 0, 8);
   ret = s_snprintf(buf, 255, "%8B", user);
   xbuf_xcat(reply, "<p>8 null bytes to base64:<br> &nbsp; %s (len: %d)</p>",
             buf, ret);

   // decode binary data
   memset(user, 'A', 16);
   ret = s_snprintf(user, 255, "%-B", buf);
   xbuf_xcat(reply, "<p>base64 %s to binary:<br> &nbsp; %s (len: %d)</p>",
             buf, 
             memcmp(user, "\0\0\0\0\0\0\0", 8) ? "mismatch" : "OK, match", 
             ret);

   // dump binary data            
   int i = 0;          
   while(i < 16)
      xbuf_xcat(reply, "user[%d] = %d<br>", i, user[i]), i++;

   return 200; // return an HTTP code (200:'OK')
}
示例#6
0
void getCallback(redisAsyncContext *c, void *r, void *privdata)
{
   redisReply *reply = r;
      if (reply == NULL) return;

   gw_req_async_t *async = (gw_req_async_t *)privdata;

   xbuf_xcat(async->reply, "%x\r\n%s\r\n", reply->len, reply->str);

   // now unlock to main()
   light_unlock(&async->lock);

   // Disconnect after receiving the reply to GET
   redisAsyncDisconnect(c);
}
示例#7
0
int main(int argc, char *argv[])
{
   u32 *old_entity_size = (u32*)get_env(argv, MAX_ENTITY_SIZE);
   u32  new_entity_size = 200 * 1024; // 200 KiB
   
   xbuf_xcat(get_reply(argv), 
             "<pre>\n"
             "Old entity size: %llk\n"
             "New entity size: %llk\n" // "New entity size: 200.00 KiB"
             "</pre>",             
             *old_entity_size, new_entity_size);
             
   *old_entity_size = new_entity_size; // raise the limit to 200 KiB
            
   return 200; // return an HTTP code (200:'OK')
}
示例#8
0
	void flushHeaders(void)
	{
		this->ptrHeaderSize = 0;
		xbuf_xcat
		(
			this->httpOut,
			"HTTP/1.1 200 OK\r\n"
			"%s"
			"Content-type: %s\r\n"
			"Content-Length:      %n\r\n" 
			"Connection: close\r\n\r\n",
			(char *) this->headers.c_str(),
			(char *) this->contentType.c_str(),
			&ptrHeaderSize
		);

		this->headersSet = true;
		this->headersSize = this->httpOut->len;
	}
示例#9
0
文件: zlib.c 项目: ers35/gwan-samples
int main(int argc, char *argv[])
{
  xbuf_t data;
  xbuf_init(&data);
  xbuf_cat(&data,
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sit amet "
    "quam purus, vitae fermentum turpis. Nam in augue mi. Donec suscipit moles"
    "tie felis, eget rhoncus risus pharetra in. Sed id diam id felis fringilla"
    " adipiscing non vitae odio. Etiam vulputate tristique elit, nec eleifend"
    " mauris scelerisque eu. Vestibulum luctus, enim a luctus posuere, mauris"
    " mauris rutrum mi, eget fermentum massa tortor id enim. Nulla feugiat "
    "porta urna quis laoreet. Morbi metus ante, commodo quis dictum vitae, "
    "rhoncus a libero. Cras viverra feugiat orci id interdum. Duis pulvinar "
    "neque id erat adipiscing facilisis. Maecenas vitae urna risus, euismod"
    " sollicitudin risus."
  );
  
  char gzipped[4096];
  
  u32 len = zlib_cmp(data.ptr, 0, data.len, gzipped, sizeof(gzipped), 1);
  
  printf("Original length: %i\n", data.len);
  printf("Gzipped length:  %i\n\n", len);
  
  xbuf_t *reply = get_reply(argv);
  
  xbuf_xcat(reply,
    "HTTP/1.1 200 OK\r\n"
    "Content-Type: text/plain\r\n"
    "Content-Length: %i\r\n"
    "Content-Encoding: gzip\r\n\r\n",
    len
  );
  
  xbuf_ncat(reply, gzipped, len);
  
  xbuf_free(&data);

  return -1; //custom headers
}
示例#10
0
文件: stream1.c 项目: joh-m/gwan
int main(int argc, char *argv[])
{
    // get the server 'reply' buffer where to write our answer to the client
    xbuf_t *reply = get_reply(argv);

    // -------------------------------------------------------------------------
    // step 1: setup a per-request context
    // -------------------------------------------------------------------------
    void **data = (void**)get_env(argv, US_REQUEST_DATA);
    if(!data[0]) // we did not setup our per-request counter yet
    {
        data[0] = (void*)1; // use the persistent pointer as a simple counter

        char head[] = "HTTP/1.1 200 OK\r\n"
                      "Connection: close\r\n"
                      "Content-length: 60\r\n" // 3 * 20 characters
                      "Content-type: text/plain; charset=utf-8\r\n"
                      "\r\n\r\n";
        xbuf_ncat(reply, head, sizeof(head) - 1);
    }

    // -------------------------------------------------------------------------
    // step 2: repeatedly send to client an incremental reply
    // -------------------------------------------------------------------------
    xbuf_xcat(reply, "partial reply %llu<br>\n", data[0]++);

    // -------------------------------------------------------------------------
    // 3: decide when to stop streaming
    // -------------------------------------------------------------------------
    if(data[0] > (void*)NBR_CHUNKS)
    {
        data[0] = 0;
        return RC_NOHEADERS; // RC_NOHEADERS: do not generate HTTP headers
    }

    // RC_NOHEADERS: do not generate HTTP headers
    // RC_STREAMING: call me again after send() is done
    return RC_NOHEADERS + RC_STREAMING;
}
示例#11
0
int list_threads(kv_item *item, xbuf_t *reply)
{
  Thread *thread = (Thread*)item->val;
  
  xbuf_t thread_li;
  xbuf_init(&thread_li);
  
  //using sprintf-like formatting
  xbuf_xcat(&thread_li, 
    "<li>"
    "<a href='/?forum_simple/act=t/id=%llu'>%s</a> (%lu)"
    "</li>",
    thread->id, thread->title.ptr, thread->posts.nbr_items
  );
  
  char *pos = (char*)xbuf_findstr(reply, "<!--tpl-->");
  if (pos) xbuf_insert(reply, pos, thread_li.len, thread_li.ptr);

  xbuf_free(&thread_li);

  return 1;
}
示例#12
0
int main(int argc, char *argv[])
{
	data_t **Data =(data_t **) get_env(argv, US_VHOST_DATA)
		, *data = NULL;
	xbuf_t *reply = get_reply ( argv );

	switch(init_data(argv, Data))
	{
		case 0: data = *Data; break;
		case 1: return 500;
		default: return 503;
	}

	char *uid;
	int auth = gw_is_member(argv, data, &uid);

	/**
    	YOUR CODE GOES HERE
	*/

	xbuf_xcat(reply, "Hello World! auth: %d\n", auth);


	// Validate int
	GW_LOG("GW_VAL_IS_INT");
	gw_val_cond_t *cond = gw_val_cond_new("-", GW_VAL_IS_INT);

	gw_validate_field(&cond, 1);

	printf("cond->field: %s\n", cond->field);
	printf("cond->type: %d\n", cond->type);
	printf("cond->is_valid: %s\n", (cond->is_valid) ? "true" : "false");


	// Validate length
	GW_LOG("GW_VAL_LEN");
	gw_val_cond_set(cond, "ssss", GW_VAL_LEN);
	cond->len.min = 10;
	cond->len.max = 0;

	gw_validate_field(&cond, 1);

	printf("cond->field: %s\n", cond->field);
	printf("cond->type: %d\n", cond->type);
	printf("cond->is_valid: %s\n", (cond->is_valid) ? "true" : "false");


	// Validate email
	GW_LOG("GW_VAL_IS_EMAIL");
	gw_val_cond_set(cond, "*****@*****.**", GW_VAL_IS_EMAIL);

	gw_validate_field(&cond, 1);

	printf("cond->field: %s\n", cond->field);
	printf("cond->type: %d\n", cond->type);
	printf("cond->is_valid: %s\n", (cond->is_valid) ? "true" : "false");


	// Cleaning data
	if(auth == true) free(uid);
	free(cond);

	return 200;
}
示例#13
0
文件: ip.c 项目: ers35/gwan-samples
int main(int argc, char *argv[])
{
  xbuf_xcat(get_reply(argv), "%s", get_env(argv, REMOTE_ADDR));

  return 200;
}
示例#14
0
文件: auth_basic.c 项目: joh-m/gwan
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
   xbuf_t *reply = get_reply(argv);
   http_t *http = (void*)get_env(argv, HTTP_HEADERS);
   
   // -------------------------------------------------------------------------
   // here you could count repeated attempts from the same IP address and then
   // take measures like blacklisting offenders (using G-WAN's TCP/IP firewall)
   // -------------------------------------------------------------------------
   // u32 client_ip = (u32)get_env(argv, REMOTE_BIN_ADDR); // binary
   // char *client_ip = (char*)get_env(argv, REMOTE_ADDR); // string
   
   // -------------------------------------------------------------------------
   // first (unauthorized) client request or something went bad
   // -------------------------------------------------------------------------
   // /?auth_digest might be called by rogue clients, without G-WAN having
   // received (and validated) an authorization request
   if(http->h_auth_type != AUTH_BASIC)
      goto ask_new_auth;

   // -------------------------------------------------------------------------
   // find the request parameters we need (auth_type is double-checked here
   // but that's to show how to implement a generic valid_user() function
   // aimed at being used with a database or an LDAP server)
   // -------------------------------------------------------------------------
   static char *auth_types[] =
   {
      [AUTH_BAD]    = "",
      [AUTH_ANY]    = "*",
      [AUTH_BASIC]  = "Basic",
      [AUTH_DIGEST] = "Digest",
      [AUTH_SRP]    = "SRP",
      [AUTH_X509]   = "x509"
   };
   if(http->h_auth_type < AUTH_BASIC // check before you lookup auth_types[]
   || http->h_auth_type > AUTH_X509)
      http->h_auth_type = AUTH_BAD;

   char *auth = auth_types[http->h_auth_type];
   char *meth = (char*)get_env(argv, REQUEST); // "GET /?auth_digest"
   char *uri  = strchr(meth, ' ');             //     ^
   if(uri) uri++;                              // pass space character
   
   // -------------------------------------------------------------------------
   // check if the user is authorized (if not, send 401)
   // -------------------------------------------------------------------------
   if(!valid_user(http->h_auth_user, http->h_auth_pwd, auth, uri, meth))
      goto ask_new_auth;

   // -------------------------------------------------------------------------
   // user is authorized, produce contents, redirect to contents, etc.
   // -------------------------------------------------------------------------
   xbuf_xcat(reply, "<h1>Basic AUTHORIZATION</h1><p>Successfully authorized" 
                   " user: <tt>'%s'</tt>, password: <tt>'%s'</tt></p>",
                   http->h_auth_user, http->h_auth_pwd);

   return 200; // return an HTTP code (200:'OK')
   
   // -------------------------------------------------------------------------
   // first (unauthorized) client request or something went bad
   // -------------------------------------------------------------------------
ask_new_auth:

   // let G-WAN generate a 401 server reply with the authorization we want
   http->h_auth_type = AUTH_BASIC;
   return 401; // "401 Unauthorized"
}
示例#15
0
// ----------------------------------------------------------------------------
// imported functions:
//   get_reply(): get a pointer on the 'reply' dynamic buffer from the server
//       getus(): get current time in microseconds (1 millisecond = 1,000 us)
//     get_env(): get connection's 'environment' variables from the server
//    xbuf_cat(): like strcat(), but it works in the specified dynamic buffer 
//   gif_build(): build an in-memory GIF image from a bitmap and palette
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
   // -------------------------------------------------------------------------
   // get a pointer on the server reply
   // -------------------------------------------------------------------------
   xbuf_t *reply = get_reply(argv);

   // -------------------------------------------------------------------------
   // allocate memory for a raw bitmap
   // -------------------------------------------------------------------------
   int   w = 800, h = 800, nbcolors = 256, wXh = w * h;
   u8 *bmp = (u8*)malloc(wXh);
   if(!bmp) return 503; // service unavailable

   // -------------------------------------------------------------------------
   // render the Mandelbrot set in our bitmap
   // -------------------------------------------------------------------------
   fractals(bmp, w, h, nbcolors);

   // -------------------------------------------------------------------------
   // display the palette (useful when playing with 'tabcol[]' values)
   // -------------------------------------------------------------------------
   {  
      #define ROUND(a) ((a) > 0 ? (int)((a)+0.5) : -(int)(0.5-(a)))
      u8 *p = bmp;
      int i = h, wd20 = w / 20;
      float color = 0, col = (float)nbcolors / (float)h;
      while(i--)
      {
         color += col;
         memset(p, ROUND(color) & 255, wd20);
         p += w;
      }   
   }

   // -------------------------------------------------------------------------
   // build a smooth multi-gradient color palette from the fixed values below
   // -------------------------------------------------------------------------
   static 
   rgb_t tabcol[]={ {  0,   0, 128}, // Med. Blue
                    {  0, 100, 200}, // Light Blue
                    {100, 160, 160}, // Cyan
                    {  0, 220, 100}, // Green
                    {255, 255,   0}, // Yellow
                    {255, 128,   0}, // Orange
                    {128,   0,   0}, // Med. Red
                    { 64,   0,   0}, // Dark Red
                    {128,   0,   0}, // Med. Red
                    {255, 128,   0}, // Orange
                    {255, 255,   0}, // Yellow
                    {  0, 220, 100}, // Green 
                    {100, 160, 160}, // Cyan
                    {  0, 100, 200}, // Light Blue
                    {  0,   0, 128}, // Med. Blue

                    { 64,   0,   0}, // Dark Red
                    {128,   0,   0}, // Med. Red
                    {255, 128,   0}, // Orange
                    {255, 255,   0}, // Yellow
                    {  0, 220, 100}, // Green 
                    {100, 160, 160}, // Cyan
                    {  0, 100, 200}, // Light Blue
                    {  0,   0, 128}, // Med.  Blue
                    {  0,   0,  64}, // Dark  Blue
                  }, *tab = tabcol;
                  
   // -------------------------------------------------------------------------
   // just for fun, select different colors for each call
   // -------------------------------------------------------------------------
   static u32 call = 0;
   u32 ncols = sizeof(tabcol) / sizeof(rgb_t);
   switch(call)
   {
      case 0: tab =  tabcol;     ncols = 10; break; // blue
      case 1: tab = &tabcol[ 4]; ncols = 10; break; // yellow
      case 2: tab = &tabcol[ 7]; ncols =  7; break; // dark red
      case 3: tab = &tabcol[ 1]; ncols = 16; break; // rainbow -
   }
   call = (call + 1) & 3;
   // generate the palette with our defined gradient steps
   u8 pal[768]; dr_gradient(pal, nbcolors, tab, ncols);
   // nice palete but we want a black body to delimit the mandelbrot set
   memset(pal + ((nbcolors - (nbcolors / 16)) * 3), 0, (nbcolors / 16) * 3);

   // -------------------------------------------------------------------------
   // create custom HTTP response headers to send a GIF file
   // -------------------------------------------------------------------------
   // (G-WAN automatically generates headers if none are provided but it can't
   //  guess all MIME types so this automatic feature is for 'text/html' only
   //  ...unless you explicitly specify the reply buffer MIME type)

#ifdef BUILD_CUSTOM_HEADERS // old way of doing things

   // get the current HTTP date (like "Wed, 02 Jun 2010 06:49:37 GMT")
   u8 *date = (u8*)get_env(argv, SERVER_DATE);

   xbuf_xcat(reply,
             "HTTP/1.1 200 OK\r\n"
             "Date: %s\r\n"
             "Last-Modified: %s\r\n"
             "Content-type: image/gif\r\n"
             "Content-Length:       \r\n" // make room for the for GIF length
             "Connection: close\r\n\r\n",
             date, date);

   // -------------------------------------------------------------------------
   // make sure that we have enough space in the 'reply' buffer
   // (we are going to fill it directly from gif_build(), not via xbuf_xxx)
   // -------------------------------------------------------------------------
   // (if we have not enough memory, we will get a 'graceful' crash)
   if(reply->allocated < (wXh / 10)) // very gross approximation
   {
      if(!xbuf_growto(reply, wXh / 10)) // resize reply
      {
         xbuf_init(reply);
         xbuf_ncat(reply, " ", 1);
         reply->len = 0; // discart pointless data, keep allocated memory
         return 503; // error: we could not allocate enough memory
      }
   }

   // -------------------------------------------------------------------------
   // save the place where to patch the void 'Content-Length' HTTP Header
   // -------------------------------------------------------------------------
   char *p = reply->ptr + reply->len
         - (sizeof("\r\nConnection: close\r\n\r\n") - 1);

   // -------------------------------------------------------------------------
   // append a GIF image (-1:no transparency, 0: no comment) to 'reply'
   // -------------------------------------------------------------------------
   int len = gif_build((u8*)(reply->ptr + reply->len), bmp, w, h, 
                       pal, nbcolors, 
                       -1, 0);
   if(len < 0) len = 0; // (len == -1) if gif_build() failed
   reply->len += len;  // add the GIF size to the 'reply' buffer length
   free(bmp);
   
   // -------------------------------------------------------------------------
   // store the GIF size in the empty space of the 'Content-Length' header
   // -------------------------------------------------------------------------
   u32toa(p, len);
   
#else // #ifdef BUILD_CUSTOM_HEADERS // works with any supported MIME type
   
   // -------------------------------------------------------------------------
   // specify a MIME type so we don't have to build custom HTTP headers
   // -------------------------------------------------------------------------
   char *mime = (char*)get_env(argv, REPLY_MIME_TYPE);
   // note that we setup the FILE EXTENTION, not the MIME type:
   mime[0] = '.'; mime[1] = 'g'; mime[2] = 'i'; mime[3] = 'f'; mime[4] = 0; 

   // -------------------------------------------------------------------------
   // make sure that we have enough space in the 'reply' buffer
   // (we are going to fill it directly from gif_build(), not via xbuf_xxx)
   // -------------------------------------------------------------------------
   // (if we have not enough memory, we will get a 'graceful' crash)
   if(reply->allocated < (wXh / 10)) // very gross approximation
   {
      if(!xbuf_growto(reply, wXh / 10)) // resize reply
      {
         xbuf_init(reply);
         xbuf_ncat(reply, " ", 1);
         reply->len = 0; // discart pointless data, keep allocated memory
         return 503; // error: we could not allocate enough memory
      }
   }

   // -------------------------------------------------------------------------
   // append a GIF image (-1:no transparency, 0: no comment) to 'reply'
   // -------------------------------------------------------------------------
   int len = gif_build((u8*)(reply->ptr + reply->len), bmp, w, h, 
                       pal, nbcolors, 
                       -1, 0);
   if(len < 0) len = 0; // (len == -1) if gif_build() failed
   reply->len += len;  // add the GIF size to the 'reply' buffer length
   free(bmp);
   
#endif // #else #ifdef BUILD_CUSTOM_HEADERS

   return 200; // return an HTTP code (200:'OK')
}
示例#16
0
// ----------------------------------------------------------------------------
// imported functions:
//   get_reply(): get a pointer on the 'reply' dynamic buffer from the server
//     get_env(): get connection's 'environment' variables from the server
//    xbuf_cat(): like strcat(), but it works in the specified dynamic buffer 
//   gif_build(): build an in-memory GIF image from a bitmap and palette
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
   // -------------------------------------------------------------------------
   // build the top of our HTML page
   // -------------------------------------------------------------------------
   static char top[]=
     "<!DOCTYPE HTML>"
     "<html lang=\"en\"><head><title>Captcha</title><meta http-equiv"
     "=\"Content-Type\" content=\"text/html; charset=utf-8\">"
     "<link href=\"/imgs/style.css\" rel=\"stylesheet\" type=\"text/css\">"
     "</head><body style=\"margin:0 16px;\"><br><h2>Captcha for Humans</h2>"
     "<p>Please enter the SUM of all the GREEN FIGURES (not letters) below "
     "(that's twice the same image, just with a different HTML background "
     "- the Data-URI-inlined GIF background is transparent):</p><br>\r\n";

   xbuf_t *reply = get_reply(argv);
   xbuf_ncat(reply, top, sizeof(top) - 1);
   
   // -------------------------------------------------------------------------
   // allocate memory for a raw bitmap
   // -------------------------------------------------------------------------
   const int w = BMP_WIDTH, h = BMP_HEIGHT, wXh = w * h;
   u8 *bmp = (u8*)calloc(CHAR_WIDTH * w, h); 
   if(!bmp) return 503; // service unavailable

   // -------------------------------------------------------------------------
   // render the captcha in our bitmap
   // -------------------------------------------------------------------------
   u32 seed = (u32)getns();
   prnd_t rnd; // pseudo-random generator (period: 1 << 158)
   sw_init(&rnd, seed); // EPOCH time in nano-seconds

   // structure needed by G-WAN's frame buffer routines like dr_text()
   bmp_t img ={ .bmp = bmp, .p = bmp, .bbp = 8, .pen = 1, .bgd = 0, 
                .rect = {0,0, w,h}, .flags = 0, .w = w, .h = h, 
                .x = 0, .y = 0 };
   u32 sum = captcha(&img, &rnd);

   // -------------------------------------------------------------------------
   // build the GIF image, gif_build(0:transparent color index, 0: no comment)
   // -------------------------------------------------------------------------
   u8 pal[] = { 255, 255, 255,  223, 255, 191,  132, 164, 100,  0, 0, 0 };
   const int nbcolors = (sizeof(pal) / sizeof(u8)) / 3; // RGB values

   u8 *gif = (u8*)malloc(CHAR_WIDTH * wXh);
   if(!gif) { free(bmp); return 503; } // service unavailable
   int gln = gif_build(gif, bmp, w, h, pal, nbcolors, 0, 0);

   // -------------------------------------------------------------------------
   // store the base64 encoded GIF in the 'reply' buffer
   // -------------------------------------------------------------------------
   if(gln > 0) // (gln == -1) if gif_build() failed
   {   
      // a real captcha test would only display the first of those two views:
      // (they are shown side-by-side to visualize the background trick)
      xbuf_cat(reply, "<table><tr>\r\n"
                      "<td style=\"background:#dfffbf;\">\r\n");
      u32 img_pos = reply->len;
      xbuf_xcat(reply,
                "<img src=\"data:image/gif;base64,%*B\" alt=\"A tree\" "
                "width=\"%d\" height=\"%d\" /></td>\r\n",
                gln, gif, w + w, h + h); // scale picture
      xbuf_xcat(reply, 
                "<td style=\"background:#84a464;\">%.*s</tr>\r\n</table>\n\r",
                reply->len - img_pos, reply->ptr + img_pos);
   }
   free(gif);
   free(bmp);

   // -------------------------------------------------------------------------
   // close our HTML page
   // -------------------------------------------------------------------------
   xbuf_xcat(reply, 
            "<br>The two sums are: <b>%u</b> and <b>%u</b>... "
            "for the same Captcha image!<br><br>"
            "By just changing the <b>HTML background color</b> (mouse cursor "
            "hovering, previous state or input or shared secret) used for "
            "the transparent GIF Captcha image we can make something simple "
            "for humans become difficult or even completely impossible "
            "for robots.<br><br>"
            "HTML and GIF are served with one single request: the picture"
            " is generated on-the-fly and embedded into the HTML code by "
            " using the base64 encoding (look at the HTML source code)."
            "<br></body></html>", 
            (sum & 0xffff0000) >> 16, 
            sum & 0x0000ffff);

   return 200; // return an HTTP code (200:'OK')
}