rfbTightExtensionMsgHandler(struct _rfbClientRec* cl, void* data,
				const rfbClientToServerMsg* msg)
{
    switch (msg->type) {

	case rfbFileListRequest:

	return handleMessage(cl, "rfbFileListRequest", HandleFileListRequest);

	case rfbFileDownloadRequest:

	return handleMessage(cl, "rfbFileDownloadRequest", HandleFileDownloadRequest);

	case rfbFileUploadRequest:

	return handleMessage(cl, "rfbFileUploadRequest", HandleFileUploadRequest);

	case rfbFileUploadData:

	return handleMessage(cl, "rfbFileUploadDataRequest", HandleFileUploadDataRequest);

	case rfbFileDownloadCancel:

	return handleMessage(cl, "rfbFileDownloadCancelRequest", HandleFileDownloadCancelRequest);

	case rfbFileUploadFailed:

	return handleMessage(cl, "rfbFileUploadFailedRequest", HandleFileUploadFailedRequest);

	case rfbFileCreateDirRequest:

	return handleMessage(cl, "rfbFileCreateDirRequest", HandleFileCreateDirRequest);

    default:

	rfbLog("rfbProcessClientNormalMessage: unknown message type %d\n",
		msg->type);

	/*

	We shouldn't close the connection here for unhandled msg,
	it should be left to libvncserver.
	rfbLog(" ... closing connection\n");
	rfbCloseClient(cl);

	*/

	return FALSE;

    }
}


void rfbTightCleanup(rfbScreenInfoPtr screen)
{
  if(tightBeforeBufSize) {
    free(tightBeforeBuf);
    tightBeforeBufSize=0;
    tightBeforeBuf = NULL;
  }
  if(tightAfterBufSize) {
    free(tightAfterBuf);
    tightAfterBufSize=0;
    tightAfterBuf = NULL;
  }
}


rfbDefaultKbdAddEvent(rfbBool down, rfbKeySym keySym, rfbClientPtr cl)
{
}


static int compat_mkdir(const char *path, int mode)
{
	return mkdir(path);
}


rfbStartOnHoldClient(rfbClientPtr cl)
{
	cl->onHold = FALSE;
}


static rfbCursorPtr rfbDefaultGetCursorPtr(rfbClientPtr cl)
{
   return(cl->screen->cursor);
}


xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
    int i, j, initNr, skip;
    xmlNodePtr n1, n2;

    if (val2 == NULL) return(val1);
    if (val1 == NULL) {
	val1 = xmlXPathNodeSetCreate(NULL);
    if (val1 == NULL)
        return (NULL);
#if 0
	/*
	* TODO: The optimization won't work in every case, since
	*  those nasty namespace nodes need to be added with
	*  xmlXPathNodeSetDupNs() to the set; thus a pure
	*  memcpy is not possible.
	*  If there was a flag on the nodesetval, indicating that
	*  some temporary nodes are in, that would be helpfull.
	*/
	/*
	* Optimization: Create an equally sized node-set
	* and memcpy the content.
	*/
	val1 = xmlXPathNodeSetCreateSize(val2->nodeNr);
	if (val1 == NULL)
	    return(NULL);
	if (val2->nodeNr != 0) {
	    if (val2->nodeNr == 1)
		*(val1->nodeTab) = *(val2->nodeTab);
	    else {
		memcpy(val1->nodeTab, val2->nodeTab,
		    val2->nodeNr * sizeof(xmlNodePtr));
	    }
	    val1->nodeNr = val2->nodeNr;
	}
	return(val1);
#endif
    }

    /* @@ with_ns to check whether namespace nodes should be looked at @@ */
    initNr = val1->nodeNr;

    for (i = 0;i < val2->nodeNr;i++) {
	n2 = val2->nodeTab[i];
	/*
	 * check against duplicates
	 */
	skip = 0;
	for (j = 0; j < initNr; j++) {
	    n1 = val1->nodeTab[j];
	    if (n1 == n2) {
		skip = 1;
		break;
	    } else if ((n1->type == XML_NAMESPACE_DECL) &&
		       (n2->type == XML_NAMESPACE_DECL)) {
		if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
		    (xmlStrEqual(((xmlNsPtr) n1)->prefix,
			((xmlNsPtr) n2)->prefix)))
		{
		    skip = 1;
		    break;
		}
	    }
	}
	if (skip)
	    continue;

	/*
	 * grow the nodeTab if needed
	 */
	if (val1->nodeMax == 0) {
	    val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
						    sizeof(xmlNodePtr));
	    if (val1->nodeTab == NULL) {
	        xmlXPathErrMemory(NULL, "merging nodeset\n");
		return(NULL);
	    }
	    memset(val1->nodeTab, 0 ,
		   XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
	    val1->nodeMax = XML_NODESET_DEFAULT;
	} else if (val1->nodeNr == val1->nodeMax) {
	    xmlNodePtr *temp;

	    temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * 2 *
					     sizeof(xmlNodePtr));
	    if (temp == NULL) {
	        xmlXPathErrMemory(NULL, "merging nodeset\n");
		return(NULL);
	    }
	    val1->nodeTab = temp;
	    val1->nodeMax *= 2;
	}
	if (n2->type == XML_NAMESPACE_DECL) {
	    xmlNsPtr ns = (xmlNsPtr) n2;

	    val1->nodeTab[val1->nodeNr++] =
		xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
	} else
	    val1->nodeTab[val1->nodeNr++] = n2;
    }

    return(val1);
}


static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
{
	struct hfs_btree *tree = prev->tree;
	struct hfs_bnode *node;
	struct hfs_bnode_desc desc;
	__be32 cnid;

	node = hfs_bnode_create(tree, idx);
	if (IS_ERR(node))
		return node;

	if (!tree->free_nodes)
		panic("FIXME!!!");
	tree->free_nodes--;
	prev->next = idx;
	cnid = cpu_to_be32(idx);
	hfs_bnode_write(prev, &cnid, offsetof(struct hfs_bnode_desc, next), 4);

	node->type = HFS_NODE_MAP;
	node->num_recs = 1;
	hfs_bnode_clear(node, 0, tree->node_size);
	desc.next = 0;
	desc.prev = 0;
	desc.type = HFS_NODE_MAP;
	desc.height = 0;
	desc.num_recs = cpu_to_be16(1);
	desc.reserved = 0;
	hfs_bnode_write(node, &desc, 0, sizeof(desc));
	hfs_bnode_write_u16(node, 14, 0x8000);
	hfs_bnode_write_u16(node, tree->node_size - 2, 14);
	hfs_bnode_write_u16(node, tree->node_size - 4, tree->node_size - 6);

	return node;
}


void hfs_btree_write(struct hfs_btree *tree)
{
	struct hfs_btree_header_rec *head;
	struct hfs_bnode *node;
	struct page *page;

	node = hfs_bnode_find(tree, 0);
	if (IS_ERR(node))
		/* panic? */
		return;
	/* Load the header */
	page = node->page[0];
	head = (struct hfs_btree_header_rec *)(kmap(page) + sizeof(struct hfs_bnode_desc));

	head->root = cpu_to_be32(tree->root);
	head->leaf_count = cpu_to_be32(tree->leaf_count);
	head->leaf_head = cpu_to_be32(tree->leaf_head);
	head->leaf_tail = cpu_to_be32(tree->leaf_tail);
	head->node_count = cpu_to_be32(tree->node_count);
	head->free_nodes = cpu_to_be32(tree->free_nodes);
	head->attributes = cpu_to_be32(tree->attributes);
	head->depth = cpu_to_be16(tree->depth);

	kunmap(page);
	set_page_dirty(page);
	hfs_bnode_put(node);
}


lzw_read_byte (GifContext *context)
{
	int code, incode;
	gint retval;
	gint my_retval;
	register int i;

	if (context->lzw_code_pending != -1) {
		retval = context->lzw_code_pending;
		context->lzw_code_pending = -1;
		return retval;
	}

	if (context->lzw_fresh) {
		context->lzw_fresh = FALSE;
		do {
			retval = get_code (context, context->lzw_code_size);
			if (retval < 0) {
				return retval;
			}

			context->lzw_firstcode = context->lzw_oldcode = retval;
		} while (context->lzw_firstcode == context->lzw_clear_code);
		return context->lzw_firstcode;
	}

	if (context->lzw_sp > context->lzw_stack) {
		my_retval = *--(context->lzw_sp);
		return my_retval;
	}

	while ((code = get_code (context, context->lzw_code_size)) >= 0) {
		if (code == context->lzw_clear_code) {
			for (i = 0; i < context->lzw_clear_code; ++i) {
				context->lzw_table[0][i] = 0;
				context->lzw_table[1][i] = i;
			}
			for (; i < (1 << MAX_LZW_BITS); ++i)
				context->lzw_table[0][i] = context->lzw_table[1][i] = 0;
			context->lzw_code_size = context->lzw_set_code_size + 1;
			context->lzw_max_code_size = 2 * context->lzw_clear_code;
			context->lzw_max_code = context->lzw_clear_code + 2;
			context->lzw_sp = context->lzw_stack;

			set_gif_lzw_clear_code (context);
			return -3;
		} else if (code == context->lzw_end_code) {
			int count;
			unsigned char buf[260];

                        /*  FIXME - we should handle this case */
                        g_set_error_literal (context->error,
                                             GDK_PIXBUF_ERROR,
                                             GDK_PIXBUF_ERROR_FAILED,
                                             _("GIF image loader cannot understand this image."));
                        return -2;

			if (ZeroDataBlock) {
				return -2;
			}

			while ((count = GetDataBlock (context, buf)) > 0)
				;

			if (count != 0) {
				/*g_print (_("GIF: missing EOD in data stream (common occurence)"));*/
				return -2;
			}
		}

		incode = code;

		if (code >= context->lzw_max_code) {
                        CHECK_LZW_SP ();
			*(context->lzw_sp)++ = context->lzw_firstcode;
			code = context->lzw_oldcode;
		}

		while (code >= context->lzw_clear_code) {
                        if (code >= (1 << MAX_LZW_BITS)) {
                                g_set_error_literal (context->error,
                                                     GDK_PIXBUF_ERROR,
                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                                     _("Bad code encountered"));
				return -2;
                        }
                        CHECK_LZW_SP ();
			*(context->lzw_sp)++ = context->lzw_table[1][code];

			if (code == context->lzw_table[0][code]) {
                                g_set_error_literal (context->error,
                                                     GDK_PIXBUF_ERROR,
                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                                     _("Circular table entry in GIF file"));
				return -2;
			}
			code = context->lzw_table[0][code];
		}

                CHECK_LZW_SP ();
		*(context->lzw_sp)++ = context->lzw_firstcode = context->lzw_table[1][code];

		if ((code = context->lzw_max_code) < (1 << MAX_LZW_BITS)) {
			context->lzw_table[0][code] = context->lzw_oldcode;
			context->lzw_table[1][code] = context->lzw_firstcode;
			++context->lzw_max_code;
			if ((context->lzw_max_code >= context->lzw_max_code_size) &&
			    (context->lzw_max_code_size < (1 << MAX_LZW_BITS))) {
				context->lzw_max_code_size *= 2;
				++context->lzw_code_size;
			}
		}

		context->lzw_oldcode = incode;

		if (context->lzw_sp > context->lzw_stack) {
			my_retval = *--(context->lzw_sp);
			return my_retval;
		}
	}
	return code;
}


get_data_block (GifContext *context,
		unsigned char *buf,
		gint *empty_block)
{

	if (context->block_count == 0) {
		if (!gif_read (context, &context->block_count, 1)) {
			return -1;
		}
	}

	if (context->block_count == 0)
		if (empty_block) {
			*empty_block = TRUE;
			return 0;
		}

	if (!gif_read (context, buf, context->block_count)) {
		return -1;
	}

	return 0;
}


gif_read (GifContext *context, guchar *buffer, size_t len)
{
	gboolean retval;
#ifdef IO_GIFDEBUG
	gint i;
#endif
	if (context->file) {
#ifdef IO_GIFDEBUG
		count += len;
		g_print ("Fsize :%d\tcount :%d\t", len, count);
#endif
		retval = (fread(buffer, len, 1, context->file) != 0);

                if (!retval && ferror (context->file)) {
                        gint save_errno = errno;
                        g_set_error (context->error,
                                     G_FILE_ERROR,
                                     g_file_error_from_errno (save_errno),
                                     _("Failure reading GIF: %s"),
                                     g_strerror (save_errno));
                }

#ifdef IO_GIFDEBUG
		if (len < 100) {
			for (i = 0; i < len; i++)
				g_print ("%d ", buffer[i]);
		}
		g_print ("\n");
#endif

		return retval;
	} else {
#ifdef IO_GIFDEBUG
/*  		g_print ("\tlooking for %d bytes.  size == %d, ptr == %d\n", len, context->size, context->ptr); */
#endif
		if ((context->size - context->ptr) >= len) {
#ifdef IO_GIFDEBUG
			count += len;
#endif
			memcpy (buffer, context->buf + context->ptr, len);
			context->ptr += len;
			context->amount_needed = 0;
#ifdef IO_GIFDEBUG
			g_print ("Psize :%d\tcount :%d\t", len, count);
			if (len < 100) {
				for (i = 0; i < len; i++)
					g_print ("%d ", buffer[i]);
			}
			g_print ("\n");
#endif
			return TRUE;
		}
		context->amount_needed = len - (context->size - context->ptr);
	}
	return FALSE;
}


maybe_update (GifContext *context,
              gint        x,
              gint        y,
              gint        width,
              gint        height)
{
        if (clip_frame (context, &x, &y, &width, &height))
                (*context->update_func) (context->frame->pixbuf,
                                         x, y, width, height,
                                         context->user_data);
}


xmlAddEncodingAlias(const char *name, const char *alias) {
    int i;
    char upper[100];

    if ((name == NULL) || (alias == NULL))
	return(-1);

    for (i = 0;i < 99;i++) {
        upper[i] = toupper(alias[i]);
	if (upper[i] == 0) break;
    }
    upper[i] = 0;

    if (xmlCharEncodingAliases == NULL) {
	xmlCharEncodingAliasesNb = 0;
	xmlCharEncodingAliasesMax = 20;
	xmlCharEncodingAliases = (xmlCharEncodingAliasPtr)
	      xmlMalloc(xmlCharEncodingAliasesMax * sizeof(xmlCharEncodingAlias));
	if (xmlCharEncodingAliases == NULL)
	    return(-1);
    } else if (xmlCharEncodingAliasesNb >= xmlCharEncodingAliasesMax) {
	xmlCharEncodingAliasesMax *= 2;
	xmlCharEncodingAliases = (xmlCharEncodingAliasPtr)
	      xmlRealloc(xmlCharEncodingAliases,
		         xmlCharEncodingAliasesMax * sizeof(xmlCharEncodingAlias));
    }
    /*
     * Walk down the list looking for a definition of the alias
     */
    for (i = 0;i < xmlCharEncodingAliasesNb;i++) {
	if (!strcmp(xmlCharEncodingAliases[i].alias, upper)) {
	    /*
	     * Replace the definition.
	     */
	    xmlFree((char *) xmlCharEncodingAliases[i].name);
	    xmlCharEncodingAliases[i].name = xmlMemStrdup(name);
	    return(0);
	}
    }
    /*
     * Add the definition
     */
    xmlCharEncodingAliases[xmlCharEncodingAliasesNb].name = xmlMemStrdup(name);
    xmlCharEncodingAliases[xmlCharEncodingAliasesNb].alias = xmlMemStrdup(upper);
    xmlCharEncodingAliasesNb++;
    return(0);
}


xmlEncodingErr(xmlParserErrors error, const char *msg, const char *val)
{
    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL,
                    XML_FROM_I18N, error, XML_ERR_FATAL,
                    NULL, 0, val, NULL, NULL, 0, 0, msg, val);
}


xmlGetCharEncodingHandler(xmlCharEncoding enc) {
    xmlCharEncodingHandlerPtr handler;

    if (handlers == NULL) xmlInitCharEncodingHandlers();
    switch (enc) {
        case XML_CHAR_ENCODING_ERROR:
	    return(NULL);
        case XML_CHAR_ENCODING_NONE:
	    return(NULL);
        case XML_CHAR_ENCODING_UTF8:
	    return(NULL);
        case XML_CHAR_ENCODING_UTF16LE:
	    return(xmlUTF16LEHandler);
        case XML_CHAR_ENCODING_UTF16BE:
	    return(xmlUTF16BEHandler);
        case XML_CHAR_ENCODING_EBCDIC:
            handler = xmlFindCharEncodingHandler("EBCDIC");
            if (handler != NULL) return(handler);
            handler = xmlFindCharEncodingHandler("ebcdic");
            if (handler != NULL) return(handler);
            handler = xmlFindCharEncodingHandler("EBCDIC-US");
            if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_UCS4BE:
            handler = xmlFindCharEncodingHandler("ISO-10646-UCS-4");
            if (handler != NULL) return(handler);
            handler = xmlFindCharEncodingHandler("UCS-4");
            if (handler != NULL) return(handler);
            handler = xmlFindCharEncodingHandler("UCS4");
            if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_UCS4LE:
            handler = xmlFindCharEncodingHandler("ISO-10646-UCS-4");
            if (handler != NULL) return(handler);
            handler = xmlFindCharEncodingHandler("UCS-4");
            if (handler != NULL) return(handler);
            handler = xmlFindCharEncodingHandler("UCS4");
            if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_UCS4_2143:
	    break;
        case XML_CHAR_ENCODING_UCS4_3412:
	    break;
        case XML_CHAR_ENCODING_UCS2:
            handler = xmlFindCharEncodingHandler("ISO-10646-UCS-2");
            if (handler != NULL) return(handler);
            handler = xmlFindCharEncodingHandler("UCS-2");
            if (handler != NULL) return(handler);
            handler = xmlFindCharEncodingHandler("UCS2");
            if (handler != NULL) return(handler);
	    break;

	    /*
	     * We used to keep ISO Latin encodings native in the
	     * generated data. This led to so many problems that
	     * this has been removed. One can still change this
	     * back by registering no-ops encoders for those
	     */
        case XML_CHAR_ENCODING_8859_1:
	    handler = xmlFindCharEncodingHandler("ISO-8859-1");
	    if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_8859_2:
	    handler = xmlFindCharEncodingHandler("ISO-8859-2");
	    if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_8859_3:
	    handler = xmlFindCharEncodingHandler("ISO-8859-3");
	    if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_8859_4:
	    handler = xmlFindCharEncodingHandler("ISO-8859-4");
	    if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_8859_5:
	    handler = xmlFindCharEncodingHandler("ISO-8859-5");
	    if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_8859_6:
	    handler = xmlFindCharEncodingHandler("ISO-8859-6");
	    if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_8859_7:
	    handler = xmlFindCharEncodingHandler("ISO-8859-7");
	    if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_8859_8:
	    handler = xmlFindCharEncodingHandler("ISO-8859-8");
	    if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_8859_9:
	    handler = xmlFindCharEncodingHandler("ISO-8859-9");
	    if (handler != NULL) return(handler);
	    break;

        case XML_CHAR_ENCODING_2022_JP:
            handler = xmlFindCharEncodingHandler("ISO-2022-JP");
            if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_SHIFT_JIS:
            handler = xmlFindCharEncodingHandler("SHIFT-JIS");
            if (handler != NULL) return(handler);
            handler = xmlFindCharEncodingHandler("SHIFT_JIS");
            if (handler != NULL) return(handler);
            handler = xmlFindCharEncodingHandler("Shift_JIS");
            if (handler != NULL) return(handler);
	    break;
        case XML_CHAR_ENCODING_EUC_JP:
            handler = xmlFindCharEncodingHandler("EUC-JP");
            if (handler != NULL) return(handler);
	    break;
	default:
	    break;
    }

#ifdef DEBUG_ENCODING
    xmlGenericError(xmlGenericErrorContext,
	    "No handler found for encoding %d\n", enc);
#endif
    return(NULL);
}


static int UTF8ToISO8859_9 (unsigned char* out, int *outlen,
    const unsigned char* in, int *inlen) {
    return UTF8ToISO8859x (out, outlen, in, inlen, xmltranscodetable_ISO8859_9);
}


static int UTF8ToISO8859_10 (unsigned char* out, int *outlen,
    const unsigned char* in, int *inlen) {
    return UTF8ToISO8859x (out, outlen, in, inlen, xmltranscodetable_ISO8859_10);
}


static int UTF8ToISO8859_11 (unsigned char* out, int *outlen,
    const unsigned char* in, int *inlen) {
    return UTF8ToISO8859x (out, outlen, in, inlen, xmltranscodetable_ISO8859_11);
}


xmlGetEncodingAlias(const char *alias) {
    int i;
    char upper[100];

    if (alias == NULL)
	return(NULL);

    if (xmlCharEncodingAliases == NULL)
	return(NULL);

    for (i = 0;i < 99;i++) {
        upper[i] = toupper(alias[i]);
	if (upper[i] == 0) break;
    }
    upper[i] = 0;

    /*
     * Walk down the list looking for a definition of the alias
     */
    for (i = 0;i < xmlCharEncodingAliasesNb;i++) {
	if (!strcmp(xmlCharEncodingAliases[i].alias, upper)) {
	    return(xmlCharEncodingAliases[i].name);
	}
    }
    return(NULL);
}


xmlParseCharEncoding(const char* name)
{
    const char *alias;
    char upper[500];
    int i;

    if (name == NULL)
	return(XML_CHAR_ENCODING_NONE);

    /*
     * Do the alias resolution
     */
    alias = xmlGetEncodingAlias(name);
    if (alias != NULL)
	name = alias;

    for (i = 0;i < 499;i++) {
        upper[i] = toupper(name[i]);
	if (upper[i] == 0) break;
    }
    upper[i] = 0;

    if (!strcmp(upper, "")) return(XML_CHAR_ENCODING_NONE);
    if (!strcmp(upper, "UTF-8")) return(XML_CHAR_ENCODING_UTF8);
    if (!strcmp(upper, "UTF8")) return(XML_CHAR_ENCODING_UTF8);

    /*
     * NOTE: if we were able to parse this, the endianness of UTF16 is
     *       already found and in use
     */
    if (!strcmp(upper, "UTF-16")) return(XML_CHAR_ENCODING_UTF16LE);
    if (!strcmp(upper, "UTF16")) return(XML_CHAR_ENCODING_UTF16LE);

    if (!strcmp(upper, "ISO-10646-UCS-2")) return(XML_CHAR_ENCODING_UCS2);
    if (!strcmp(upper, "UCS-2")) return(XML_CHAR_ENCODING_UCS2);
    if (!strcmp(upper, "UCS2")) return(XML_CHAR_ENCODING_UCS2);

    /*
     * NOTE: if we were able to parse this, the endianness of UCS4 is
     *       already found and in use
     */
    if (!strcmp(upper, "ISO-10646-UCS-4")) return(XML_CHAR_ENCODING_UCS4LE);
    if (!strcmp(upper, "UCS-4")) return(XML_CHAR_ENCODING_UCS4LE);
    if (!strcmp(upper, "UCS4")) return(XML_CHAR_ENCODING_UCS4LE);

    if (!strcmp(upper,  "ISO-8859-1")) return(XML_CHAR_ENCODING_8859_1);
    if (!strcmp(upper,  "ISO-LATIN-1")) return(XML_CHAR_ENCODING_8859_1);
    if (!strcmp(upper,  "ISO LATIN 1")) return(XML_CHAR_ENCODING_8859_1);

    if (!strcmp(upper,  "ISO-8859-2")) return(XML_CHAR_ENCODING_8859_2);
    if (!strcmp(upper,  "ISO-LATIN-2")) return(XML_CHAR_ENCODING_8859_2);
    if (!strcmp(upper,  "ISO LATIN 2")) return(XML_CHAR_ENCODING_8859_2);

    if (!strcmp(upper,  "ISO-8859-3")) return(XML_CHAR_ENCODING_8859_3);
    if (!strcmp(upper,  "ISO-8859-4")) return(XML_CHAR_ENCODING_8859_4);
    if (!strcmp(upper,  "ISO-8859-5")) return(XML_CHAR_ENCODING_8859_5);
    if (!strcmp(upper,  "ISO-8859-6")) return(XML_CHAR_ENCODING_8859_6);
    if (!strcmp(upper,  "ISO-8859-7")) return(XML_CHAR_ENCODING_8859_7);
    if (!strcmp(upper,  "ISO-8859-8")) return(XML_CHAR_ENCODING_8859_8);
    if (!strcmp(upper,  "ISO-8859-9")) return(XML_CHAR_ENCODING_8859_9);

    if (!strcmp(upper, "ISO-2022-JP")) return(XML_CHAR_ENCODING_2022_JP);
    if (!strcmp(upper, "SHIFT_JIS")) return(XML_CHAR_ENCODING_SHIFT_JIS);
    if (!strcmp(upper, "EUC-JP")) return(XML_CHAR_ENCODING_EUC_JP);

#ifdef DEBUG_ENCODING
    xmlGenericError(xmlGenericErrorContext, "Unknown encoding %s\n", name);
#endif
    return(XML_CHAR_ENCODING_ERROR);
}


xmlCharEncFirstLineInt(xmlCharEncodingHandler *handler, xmlBufferPtr out,
                       xmlBufferPtr in, int len) {
    int ret = -2;
    int written;
    int toconv;

    if (handler == NULL) return(-1);
    if (out == NULL) return(-1);
    if (in == NULL) return(-1);

    /* calculate space available */
    written = out->size - out->use - 1; /* count '\0' */
    toconv = in->use;
    /*
     * echo '<?xml version="1.0" encoding="UCS4"?>' | wc -c => 38
     * 45 chars should be sufficient to reach the end of the encoding
     * declaration without going too far inside the document content.
     * on UTF-16 this means 90bytes, on UCS4 this means 180
     * The actual value depending on guessed encoding is passed as @len
     * if provided
     */
    if (len >= 0) {
        if (toconv > len)
            toconv = len;
    } else {
        if (toconv > 180)
            toconv = 180;
    }
    if (toconv * 2 >= written) {
        xmlBufferGrow(out, toconv);
	written = out->size - out->use - 1;
    }

    if (handler->input != NULL) {
	ret = handler->input(&out->content[out->use], &written,
	                     in->content, &toconv);
	xmlBufferShrink(in, toconv);
	out->use += written;
	out->content[out->use] = 0;
    }
#ifdef LIBXML_ICONV_ENABLED
    else if (handler->iconv_in != NULL) {
	ret = xmlIconvWrapper(handler->iconv_in, &out->content[out->use],
	                      &written, in->content, &toconv);
	xmlBufferShrink(in, toconv);
	out->use += written;
	out->content[out->use] = 0;
	if (ret == -1) ret = -3;
    }
#endif /* LIBXML_ICONV_ENABLED */
#ifdef LIBXML_ICU_ENABLED
    else if (handler->uconv_in != NULL) {
	ret = xmlUconvWrapper(handler->uconv_in, 1, &out->content[out->use],
	                      &written, in->content, &toconv);
	xmlBufferShrink(in, toconv);
	out->use += written;
	out->content[out->use] = 0;
	if (ret == -1) ret = -3;
    }
#endif /* LIBXML_ICU_ENABLED */
#ifdef DEBUG_ENCODING
    switch (ret) {
        case 0:
	    xmlGenericError(xmlGenericErrorContext,
		    "converted %d bytes to %d bytes of input\n",
	            toconv, written);
	    break;
        case -1:
	    xmlGenericError(xmlGenericErrorContext,"converted %d bytes to %d bytes of input, %d left\n",
	            toconv, written, in->use);
	    break;
        case -2:
	    xmlGenericError(xmlGenericErrorContext,
		    "input conversion failed due to input error\n");
	    break;
        case -3:
	    xmlGenericError(xmlGenericErrorContext,"converted %d bytes to %d bytes of input, %d left\n",
	            toconv, written, in->use);
	    break;
	default:
	    xmlGenericError(xmlGenericErrorContext,"Unknown input conversion failed %d\n", ret);
    }
#endif /* DEBUG_ENCODING */
    /*
     * Ignore when input buffer is not on a boundary
     */
    if (ret == -3) ret = 0;
    if (ret == -1) ret = 0;
    return(ret);
}


int udhcpc_main(int argc UNUSED_PARAM, char **argv)
{
	uint8_t *temp, *message;
	const char *str_V, *str_h, *str_F, *str_r;
	IF_FEATURE_UDHCP_PORT(char *str_P;)
	void *clientid_mac_ptr;
	llist_t *list_O = NULL;
	llist_t *list_x = NULL;
	int tryagain_timeout = 20;
	int discover_timeout = 3;
	int discover_retries = 3;
	uint32_t server_addr = server_addr; /* for compiler */
	uint32_t requested_ip = 0;
	uint32_t xid = xid; /* for compiler */
	int packet_num;
	int timeout; /* must be signed */
	unsigned already_waited_sec;
	unsigned opt;
	int max_fd;
	int retval;
	fd_set rfds;

	/* Default options */
	IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;)
	IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;)
	client_config.interface = "eth0";
	client_config.script = CONFIG_UDHCPC_DEFAULT_SCRIPT;
	str_V = "udhcp "BB_VER;

	/* Parse command line */
	/* O,x: list; -T,-t,-A take numeric param */
	opt_complementary = "O::x::T+:t+:A+" IF_UDHCP_VERBOSE(":vv") ;
	IF_LONG_OPTS(applet_long_options = udhcpc_longopts;)
	opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:t:SA:O:ox:fB"
		USE_FOR_MMU("b")
		IF_FEATURE_UDHCPC_ARPING("a")
		IF_FEATURE_UDHCP_PORT("P:")
		"v"
		, &str_V, &str_h, &str_h, &str_F
		, &client_config.interface, &client_config.pidfile, &str_r /* i,p */
		, &client_config.script /* s */
		, &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
		, &list_O
		, &list_x
		IF_FEATURE_UDHCP_PORT(, &str_P)
		IF_UDHCP_VERBOSE(, &dhcp_verbose)
	);
	if (opt & (OPT_h|OPT_H)) {
		//msg added 2011-11
		bb_error_msg("option -h NAME is deprecated, use -x hostname:NAME");
		client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0);
	}
	if (opt & OPT_F) {
		/* FQDN option format: [0x51][len][flags][0][0]<fqdn> */
		client_config.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3);
		/* Flag bits: 0000NEOS
		 * S: 1 = Client requests server to update A RR in DNS as well as PTR
		 * O: 1 = Server indicates to client that DNS has been updated regardless
		 * E: 1 = Name is in DNS format, i.e. <4>host<6>domain<3>com<0>,
		 *    not "host.domain.com". Format 0 is obsolete.
		 * N: 1 = Client requests server to not update DNS (S must be 0 then)
		 * Two [0] bytes which follow are deprecated and must be 0.
		 */
		client_config.fqdn[OPT_DATA + 0] = 0x1;
		/*client_config.fqdn[OPT_DATA + 1] = 0; - xzalloc did it */
		/*client_config.fqdn[OPT_DATA + 2] = 0; */
	}
	if (opt & OPT_r)
		requested_ip = inet_addr(str_r);
#if ENABLE_FEATURE_UDHCP_PORT
	if (opt & OPT_P) {
		CLIENT_PORT = xatou16(str_P);
		SERVER_PORT = CLIENT_PORT - 1;
	}
#endif
	if (opt & OPT_o)
		client_config.no_default_options = 1;
	while (list_O) {
		char *optstr = llist_pop(&list_O);
		unsigned n = bb_strtou(optstr, NULL, 0);
		if (errno || n > 254) {
			n = udhcp_option_idx(optstr);
			n = dhcp_optflags[n].code;
		}
		client_config.opt_mask[n >> 3] |= 1 << (n & 7);
	}
	while (list_x) {
		char *optstr = llist_pop(&list_x);
		char *colon = strchr(optstr, ':');
		if (colon)
			*colon = ' ';
		/* now it looks similar to udhcpd's config file line:
		 * "optname optval", using the common routine: */
		udhcp_str2optset(optstr, &client_config.options);
	}

	if (udhcp_read_interface(client_config.interface,
			&client_config.ifindex,
			NULL,
			client_config.client_mac)
	) {
		return 1;
	}

	clientid_mac_ptr = NULL;
	if (!(opt & OPT_C) && !udhcp_find_option(client_config.options, DHCP_CLIENT_ID)) {
		/* not suppressed and not set, set the default client ID */
		client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7);
		client_config.clientid[OPT_DATA] = 1; /* type: ethernet */
		clientid_mac_ptr = client_config.clientid + OPT_DATA+1;
		memcpy(clientid_mac_ptr, client_config.client_mac, 6);
	}
	if (str_V[0] != '\0') {
		// can drop -V, str_V, client_config.vendorclass,
		// but need to add "vendor" to the list of recognized
		// string opts for this to work;
		// and need to tweak add_client_options() too...
		// ...so the question is, should we?
		//bb_error_msg("option -V VENDOR is deprecated, use -x vendor:VENDOR");
		client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0);
	}

#if !BB_MMU
	/* on NOMMU reexec (i.e., background) early */
	if (!(opt & OPT_f)) {
		bb_daemonize_or_rexec(0 /* flags */, argv);
		logmode = LOGMODE_NONE;
	}
#endif
	if (opt & OPT_S) {
		openlog(applet_name, LOG_PID, LOG_DAEMON);
		logmode |= LOGMODE_SYSLOG;
	}

	/* Make sure fd 0,1,2 are open */
	bb_sanitize_stdio();
	/* Equivalent of doing a fflush after every \n */
	setlinebuf(stdout);
	/* Create pidfile */
	write_pidfile(client_config.pidfile);
	/* Goes to stdout (unless NOMMU) and possibly syslog */
	bb_info_msg("%s (v"BB_VER") started", applet_name);
	/* Set up the signal pipe */
	udhcp_sp_setup();
	/* We want random_xid to be random... */
	srand(monotonic_us());

	state = INIT_SELECTING;
	udhcp_run_script(NULL, "deconfig");
	change_listen_mode(LISTEN_RAW);
	packet_num = 0;
	timeout = 0;
	already_waited_sec = 0;

	/* Main event loop. select() waits on signal pipe and possibly
	 * on sockfd.
	 * "continue" statements in code below jump to the top of the loop.
	 */
	for (;;) {
		struct timeval tv;
		struct dhcp_packet packet;
		/* silence "uninitialized!" warning */
		unsigned timestamp_before_wait = timestamp_before_wait;

		//bb_error_msg("sockfd:%d, listen_mode:%d", sockfd, listen_mode);

		/* Was opening raw or udp socket here
		 * if (listen_mode != LISTEN_NONE && sockfd < 0),
		 * but on fast network renew responses return faster
		 * than we open sockets. Thus this code is moved
		 * to change_listen_mode(). Thus we open listen socket
		 * BEFORE we send renew request (see "case BOUND:"). */

		max_fd = udhcp_sp_fd_set(&rfds, sockfd);

		tv.tv_sec = timeout - already_waited_sec;
		tv.tv_usec = 0;
		retval = 0;
		/* If we already timed out, fall through with retval = 0, else... */
		if ((int)tv.tv_sec > 0) {
			timestamp_before_wait = (unsigned)monotonic_sec();
			log1("Waiting on select...");
			retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
			if (retval < 0) {
				/* EINTR? A signal was caught, don't panic */
				if (errno == EINTR) {
					already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
					continue;
				}
				/* Else: an error occured, panic! */
				bb_perror_msg_and_die("select");
			}
		}

		/* If timeout dropped to zero, time to become active:
		 * resend discover/renew/whatever
		 */
		if (retval == 0) {
			/* When running on a bridge, the ifindex may have changed
			 * (e.g. if member interfaces were added/removed
			 * or if the status of the bridge changed).
			 * Refresh ifindex and client_mac:
			 */
			if (udhcp_read_interface(client_config.interface,
					&client_config.ifindex,
					NULL,
					client_config.client_mac)
			) {
				goto ret0; /* iface is gone? */
			}
			if (clientid_mac_ptr)
				memcpy(clientid_mac_ptr, client_config.client_mac, 6);

			/* We will restart the wait in any case */
			already_waited_sec = 0;

			switch (state) {
			case INIT_SELECTING:
				if (packet_num < discover_retries) {
					if (packet_num == 0)
						xid = random_xid();
					/* broadcast */
					send_discover(xid, requested_ip);
					timeout = discover_timeout;
					packet_num++;
					continue;
				}
 leasefail:
				udhcp_run_script(NULL, "leasefail");
#if BB_MMU /* -b is not supported on NOMMU */
				if (opt & OPT_b) { /* background if no lease */
					bb_info_msg("No lease, forking to background");
					client_background();
					/* do not background again! */
					opt = ((opt & ~OPT_b) | OPT_f);
				} else
#endif
				if (opt & OPT_n) { /* abort if no lease */
					bb_info_msg("No lease, failing");
					retval = 1;
					goto ret;
				}
				/* wait before trying again */
				timeout = tryagain_timeout;
				packet_num = 0;
				continue;
			case REQUESTING:
				if (packet_num < discover_retries) {
					/* send broadcast select packet */
					send_select(xid, server_addr, requested_ip);
					timeout = discover_timeout;
					packet_num++;
					continue;
				}
				/* Timed out, go back to init state.
				 * "discover...select...discover..." loops
				 * were seen in the wild. Treat them similarly
				 * to "no response to discover" case */
				change_listen_mode(LISTEN_RAW);
				state = INIT_SELECTING;
				goto leasefail;
			case BOUND:
				/* 1/2 lease passed, enter renewing state */
				state = RENEWING;
				client_config.first_secs = 0; /* make secs field count from 0 */
				change_listen_mode(LISTEN_KERNEL);
				log1("Entering renew state");
				/* fall right through */
			case RENEW_REQUESTED: /* manual (SIGUSR1) renew */
			case_RENEW_REQUESTED:
			case RENEWING:
				if (timeout > 60) {
					/* send an unicast renew request */
			/* Sometimes observed to fail (EADDRNOTAVAIL) to bind
			 * a new UDP socket for sending inside send_renew.
			 * I hazard to guess existing listening socket
			 * is somehow conflicting with it, but why is it
			 * not deterministic then?! Strange.
			 * Anyway, it does recover by eventually failing through
			 * into INIT_SELECTING state.
			 */
					send_renew(xid, server_addr, requested_ip);
					timeout >>= 1;
					continue;
				}
				/* Timed out, enter rebinding state */
				log1("Entering rebinding state");
				state = REBINDING;
				/* fall right through */
			case REBINDING:
				/* Switch to bcast receive */
				change_listen_mode(LISTEN_RAW);
				/* Lease is *really* about to run out,
				 * try to find DHCP server using broadcast */
				if (timeout > 0) {
					/* send a broadcast renew request */
					send_renew(xid, 0 /*INADDR_ANY*/, requested_ip);
					timeout >>= 1;
					continue;
				}
				/* Timed out, enter init state */
				bb_info_msg("Lease lost, entering init state");
				udhcp_run_script(NULL, "deconfig");
				state = INIT_SELECTING;
				client_config.first_secs = 0; /* make secs field count from 0 */
				/*timeout = 0; - already is */
				packet_num = 0;
				continue;
			/* case RELEASED: */
			}
			/* yah, I know, *you* say it would never happen */
			timeout = INT_MAX;
			continue; /* back to main loop */
		} /* if select timed out */

		/* select() didn't timeout, something happened */

		/* Is it a signal? */
		/* note: udhcp_sp_read checks FD_ISSET before reading */
		switch (udhcp_sp_read(&rfds)) {
		case SIGUSR1:
			client_config.first_secs = 0; /* make secs field count from 0 */
			already_waited_sec = 0;
			perform_renew();
			if (state == RENEW_REQUESTED) {
				/* We might be either on the same network
				 * (in which case renew might work),
				 * or we might be on a completely different one
				 * (in which case renew won't ever succeed).
				 * For the second case, must make sure timeout
				 * is not too big, or else we can send
				 * futile renew requests for hours.
				 * (Ab)use -A TIMEOUT value (usually 20 sec)
				 * as a cap on the timeout.
				 */
				if (timeout > tryagain_timeout)
					timeout = tryagain_timeout;
				goto case_RENEW_REQUESTED;
			}
			/* Start things over */
			packet_num = 0;
			/* Kill any timeouts, user wants this to hurry along */
			timeout = 0;
			continue;
		case SIGUSR2:
			perform_release(server_addr, requested_ip);
			timeout = INT_MAX;
			continue;
		case SIGTERM:
			bb_info_msg("Received SIGTERM");
			goto ret0;
		}

		/* Is it a packet? */
		if (listen_mode == LISTEN_NONE || !FD_ISSET(sockfd, &rfds))
			continue; /* no */

		{
			int len;

			/* A packet is ready, read it */
			if (listen_mode == LISTEN_KERNEL)
				len = udhcp_recv_kernel_packet(&packet, sockfd);
			else
				len = udhcp_recv_raw_packet(&packet, sockfd);
			if (len == -1) {
				/* Error is severe, reopen socket */
				bb_info_msg("Read error: %s, reopening socket", strerror(errno));
				sleep(discover_timeout); /* 3 seconds by default */
				change_listen_mode(listen_mode); /* just close and reopen */
			}
			/* If this packet will turn out to be unrelated/bogus,
			 * we will go back and wait for next one.
			 * Be sure timeout is properly decreased. */
			already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait;
			if (len < 0)
				continue;
		}

		if (packet.xid != xid) {
			log1("xid %x (our is %x), ignoring packet",
				(unsigned)packet.xid, (unsigned)xid);
			continue;
		}

		/* Ignore packets that aren't for us */
		if (packet.hlen != 6
		 || memcmp(packet.chaddr, client_config.client_mac, 6) != 0
		) {
//FIXME: need to also check that last 10 bytes are zero
			log1("chaddr does not match, ignoring packet"); // log2?
			continue;
		}

		message = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
		if (message == NULL) {
			bb_error_msg("no message type option, ignoring packet");
			continue;
		}

		switch (state) {
		case INIT_SELECTING:
			/* Must be a DHCPOFFER */
			if (*message == DHCPOFFER) {
/* What exactly is server's IP? There are several values.
 * Example DHCP offer captured with tchdump:
 *
 * 10.34.25.254:67 > 10.34.25.202:68 // IP header's src
 * BOOTP fields:
 * Your-IP 10.34.25.202
 * Server-IP 10.34.32.125   // "next server" IP
 * Gateway-IP 10.34.25.254  // relay's address (if DHCP relays are in use)
 * DHCP options:
 * DHCP-Message Option 53, length 1: Offer
 * Server-ID Option 54, length 4: 10.34.255.7       // "server ID"
 * Default-Gateway Option 3, length 4: 10.34.25.254 // router
 *
 * We think that real server IP (one to use in renew/release)
 * is one in Server-ID option. But I am not 100% sure.
 * IP header's src and Gateway-IP (same in this example)
 * might work too.
 * "Next server" and router are definitely wrong ones to use, though...
 */
				temp = udhcp_get_option(&packet, DHCP_SERVER_ID);
				if (!temp) {
					bb_error_msg("no server ID, ignoring packet");
					continue;
					/* still selecting - this server looks bad */
				}
				/* it IS unaligned sometimes, don't "optimize" */
				move_from_unaligned32(server_addr, temp);
				/*xid = packet.xid; - already is */
				requested_ip = packet.yiaddr;

				/* enter requesting state */
				state = REQUESTING;
				timeout = 0;
				packet_num = 0;
				already_waited_sec = 0;
			}
			continue;
		case REQUESTING:
		case RENEWING:
		case RENEW_REQUESTED:
		case REBINDING:
			if (*message == DHCPACK) {
				uint32_t lease_seconds;
				struct in_addr temp_addr;

				temp = udhcp_get_option(&packet, DHCP_LEASE_TIME);
				if (!temp) {
					bb_error_msg("no lease time with ACK, using 1 hour lease");
					lease_seconds = 60 * 60;
				} else {
					/* it IS unaligned sometimes, don't "optimize" */
					move_from_unaligned32(lease_seconds, temp);
					lease_seconds = ntohl(lease_seconds);
					/* paranoia: must not be too small and not prone to overflows */
					if (lease_seconds < 0x10)
						lease_seconds = 0x10;
					if (lease_seconds >= 0x10000000)
						lease_seconds = 0x0fffffff;
				}
#if ENABLE_FEATURE_UDHCPC_ARPING
				if (opt & OPT_a) {
/* RFC 2131 3.1 paragraph 5:
 * "The client receives the DHCPACK message with configuration
 * parameters. The client SHOULD perform a final check on the
 * parameters (e.g., ARP for allocated network address), and notes
 * the duration of the lease specified in the DHCPACK message. At this
 * point, the client is configured. If the client detects that the
 * address is already in use (e.g., through the use of ARP),
 * the client MUST send a DHCPDECLINE message to the server and restarts
 * the configuration process..." */
					if (!arpping(packet.yiaddr,
							NULL,
							(uint32_t) 0,
							client_config.client_mac,
							client_config.interface)
					) {
						bb_info_msg("Offered address is in use "
							"(got ARP reply), declining");
						send_decline(/*xid,*/ server_addr, packet.yiaddr);

						if (state != REQUESTING)
							udhcp_run_script(NULL, "deconfig");
						change_listen_mode(LISTEN_RAW);
						state = INIT_SELECTING;
						client_config.first_secs = 0; /* make secs field count from 0 */
						requested_ip = 0;
						timeout = tryagain_timeout;
						packet_num = 0;
						already_waited_sec = 0;
						continue; /* back to main loop */
					}
				}
#endif
				/* enter bound state */
				timeout = lease_seconds / 2;
			        temp_addr.s_addr = packet.yiaddr;
				bb_info_msg("Lease of %s obtained, lease time %u",
					inet_ntoa(temp_addr), (unsigned)lease_seconds);
				requested_ip = packet.yiaddr;
				udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew");

				state = BOUND;
				change_listen_mode(LISTEN_NONE);
				if (opt & OPT_q) { /* quit after lease */
					goto ret0;
				}
				/* future renew failures should not exit (JM) */
				opt &= ~OPT_n;
#if BB_MMU /* NOMMU case backgrounded earlier */
				if (!(opt & OPT_f)) {
					client_background();
					/* do not background again! */
					opt = ((opt & ~OPT_b) | OPT_f);
				}
#endif
				/* make future renew packets use different xid */
				/* xid = random_xid(); ...but why bother? */
				already_waited_sec = 0;
				continue; /* back to main loop */
			}
			if (*message == DHCPNAK) {
				/* return to init state */
				bb_info_msg("Received DHCP NAK");
				udhcp_run_script(&packet, "nak");
				if (state != REQUESTING)
					udhcp_run_script(NULL, "deconfig");
				change_listen_mode(LISTEN_RAW);
				sleep(3); /* avoid excessive network traffic */
				state = INIT_SELECTING;
				client_config.first_secs = 0; /* make secs field count from 0 */
				requested_ip = 0;
				timeout = 0;
				packet_num = 0;
				already_waited_sec = 0;
			}
			continue;
		/* case BOUND: - ignore all packets */
		/* case RELEASED: - ignore all packets */
		}
		/* back to main loop */
	} /* for (;;) - main loop ends */

 ret0:
	if (opt & OPT_R) /* release on quit */
		perform_release(server_addr, requested_ip);
	retval = 0;
 ret:
	/*if (client_config.pidfile) - remove_pidfile has its own check */
		remove_pidfile(client_config.pidfile);
	return retval;
}


static char *allocate_tempopt_if_needed(
		const struct dhcp_optflag *optflag,
		char *buffer,
		int *length_p)
{
	char *allocated = NULL;
	if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_BIN) {
		const char *end;
		allocated = xstrdup(buffer); /* more than enough */
		end = hex2bin(allocated, buffer, 255);
		if (errno)
			bb_error_msg_and_die("malformed hex string '%s'", buffer);
		*length_p = end - allocated;
	}
	return allocated;
}


static int good_hostname(const char *name)
{
	//const char *start = name;

	for (;;) {
		name = valid_domain_label(name);
		if (!name)
			return 0;
		if (!name[0])
			return 1;
			//Do we want this?
			//return ((name - start) < 1025); /* NS_MAXDNAME */
		name++;
	}
}


uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code)
{
	uint8_t *optionptr;
	int len;
	int rem;
	int overload = 0;
	enum {
		FILE_FIELD101  = FILE_FIELD  * 0x101,
		SNAME_FIELD101 = SNAME_FIELD * 0x101,
	};

	/* option bytes: [code][len][data1][data2]..[dataLEN] */
	optionptr = packet->options;
	rem = sizeof(packet->options);
	while (1) {
		if (rem <= 0) {
			bb_error_msg("bad packet, malformed option field");
			return NULL;
		}
		if (optionptr[OPT_CODE] == DHCP_PADDING) {
			rem--;
			optionptr++;
			continue;
		}
		if (optionptr[OPT_CODE] == DHCP_END) {
			if ((overload & FILE_FIELD101) == FILE_FIELD) {
				/* can use packet->file, and didn't look at it yet */
				overload |= FILE_FIELD101; /* "we looked at it" */
				optionptr = packet->file;
				rem = sizeof(packet->file);
				continue;
			}
			if ((overload & SNAME_FIELD101) == SNAME_FIELD) {
				/* can use packet->sname, and didn't look at it yet */
				overload |= SNAME_FIELD101; /* "we looked at it" */
				optionptr = packet->sname;
				rem = sizeof(packet->sname);
				continue;
			}
			break;
		}
		len = 2 + optionptr[OPT_LEN];
		rem -= len;
		if (rem < 0)
			continue; /* complain and return NULL */

		if (optionptr[OPT_CODE] == code) {
			log_option("Option found", optionptr);
			return optionptr + OPT_DATA;
		}

		if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
			overload |= optionptr[OPT_DATA];
			/* fall through */
		}
		optionptr += len;
	}

	/* log3 because udhcpc uses it a lot - very noisy */
	log3("Option 0x%02x not found", code);
	return NULL;
}


static void perform_release(uint32_t server_addr, uint32_t requested_ip)
{
	char buffer[sizeof("255.255.255.255")];
	struct in_addr temp_addr;

	/* send release packet */
	if (state == BOUND || state == RENEWING || state == REBINDING) {
		temp_addr.s_addr = server_addr;
		strcpy(buffer, inet_ntoa(temp_addr));
		temp_addr.s_addr = requested_ip;
		bb_info_msg("Unicasting a release of %s to %s",
				inet_ntoa(temp_addr), buffer);
		send_release(server_addr, requested_ip); /* unicast */
		udhcp_run_script(NULL, "deconfig");
	}
	bb_info_msg("Entering released state");

	change_listen_mode(LISTEN_NONE);
	state = RELEASED;
}


void hm_flush(struct cli_matcher *root) {
    enum CLI_HASH_TYPE type;

    if(!root)
	return;

    for(type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
	struct cli_htu32 *ht = &root->hm.sizehashes[type];
	const struct cli_htu32_element *item = NULL;

	if(!root->hm.sizehashes[type].capacity)
	    continue;

	while((item = cli_htu32_next(ht, item))) {
	    struct cli_sz_hash *szh = (struct cli_sz_hash *)item->data.as_ptr;
	    unsigned int keylen = hashlen[type];

	    if(szh->items > 1)
		hm_sort(szh, 0, szh->items, keylen);
	}
    }
}


tiff_unmap_file (thandle_t handle, tdata_t data, toff_t offset)
{
}


gdk_pixbuf__tiff_image_begin_load (ModulePreparedNotifyFunc prepare_func,
				   ModuleUpdatedNotifyFunc update_func,
				   gpointer user_data,
                                   GError **error)
{
	TiffContext *context;

	context = g_new0 (TiffContext, 1);
	context->prepare_func = prepare_func;
	context->update_func = update_func;
	context->user_data = user_data;
        context->buffer = NULL;
        context->allocated = 0;
        context->used = 0;
        context->pos = 0;

	return context;
}


gif_get_frame_info (GifContext *context)
{
	unsigned char buf[9];

	if (!gif_read (context, buf, 9)) {
		return -1;
	}

	/* Okay, we got all the info we need.  Lets record it */
	context->frame_len = LM_to_uint (buf[4], buf[5]);
	context->frame_height = LM_to_uint (buf[6], buf[7]);
	context->x_offset = LM_to_uint (buf[0], buf[1]);
	context->y_offset = LM_to_uint (buf[2], buf[3]);

	if (((context->frame_height + context->y_offset) > context->height) ||
            ((context->frame_len + context->x_offset) > context->width)) {
		/* All frames must fit in the image bounds */
		context->state = GIF_DONE;

                g_set_error (context->error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("GIF image contained a frame appearing outside the image bounds."));

		return -2;
	}

	if (context->animation->frames == NULL &&
            context->gif89.disposal == 3) {
                /* First frame can't have "revert to previous" as its
                 * dispose mode.
                 */

		context->state = GIF_DONE;

                g_set_error (context->error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("First frame of GIF image had 'revert to previous' as its disposal mode."));

		return -2;
	}

#ifdef DUMP_IMAGE_DETAILS
        g_print (">width: %d height: %d xoffset: %d yoffset: %d disposal: %d delay: %d transparent: %d\n",
                 context->frame_len, context->frame_height, context->x_offset, context->y_offset,
                 context->gif89.disposal, context->gif89.delay_time, context->gif89.transparent);
#endif

	context->frame_interlace = BitSet (buf[8], INTERLACE);
	if (BitSet (buf[8], LOCALCOLORMAP)) {

#ifdef DUMP_IMAGE_DETAILS
                g_print (">has local colormap\n");
#endif

		/* Does this frame have it's own colormap. */
		/* really only relevant when looking at the first frame
		 * of an animated gif. */
		/* if it does, we need to re-read in the colormap,
		 * the gray_scale, and the bit_pixel */
                context->frame_cmap_active = TRUE;
		context->frame_bit_pixel = 1 << ((buf[8] & 0x07) + 1);
		gif_set_get_colormap2 (context);
		return 0;
	}

        if (!context->has_global_cmap) {
                context->state = GIF_DONE;

                g_set_error (context->error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("GIF image has no global colormap, and a frame inside it has no local colormap."));

		return -2;
        }

	gif_set_prepare_lzw (context);
	return 0;

}


tiff_map_file (thandle_t handle, tdata_t *buf, toff_t *size)
{
        TiffContext *context = (TiffContext *)handle;

        *buf = context->buffer;
        *size = context->used;

        return 0;
}


ReadColorMap (FILE *fd,
	      int   number,
	      CMap  buffer,
	      int  *format)
{
  int i;
  unsigned char rgb[3];
  int flag;

  flag = TRUE;

  for (i = 0; i < number; ++i)
    {
      if (!ReadOK (fd, rgb, sizeof (rgb)))
	{
	  g_message ("GIF: bad colormap\n");
	  return TRUE;
	}

      buffer[CM_RED][i] = rgb[0];
      buffer[CM_GREEN][i] = rgb[1];
      buffer[CM_BLUE][i] = rgb[2];

      flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
    }

  *format = (flag) ? GRAYSCALE : COLOR;

  return FALSE;
}


static void doDie(int sig)
{
#	define MSG1 "DoDie called.\n"
#	define MSG2 "DoDie called 5 times - unconditional exit\n"
	static int iRetries = 0; /* debug aid */
	if(Debug)
		write(1, MSG1, sizeof(MSG1) - 1);
	if(iRetries++ == 4) {
		if(Debug)
			write(1, MSG2, sizeof(MSG2) - 1);
		abort();
	}
	bFinished = sig;
#	undef MSG1
#	undef MSG2
}


diagGetMainMsgQSize(int *piSize)
{
	DEFiRet;
	assert(piSize != NULL);
	*piSize = (pMsgQueue->pqDA != NULL) ? pMsgQueue->pqDA->iQueueSize : 0;
	*piSize += pMsgQueue->iQueueSize;
	RETiRet;
}


destructAllActions(void)
{
	ruleset.DestructAllActions();
	bHaveMainQueue = 0; // flag that internal messages need to be temporarily stored
}


runInputModules(void)
{
	modInfo_t *pMod;

	BEGINfunc
	/* loop through all modules and activate them (brr...) */
	pMod = module.GetNxtType(NULL, eMOD_IN);
	while(pMod != NULL) {
		if(pMod->mod.im.bCanRun) {
			/* activate here */
			thrdCreate(pMod->mod.im.runInput, pMod->mod.im.afterRun);
		}
	pMod = module.GetNxtType(pMod, eMOD_IN);
	}

	ENDfunc
	return RS_RET_OK; /* intentional: we do not care about module errors */
}


ReadImage (FILE        *fd,
           const gchar *filename,
           gint         len,
           gint         height,
           CMap         cmap,
           gint         ncols,
           gint         format,
           gint         interlace,
           gint         number,
           guint        leftpos,
           guint        toppos,
           guint        screenwidth,
           guint        screenheight)
{
  static gint32 image_ID   = -1;
  static gint   frame_number = 1;

  gint32        layer_ID;
  GimpPixelRgn  pixel_rgn;
  GimpDrawable *drawable;
  guchar       *dest, *temp;
  guchar        c;
  gint          xpos = 0, ypos = 0, pass = 0;
  gint          cur_progress, max_progress;
  gint          v;
  gint          i, j;
  gchar        *framename;
  gchar        *framename_ptr;
  gboolean      alpha_frame = FALSE;
  static gint   previous_disposal;

  /* Guard against bogus frame size */
  if (len < 1 || height < 1)
    {
      g_message ("Bogus frame dimensions");
      return -1;
    }

  /*
   **  Initialize the Compression routines
   */
  if (! ReadOK (fd, &c, 1))
    {
      g_message ("EOF / read error on image data");
      return -1;
    }

  if (LZWReadByte (fd, TRUE, c) < 0)
    {
      g_message ("Error while reading");
      return -1;
    }

  if (frame_number == 1)
    {
      /* Guard against bogus logical screen size values */
      if (screenwidth == 0)
        screenwidth = len;

      if (screenheight == 0)
        screenheight = height;

      image_ID = gimp_image_new (screenwidth, screenheight, GIMP_INDEXED);
      gimp_image_set_filename (image_ID, filename);

      for (i = 0, j = 0; i < ncols; i++)
        {
          used_cmap[0][i] = gimp_cmap[j++] = cmap[0][i];
          used_cmap[1][i] = gimp_cmap[j++] = cmap[1][i];
          used_cmap[2][i] = gimp_cmap[j++] = cmap[2][i];
        }

      gimp_image_set_colormap (image_ID, gimp_cmap, ncols);

      if (Gif89.delayTime < 0)
        framename = g_strdup (_("Background"));
      else
        framename = g_strdup_printf (_("Background (%d%s)"),
                                     10 * Gif89.delayTime, "ms");

      previous_disposal = Gif89.disposal;

      if (Gif89.transparent == -1)
        {
          layer_ID = gimp_layer_new (image_ID, framename,
                                     len, height,
                                     GIMP_INDEXED_IMAGE, 100, GIMP_NORMAL_MODE);
        }
      else
        {
          layer_ID = gimp_layer_new (image_ID, framename,
                                     len, height,
                                     GIMP_INDEXEDA_IMAGE, 100, GIMP_NORMAL_MODE);
          alpha_frame=TRUE;
        }

      g_free (framename);
    }
  else /* NOT FIRST FRAME */
    {
      gimp_progress_set_text_printf (_("Opening '%s' (frame %d)"),
                                     gimp_filename_to_utf8 (filename),
                                     frame_number);
      gimp_progress_pulse ();

       /* If the colourmap is now different, we have to promote to RGB! */
      if (! promote_to_rgb)
        {
          for (i = 0; i < ncols; i++)
            {
              if ((used_cmap[0][i] != cmap[0][i]) ||
                  (used_cmap[1][i] != cmap[1][i]) ||
                  (used_cmap[2][i] != cmap[2][i]))
                {
                  /* Everything is RGB(A) from now on... sigh. */
                  promote_to_rgb = TRUE;

                  /* Promote everything we have so far into RGB(A) */
#ifdef GIFDEBUG
                  g_print ("GIF: Promoting image to RGB...\n");
#endif
                  gimp_image_convert_rgb (image_ID);

                  break;
                }
            }
        }

      if (Gif89.delayTime < 0)
        framename = g_strdup_printf (_("Frame %d"), frame_number);
      else
        framename = g_strdup_printf (_("Frame %d (%d%s)"),
                                     frame_number, 10 * Gif89.delayTime, "ms");

      switch (previous_disposal)
        {
        case 0x00:
          break; /* 'don't care' */
        case 0x01:
          framename_ptr = framename;
          framename = g_strconcat (framename, " (combine)", NULL);
          g_free (framename_ptr);
          break;
        case 0x02:
          framename_ptr = framename;
          framename = g_strconcat (framename, " (replace)", NULL);
          g_free (framename_ptr);
          break;
        case 0x03:  /* Rarely-used, and unhandled by many
                       loaders/players (including GIMP: we treat as
                       'combine' mode). */
          framename_ptr = framename;
          framename = g_strconcat (framename, " (combine) (!)", NULL);
          g_free (framename_ptr);
          break;
        case 0x04: /* I've seen a composite of this type. stvo_online_banner2.gif */
        case 0x05:
        case 0x06: /* I've seen a composite of this type. bn31.Gif */
        case 0x07:
          framename_ptr = framename;
          framename = g_strconcat (framename, " (unknown disposal)", NULL);
          g_free (framename_ptr);
          g_message (_("GIF: Undocumented GIF composite type %d is "
                       "not handled.  Animation might not play or "
                       "re-save perfectly."),
                     previous_disposal);
          break;
        default:
          g_message ("Disposal word got corrupted.  Bug.");
          break;
        }
      previous_disposal = Gif89.disposal;

      layer_ID = gimp_layer_new (image_ID, framename,
                                 len, height,
                                 promote_to_rgb ?
                                 GIMP_RGBA_IMAGE : GIMP_INDEXEDA_IMAGE,
                                 100, GIMP_NORMAL_MODE);
      alpha_frame = TRUE;
      g_free (framename);
    }

  frame_number++;

  gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
  gimp_layer_translate (layer_ID, (gint) leftpos, (gint) toppos);

  drawable = gimp_drawable_get (layer_ID);

  cur_progress = 0;
  max_progress = height;

  if (alpha_frame)
    dest = (guchar *) g_malloc (len * height * (promote_to_rgb ? 4 : 2));
  else
    dest = (guchar *) g_malloc (len * height);

#ifdef GIFDEBUG
    g_print ("GIF: reading %d by %d%s GIF image, ncols=%d\n",
             len, height, interlace ? " interlaced" : "", ncols);
#endif

  if (! alpha_frame && promote_to_rgb)
    {
      /* I don't see how one would easily construct a GIF in which
         this could happen, but it's a mad mad world. */
      g_message ("Ouch!  Can't handle non-alpha RGB frames.\n"
                 "Please file a bug report in GIMP's bugzilla.");
      gimp_quit ();
    }

  while ((v = LZWReadByte (fd, FALSE, c)) >= 0)
    {
      if (alpha_frame)
        {
          if (((guchar) v > highest_used_index) && !(v == Gif89.transparent))
            highest_used_index = (guchar) v;

          if (promote_to_rgb)
            {
              temp = dest + ( (ypos * len) + xpos ) * 4;
              *(temp  ) = (guchar) cmap[0][v];
              *(temp+1) = (guchar) cmap[1][v];
              *(temp+2) = (guchar) cmap[2][v];
              *(temp+3) = (guchar) ((v == Gif89.transparent) ? 0 : 255);
            }
          else
            {
              temp = dest + ( (ypos * len) + xpos ) * 2;
              *temp = (guchar) v;
              *(temp+1) = (guchar) ((v == Gif89.transparent) ? 0 : 255);
            }
        }
      else
        {
          if ((guchar) v > highest_used_index)
            highest_used_index = (guchar) v;

          temp = dest + (ypos * len) + xpos;
          *temp = (guchar) v;
        }

      xpos++;
      if (xpos == len)
        {
          xpos = 0;
          if (interlace)
            {
              switch (pass)
                {
                case 0:
                case 1:
                  ypos += 8;
                  break;
                case 2:
                  ypos += 4;
                  break;
                case 3:
                  ypos += 2;
                  break;
                }

              if (ypos >= height)
                {
                  pass++;
                  switch (pass)
                    {
                    case 1:
                      ypos = 4;
                      break;
                    case 2:
                      ypos = 2;
                      break;
                    case 3:
                      ypos = 1;
                      break;
                    default:
                      goto fini;
                    }
                }
            }
          else
            {
              ypos++;
            }

          if (frame_number == 1)
            {
              cur_progress++;
              if ((cur_progress % 16) == 0)
                gimp_progress_update ((gdouble) cur_progress /
                                      (gdouble) max_progress);
            }
        }

      if (ypos >= height)
        break;
    }

 fini:
  if (LZWReadByte (fd, FALSE, c) >= 0)
    g_print ("GIF: too much input data, ignoring extra...\n");

  gimp_progress_update (1.0);
  gimp_pixel_rgn_init (&pixel_rgn, drawable,
                       0, 0, drawable->width, drawable->height, TRUE, FALSE);
  gimp_pixel_rgn_set_rect (&pixel_rgn, dest,
                           0, 0, drawable->width, drawable->height);

  g_free (dest);

  gimp_drawable_flush (drawable);
  gimp_drawable_detach (drawable);

  return image_ID;
}


static int newnews_cb(const char *msgid, const char *rcpt, time_t mark,
		      unsigned long uid, void *rock)
{
    static char lastid[1024];
    struct newrock *nrock = (struct newrock *) rock;

    /* We have to reset the initial state.
     * Handle it as a dirty hack.
     */
    if (!msgid) {
	lastid[0] = '\0';
	return 0;
    }

    /* Make sure we don't return duplicate msgids,
     * the message is newer than the tstamp, and
     * the message is in mailbox we serve as a newsgroup..
     */
    if (strcmp(msgid, lastid) && mark >= nrock->tstamp &&
	uid && is_newsgroup(rcpt)) {
	struct wildmat *wild = nrock->wild;

	/* see if the mailbox matches one of our specified wildmats */
	while (wild->pat && wildmat(rcpt, wild->pat) != 1) wild++;

	/* we have a match, and its not a negative match */
	if (wild->pat && !wild->not) {
	    prot_printf(nntp_out, "%s\r\n", msgid);
	    strlcpy(lastid, msgid, sizeof(lastid));
	}
    }

    return 0;
}


static void cmd_newnews(char *wild, time_t tstamp)
{
    struct newrock nrock;

    nrock.tstamp = tstamp;
    nrock.wild = split_wildmats(wild);

    prot_printf(nntp_out, "230 List of new articles follows:\r\n");

    newnews_cb(NULL, NULL, 0, 0, NULL);
    duplicate_find("", &newnews_cb, &nrock);

    prot_printf(nntp_out, ".\r\n");

    free_wildmats(nrock.wild);
}


static void cmd_newgroups(time_t tstamp __attribute__((unused)))
{
    prot_printf(nntp_out, "503 Can't determine NEWGROUPS at this time\r\n");
#if 0
    prot_printf(nntp_out, "231 List of new newsgroups follows:\r\n");

    /* Do search of annotations here. */

    prot_printf(nntp_out, ".\r\n");
#endif
}


xmlXPtrNbLocChildren(xmlNodePtr node) {
    int ret = 0;
    if (node == NULL)
	return(-1);
    switch (node->type) {
        case XML_HTML_DOCUMENT_NODE:
        case XML_DOCUMENT_NODE:
        case XML_ELEMENT_NODE:
	    node = node->children;
	    while (node != NULL) {
		if (node->type == XML_ELEMENT_NODE)
		    ret++;
		node = node->next;
	    }
	    break;
        case XML_ATTRIBUTE_NODE:
	    return(-1);

        case XML_PI_NODE:
        case XML_COMMENT_NODE:
        case XML_TEXT_NODE:
        case XML_CDATA_SECTION_NODE:
        case XML_ENTITY_REF_NODE:
	    ret = xmlStrlen(node->content);
	    break;
	default:
	    return(-1);
    }
    return(ret);
}


xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt, int nargs) {
    int i;
    xmlXPathObjectPtr set;
    xmlLocationSetPtr oldset;
    xmlLocationSetPtr newset;

    CHECK_ARITY(1);
    if ((ctxt->value == NULL) ||
	((ctxt->value->type != XPATH_LOCATIONSET) &&
	 (ctxt->value->type != XPATH_NODESET)))
        XP_ERROR(XPATH_INVALID_TYPE)

    set = valuePop(ctxt);
    if (set->type == XPATH_NODESET) {
	xmlXPathObjectPtr tmp;

	/*
	 * First convert to a location set
	 */
	tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval);
	xmlXPathFreeObject(set);
	set = tmp;
    }
    oldset = (xmlLocationSetPtr) set->user;

    /*
     * The loop is to compute the covering range for each item and add it
     */
    newset = xmlXPtrLocationSetCreate(NULL);
    for (i = 0;i < oldset->locNr;i++) {
	xmlXPtrLocationSetAdd(newset,
		xmlXPtrInsideRange(ctxt, oldset->locTab[i]));
    }

    /*
     * Save the new value and cleanup
     */
    valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
    xmlXPathFreeObject(set);
}


xmlXPtrAdvanceChar(xmlNodePtr *node, int *indx, int bytes) {
    xmlNodePtr cur;
    int pos;
    int len;

    if ((node == NULL) || (indx == NULL))
	return(-1);
    cur = *node;
    if (cur == NULL)
	return(-1);
    pos = *indx;

    while (bytes >= 0) {
	/*
	 * First position to the beginning of the first text node
	 * corresponding to this point
	 */
	while ((cur != NULL) &&
	       ((cur->type == XML_ELEMENT_NODE) ||
	        (cur->type == XML_DOCUMENT_NODE) ||
	        (cur->type == XML_HTML_DOCUMENT_NODE))) {
	    if (pos > 0) {
		cur = xmlXPtrGetNthChild(cur, pos);
		pos = 0;
	    } else {
		cur = xmlXPtrAdvanceNode(cur, NULL);
		pos = 0;
	    }
	}

	if (cur == NULL) {
	    *node = NULL;
	    *indx = 0;
	    return(-1);
	}

	/*
	 * if there is no move needed return the current value.
	 */
	if (pos == 0) pos = 1;
	if (bytes == 0) {
	    *node = cur;
	    *indx = pos;
	    return(0);
	}
	/*
	 * We should have a text (or cdata) node ...
	 */
	len = 0;
	if ((cur->type != XML_ELEMENT_NODE) &&
            (cur->content != NULL)) {
	    len = xmlStrlen(cur->content);
	}
	if (pos > len) {
	    /* Strange, the indx in the text node is greater than it's len */
	    STRANGE
	    pos = len;
	}
	if (pos + bytes >= len) {
	    bytes -= (len - pos);
	    cur = xmlXPtrAdvanceNode(cur, NULL);
	    pos = 0;
	} else if (pos + bytes < len) {
	    pos += bytes;
	    *node = cur;
	    *indx = pos;
	    return(0);
	}
    }
    return(-1);
}


xmlXPtrLocationSetMerge(xmlLocationSetPtr val1, xmlLocationSetPtr val2) {
    int i;

    if (val1 == NULL) return(NULL);
    if (val2 == NULL) return(val1);

    /*
     * !!!!! this can be optimized a lot, knowing that both
     *       val1 and val2 already have unicity of their values.
     */

    for (i = 0;i < val2->locNr;i++)
        xmlXPtrLocationSetAdd(val1, val2->locTab[i]);

    return(val1);
}


QByteArray CtcpParser::lowLevelQuote(const QByteArray &message) {
  QByteArray quotedMessage = message;

  QHash<QByteArray, QByteArray> quoteHash = _ctcpMDequoteHash;
  QByteArray MQUOTE = QByteArray("\020");
  quoteHash.remove(MQUOTE + MQUOTE);
  quotedMessage.replace(MQUOTE, MQUOTE + MQUOTE);

  QHash<QByteArray, QByteArray>::const_iterator quoteIter = quoteHash.constBegin();
  while(quoteIter != quoteHash.constEnd()) {
    quotedMessage.replace(quoteIter.value(), quoteIter.key());
    quoteIter++;
  }
  return quotedMessage;
}


static void cmd_help(void)
{
    prot_printf(nntp_out, "100 Supported commands:\r\n");

    if ((nntp_capa & MODE_READ) && (nntp_authstate || allowanonymous)) {
	prot_printf(nntp_out, "\tARTICLE [ message-id | number ]\r\n"
		    "\t\tRetrieve entirety of the specified article.\r\n");
    }
    if (!nntp_authstate) {
	if (!nntp_userid) {
	    prot_printf(nntp_out, "\tAUTHINFO SASL mechanism [initial-response]\r\n"
			"\t\tPerform an authentication exchange using the specified\r\n"
			"\t\tSASL mechanism.\r\n");
	    prot_printf(nntp_out, "\tAUTHINFO USER username\r\n"
			"\t\tPresent username for authentication.\r\n");
	}
	prot_printf(nntp_out, "\tAUTHINFO PASS password\r\n"
		    "\t\tPresent clear-text password for authentication.\r\n");
    }
    if ((nntp_capa & MODE_READ) && (nntp_authstate || allowanonymous)) {
	prot_printf(nntp_out, "\tBODY [ message-id | number ]\r\n"
		    "\t\tRetrieve body of the specified article.\r\n");
    }
    prot_printf(nntp_out, "\tCAPABILITIES\r\n"
		"\t\tList the current server capabilities.\r\n");
    if (nntp_capa & MODE_FEED) {
	prot_printf(nntp_out, "\tCHECK message-id\r\n"
		    "\t\tCheck if the server wants the specified article.\r\n");
    }
    if ((nntp_capa & MODE_READ) && (nntp_authstate || allowanonymous)) {
	prot_printf(nntp_out, "\tDATE\r\n"
		    "\t\tRequest the current server UTC date and time.\r\n");
	prot_printf(nntp_out, "\tGROUP group\r\n"
		    "\t\tSelect a newsgroup for article retrieval.\r\n");
	prot_printf(nntp_out, "\tHDR header [ message-id | range ]\r\n"
		    "\t\tRetrieve the specified header/metadata from the\r\n"
		    "\t\tspecified article(s).\r\n");
    }
    prot_printf(nntp_out, "\tHEAD [ message-id | number ]\r\n"
		"\t\tRetrieve the headers of the specified article.\r\n");
    prot_printf(nntp_out, "\tHELP\r\n"
		"\t\tRequest command summary (this text).\r\n");
    if (nntp_capa & MODE_FEED) {
	prot_printf(nntp_out, "\tIHAVE message-id\r\n"
		    "\t\tPresent/transfer the specified article to the server.\r\n");
    }
    if ((nntp_capa & MODE_READ) && (nntp_authstate || allowanonymous)) {
	prot_printf(nntp_out, "\tLAST\r\n"
		    "\t\tSelect the previous article.\r\n");
    }
    prot_printf(nntp_out, "\tLIST [ ACTIVE wildmat ]\r\n"
		"\t\tList the (subset of) valid newsgroups.\r\n");
    if ((nntp_capa & MODE_READ) && (nntp_authstate || allowanonymous)) {
	prot_printf(nntp_out, "\tLIST HEADERS [ MSGID | RANGE ]\r\n"
		    "\t\tList the headers and metadata items available via HDR.\r\n");
	prot_printf(nntp_out, "\tLIST NEWSGROUPS [wildmat]\r\n"
		    "\t\tList the descriptions of the specified newsgroups.\r\n");
	prot_printf(nntp_out, "\tLIST OVERVIEW.FMT\r\n"
		    "\t\tList the headers and metadata items available via OVER.\r\n");
	prot_printf(nntp_out, "\tLISTGROUP [group [range]]\r\n"
		    "\t\tList the article numbers in the specified newsgroup.\r\n");
	if (config_getswitch(IMAPOPT_ALLOWNEWNEWS))
	    prot_printf(nntp_out, "\tNEWNEWS wildmat date time [GMT]\r\n"
			"\t\tList the newly arrived articles in the specified newsgroup(s)\r\n"
			"\t\tsince the specified date and time.\r\n");
	prot_printf(nntp_out, "\tNEXT\r\n"
		    "\t\tSelect the next article.\r\n");
	prot_printf(nntp_out, "\tOVER [ message-id | range ]\r\n"
		    "\t\tRetrieve the overview information for the specified article(s).\r\n");
	prot_printf(nntp_out, "\tPOST\r\n"
		    "\t\tPost an article to the server.\r\n");
    }

    prot_printf(nntp_out, "\tQUIT\r\n"
		"\t\tTerminate the session.\r\n");
    if (tls_enabled() && !nntp_starttls_done && !nntp_authstate) {
	prot_printf(nntp_out, "\tSTARTTLS\r\n"
		    "\t\tStart a TLS negotiation.\r\n");
    }
    prot_printf(nntp_out, "\tSTAT [ message-id | number ]\r\n"
		"\t\tCheck if the specified article exists.\r\n");
    if (nntp_capa & MODE_FEED) {
	prot_printf(nntp_out, "\tTAKETHIS message-id\r\n"
		    "\t\tTransfer the specified article to the server.\r\n");
    }
    if ((nntp_capa & MODE_READ) && (nntp_authstate || allowanonymous)) {
	prot_printf(nntp_out, "\tXPAT header message-id|range wildmat\r\n"
		    "\t\tList the specified article(s) in which the contents\r\n"
		    "\t\tof the specified header/metadata matches the wildmat.\r\n");
    }
    prot_printf(nntp_out, ".\r\n");
}


int do_newsgroups(char *name, void *rock)
{
    struct list_rock *lrock = (struct list_rock *) rock;
    char *acl, *server;
    int r;

    r = mlookup(name, &server, &acl, NULL);

    if (r || !acl || !(cyrus_acl_myrights(nntp_authstate, acl) && ACL_LOOKUP))
	return 0;

    if (server) {
	/* remote group */
	if (!hash_lookup(server, &lrock->server_table)) {
	    /* add this server to our table */
	    hash_insert(server, (void *)0xDEADBEEF, &lrock->server_table);
	}
    }
    else {
	/* local group */
	return CYRUSDB_DONE;
    }

    return 0;
}


static void cmd_authinfo_user(char *user)
{
    char *p;

    if (nntp_authstate) {
	prot_printf(nntp_out, "502 Already authenticated\r\n");
	return;
    }

    /* possibly disallow USER */
    if (!(nntp_starttls_done || (extprops_ssf > 1) ||
	  config_getswitch(IMAPOPT_ALLOWPLAINTEXT))) {
	prot_printf(nntp_out,
		    "483 AUTHINFO USER command only available under a layer\r\n");
	return;
    }

    if (nntp_userid) {
	free(nntp_userid);
	nntp_userid = NULL;
    }

    if (!(p = canonify_userid(user, NULL, NULL))) {
	prot_printf(nntp_out, "481 Invalid user\r\n");
	syslog(LOG_NOTICE,
	       "badlogin: %s plaintext %s invalid user",
	       nntp_clienthost, beautify_string(user));
    }
    else {
	nntp_userid = xstrdup(p);
	prot_printf(nntp_out, "381 Give AUTHINFO PASS command\r\n");
    }
}


static void cmdloop(void)
{
    int c, r = 0, mode;
    static struct buf cmd, arg1, arg2, arg3, arg4;
    char *p, *result, buf[1024];
    const char *err;
    uint32_t uid, last;
    struct backend *be;
    char curgroup[MAX_MAILBOX_BUFFER] = "";

    allowanonymous = config_getswitch(IMAPOPT_ALLOWANONYMOUSLOGIN);

    for (;;) {
	/* Flush any buffered output */
	prot_flush(nntp_out);
	if (backend_current) prot_flush(backend_current->out);

	/* Check for shutdown file */
	if (shutdown_file(buf, sizeof(buf)) ||
	    (nntp_userid &&
	     userdeny(nntp_userid, config_ident, buf, sizeof(buf)))) {
	    prot_printf(nntp_out, "400 %s\r\n", buf);
	    shut_down(0);
	}

	signals_poll();

	if (!proxy_check_input(protin, nntp_in, nntp_out,
			       backend_current ? backend_current->in : NULL,
			       NULL, 0)) {
	    /* No input from client */
	    continue;
	}

	if (group_state &&
	    config_getswitch(IMAPOPT_DISCONNECT_ON_VANISHED_MAILBOX)) {
	    if (group_state->mailbox->i.options & OPT_MAILBOX_DELETED) {
		/* Mailbox has been (re)moved */
		syslog(LOG_WARNING,
		       "Newsgroup %s has been (re)moved out from under client",
		       group_state->mailbox->name);
		prot_printf(nntp_out,
			    "400 Newsgroup has been (re)moved\r\n");
		shut_down(0);
	    }
	}

	/* Parse command name */
	c = getword(nntp_in, &cmd);
	if (c == EOF) {
	    if ((err = prot_error(nntp_in)) != NULL
		 && strcmp(err, PROT_EOF_STRING)) {
		syslog(LOG_WARNING, "%s, closing connection", err);
		prot_printf(nntp_out, "400 %s\r\n", err);
	    }
	    return;
	}
	if (!cmd.s[0]) {
	    prot_printf(nntp_out, "501 Empty command\r\n");
	    eatline(nntp_in, c);
	    continue;
	}
	if (Uislower(cmd.s[0]))
	    cmd.s[0] = toupper((unsigned char) cmd.s[0]);
	for (p = &cmd.s[1]; *p; p++) {
	    if (Uisupper(*p)) *p = tolower((unsigned char) *p);
	}

	/* Ihave/Takethis only allowed for feeders */
	if (!(nntp_capa & MODE_FEED) &&
	    strchr("IT", cmd.s[0])) goto noperm;

	/* Body/Date/Group/Newgroups/Newnews/Next/Over/Post/Xhdr/Xover/Xpat
	   only allowed for readers */
	if (!(nntp_capa & MODE_READ) &&
	    strchr("BDGNOPX", cmd.s[0])) goto noperm;

	/* Only Authinfo/Capabilities/Check/Head/Help/Ihave/List Active/
	   Mode/Quit/Starttls/Stat/Takethis allowed when not logged in */
	if (!nntp_authstate && !allowanonymous &&
	    !strchr("ACHILMQST", cmd.s[0])) goto nologin;

	/* In case a [LIST]GROUP fails or
	   a retrieval by msgid makes us switch groups */
	strcpy(curgroup, group_state ? group_state->mailbox->name : "");

	switch (cmd.s[0]) {
	case 'A':
	    if (!strcmp(cmd.s, "Authinfo")) {
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* subcommand */
		if (c == EOF) goto missingargs;

		lcase(arg1.s);

		if (!strcmp(arg1.s, "user") || !strcmp(arg1.s, "pass")) {
		    if (c != ' ') goto missingargs;
		    c = getuserpass(nntp_in, &arg2); /* user/pass */
		    if (c == EOF) goto missingargs;

		    if (c == '\r') c = prot_getc(nntp_in);
		    if (c != '\n') goto extraargs;

		    if (arg1.s[0] == 'u')
			cmd_authinfo_user(arg2.s);
		    else
			cmd_authinfo_pass(arg2.s);
		}
		else if (!strcmp(arg1.s, "sasl") || !strcmp(arg1.s, "generic")) {
		    arg2.len = arg3.len = 0;

		    /* mech name required for SASL but not GENERIC */
		    if ((arg1.s[0] == 's') && (c != ' ')) goto missingargs;

		    if (c == ' ') {
			c = getword(nntp_in, &arg2); /* mech name */
			if (c == EOF) goto missingargs;

			if (c == ' ') {
			    c = getword(nntp_in, &arg3); /* init response */
			    if (c == EOF) goto missingargs;
			}
		    }

		    if (c == '\r') c = prot_getc(nntp_in);
		    if (c != '\n') goto extraargs;

		    cmd_authinfo_sasl(arg1.s, arg2.len ? arg2.s : NULL,
				      arg3.len ? arg3.s : NULL);
		}
		else
		    prot_printf(nntp_out,
				"501 Unrecognized AUTHINFO command\r\n");
	    }
	    else if (!(nntp_capa & MODE_READ)) goto noperm;
	    else if (!nntp_authstate && !allowanonymous) goto nologin;
	    else if (!strcmp(cmd.s, "Article")) {
		char *msgid;

		mode = ARTICLE_ALL;

	      article:
		if (arg1.s) *arg1.s = 0;

		if (c == ' ') {
		    c = getword(nntp_in, &arg1); /* number/msgid (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if (parserange(arg1.s, &uid, NULL, &msgid, &be) != -1) {
		    if (be) {
			if (arg1.s && *arg1.s)
			    prot_printf(be->out, "%s %s\r\n", cmd.s, arg1.s);
			else
			    prot_printf(be->out, "%s\r\n", cmd.s);

			if (be != backend_current) {
			    r = read_response(be, 0, &result);
			    if (r) goto noopengroup;

			    prot_printf(nntp_out, "%s", result);
			    if (!strncmp(result, "22", 2) &&
				mode != ARTICLE_STAT) {
				pipe_to_end_of_response(be, 0);
			    }
			}
		    }
		    else
			cmd_article(mode, msgid, uid);
		}

		if (msgid) goto prevgroup;
	    }
	    else goto badcmd;
	    break;

	case 'B':
	    if (!strcmp(cmd.s, "Body")) {
		mode = ARTICLE_BODY;
		goto article;
	    }
	    else goto badcmd;
	    break;

	case 'C':
	    if (!strcmp(cmd.s, "Capabilities")) {
		arg1.len = 0;

		if (c == ' ') {
		    c = getword(nntp_in, &arg1); /* keyword (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_capabilities(arg1.s);
	    }
	    else if (!(nntp_capa & MODE_FEED)) goto noperm;
	    else if (!strcmp(cmd.s, "Check")) {
		mode = POST_CHECK;
		goto ihave;
	    }
	    else goto badcmd;
	    break;

	case 'D':
	    if (!strcmp(cmd.s, "Date")) {
		time_t now = time(NULL);
		struct tm *my_tm = gmtime(&now);
		char buf[15];

		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", my_tm);
		prot_printf(nntp_out, "111 %s\r\n", buf);
	    }
	    else goto badcmd;
	    break;

	case 'G':
	    if (!strcmp(cmd.s, "Group")) {
		arg2.len = 0; /* GROUP command (no range) */

	      group:
#define LISTGROUP (arg2.len)

		if (!LISTGROUP && c != ' ') goto missingargs;
		if (c == ' ') {
		    c = getword(nntp_in, &arg1); /* group */
		    if (c == EOF) goto missingargs;
		}
		if (LISTGROUP && c == ' ') {
		    c = getword(nntp_in, &arg2); /* range (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		be = backend_current;
		if (arg1.len &&
		    (!is_newsgroup(arg1.s) ||
		     (r = open_group(arg1.s, 1, &be, NULL)))) goto nogroup;
		else if (be) {
		    prot_printf(be->out, "%s", cmd.s);
		    if (arg1.len) {
			prot_printf(be->out, " %s", arg1.s);
			  if (LISTGROUP) prot_printf(be->out, " %s", arg2.s);
		    }
		    prot_printf(be->out, "\r\n");

		    r = read_response(be, 0, &result);
		    if (r) goto nogroup;

		    prot_printf(nntp_out, "%s", result);

		    if (!strncmp(result, "211", 3)) {
			if (LISTGROUP) pipe_to_end_of_response(be, 0);

			if (backend_current && backend_current != be) {
			    /* remove backend_current from the protgroup */
			    protgroup_delete(protin, backend_current->in);
			}
			backend_current = be;

			/* add backend_current to the protgroup */
			protgroup_insert(protin, backend_current->in);
		    }
		}
		else if (!group_state) goto noopengroup;
		else if (LISTGROUP &&
			 parserange(arg2.s, &uid, &last, NULL, NULL) != 0) {
		    /* parserange() will handle error code -- do nothing */
		}
		else {
		    if (backend_current) {
			/* remove backend_current from the protgroup */
			protgroup_delete(protin, backend_current->in);
		    }
		    backend_current = NULL;

		    nntp_exists = group_state->exists;
		    nntp_current = nntp_exists > 0;

		    prot_printf(nntp_out, "211 %u %lu %lu %s\r\n",
				nntp_exists,
				nntp_exists ? index_getuid(group_state, 1) :
				group_state->last_uid+1,
				nntp_exists ? index_getuid(group_state, nntp_exists) :
				group_state->last_uid,
				group_state->mailbox->name + strlen(newsprefix));

		    if (LISTGROUP) {
			int msgno, last_msgno;

			msgno = index_finduid(group_state, uid);
			if (!msgno || index_getuid(group_state, msgno) != uid) {
			    msgno++;
			}
			last_msgno = index_finduid(group_state, last);

			for (; msgno <= last_msgno; msgno++) {
			    prot_printf(nntp_out, "%u\r\n",
					index_getuid(group_state, msgno));
			}
			prot_printf(nntp_out, ".\r\n");
		    }
		}
#undef LISTGROUP
	    }
	    else goto badcmd;
	    break;

	case 'H':
	    if (!strcmp(cmd.s, "Head")) {
		mode = ARTICLE_HEAD;
		goto article;
	    }
	    else if (!strcmp(cmd.s, "Help")) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_help();
	    }
	    else if (!(nntp_capa & MODE_READ)) goto noperm;
	    else if (!nntp_authstate && !allowanonymous) goto nologin;
	    else if (!strcmp(cmd.s, "Hdr")) {
		char *msgid;

	      hdr:
		if (arg2.s) *arg2.s = 0;

		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* header */
		if (c == EOF) goto missingargs;
		if (c == ' ') {
		    c = getword(nntp_in, &arg2); /* range (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if (parserange(arg2.s, &uid, &last, &msgid, &be) != -1) {
		    if (be) {
			if (arg2.s && *arg2.s)
			    prot_printf(be->out, "%s %s %s\r\n",
					cmd.s, arg1.s, arg2.s);
			else
			    prot_printf(be->out, "%s %s\r\n", cmd.s, arg1.s);

			if (be != backend_current) {
			    r = read_response(be, 0, &result);
			    if (r) goto noopengroup;

			    prot_printf(nntp_out, "%s", result);
			    if (!strncmp(result, "22", 2)) { /* 221 or 225 */
				pipe_to_end_of_response(be, 0);
			    }
			}
		    }
		    else
			cmd_hdr(cmd.s, arg1.s, NULL, msgid, uid, last);
		}

		if (msgid) goto prevgroup;
	    }
	    else goto badcmd;
	    break;

	case 'I':
	    if (!strcmp(cmd.s, "Ihave")) {
		mode = POST_IHAVE;

	      ihave:
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* msgid */
		if (c == EOF) goto missingargs;
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_post(arg1.s, mode);
	    }
	    else goto badcmd;
	    break;

	case 'L':
	    if (!strcmp(cmd.s, "List")) {
		arg1.len = arg2.len = 0;
		if (c == ' ') {
		    c = getword(nntp_in, &arg1); /* subcommand (optional) */
		    if (c == EOF) goto missingargs;
		    if (c == ' ') {
			c = getword(nntp_in, &arg2); /* argument (optional) */
			if (c == EOF) goto missingargs;
		    }
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_list(arg1.len ? arg1.s : NULL, arg2.len ? arg2.s : NULL);

		goto prevgroup;  /* In case we did LIST [ACTIVE] */
	    }
	    else if (!(nntp_capa & MODE_READ)) goto noperm;
	    else if (!nntp_authstate && !allowanonymous) goto nologin;
	    else if (!strcmp(cmd.s, "Last")) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if (backend_current) {
		    prot_printf(backend_current->out, "LAST\r\n");
		}
		else if (!group_state) goto noopengroup;
		else if (!nntp_current) goto nocurrent;
		else if (nntp_current == 1) {
		    prot_printf(nntp_out,
				"422 No previous article in this group\r\n");
		}
		else {
		    char *msgid = index_get_msgid(group_state, --nntp_current);

		    prot_printf(nntp_out, "223 %u %s\r\n",
				index_getuid(group_state, nntp_current),
				msgid ? msgid : "<0>");

		    if (msgid) free(msgid);
		}
	    }
	    else if (!strcmp(cmd.s, "Listgroup")) {
		arg1.len = 0;   	   /* group is optional */
		buf_setcstr(&arg2, "1-");  /* default range is all */
		buf_cstring(&arg2);	   /* appends a '\0' */
		goto group;
	    }
	    else goto badcmd;
	    break;

	case 'M':
	    if (!strcmp(cmd.s, "Mode")) {
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* mode */
		if (c == EOF) goto missingargs;
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_mode(arg1.s);
	    }
	    else goto badcmd;
	    break;

	case 'N':
	    if (!strcmp(cmd.s, "Newgroups")) {
		time_t tstamp;

		arg3.len = 0;
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* date */
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg2); /* time */
		if (c == EOF) goto missingargs;
		if (c == ' ') {
		    c = getword(nntp_in, &arg3); /* "GMT" (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if ((tstamp = parse_datetime(arg1.s, arg2.s,
					     arg3.len ? arg3.s : NULL)) < 0)
		    goto baddatetime;

		cmd_newgroups(tstamp);
	    }
	    else if (!strcmp(cmd.s, "Newnews")) {
		time_t tstamp;

		if (!config_getswitch(IMAPOPT_ALLOWNEWNEWS))
		    goto cmddisabled;

		arg4.len = 0;
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* wildmat */
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg2); /* date */
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg3); /* time */
		if (c == EOF) goto missingargs;
		if (c == ' ') {
		    c = getword(nntp_in, &arg4); /* "GMT" (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if ((tstamp = parse_datetime(arg2.s, arg3.s,
					     arg4.len ? arg4.s : NULL)) < 0)
		    goto baddatetime;

		cmd_newnews(arg1.s, tstamp);
	    }
	    else if (!strcmp(cmd.s, "Next")) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if (backend_current) {
		    prot_printf(backend_current->out, "NEXT\r\n");
		}
		else if (!group_state) goto noopengroup;
		else if (!nntp_current) goto nocurrent;
		else if (nntp_current == nntp_exists) {
		    prot_printf(nntp_out,
				"421 No next article in this group\r\n");
		}
		else {
		    char *msgid = index_get_msgid(group_state, ++nntp_current);

		    prot_printf(nntp_out, "223 %u %s\r\n",
				index_getuid(group_state, nntp_current),
				msgid ? msgid : "<0>");

		    if (msgid) free(msgid);
		}
	    }
	    else goto badcmd;
	    break;

	case 'O':
	    if (!strcmp(cmd.s, "Over")) {
		char *msgid;

	      over:
		if (arg1.s) *arg1.s = 0;

		if (c == ' ') {
		    c = getword(nntp_in, &arg1); /* range/msgid (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		msgid = NULL;
		if (parserange(arg1.s, &uid, &last,
			       /* XOVER doesn't accept message-id */
			       (cmd.s[0] == 'X' ? NULL : &msgid), &be) != -1) {
		    if (be) {
			if (arg1.s && *arg1.s)
			    prot_printf(be->out, "%s %s\r\n", cmd.s, arg1.s);
			else
			    prot_printf(be->out, "%s\r\n", cmd.s);

			if (be != backend_current) {
			    r = read_response(be, 0, &result);
			    if (r) goto noopengroup;

			    prot_printf(nntp_out, "%s", result);
			    if (!strncmp(result, "224", 3)) {
				pipe_to_end_of_response(be, 0);
			    }
			}
		    }
		    else
			cmd_over(msgid, uid, last);
		}

		if (msgid) goto prevgroup;
	    }
	    else goto badcmd;
	    break;

	case 'P':
	    if (!strcmp(cmd.s, "Post")) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_post(NULL, POST_POST);
	    }
	    else goto badcmd;
	    break;

	case 'Q':
	    if (!strcmp(cmd.s, "Quit")) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		prot_printf(nntp_out, "205 Connection closing\r\n");
		return;
	    }
	    else goto badcmd;
	    break;

	case 'S':
	    if (!strcmp(cmd.s, "Starttls") && tls_enabled()) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		/* XXX  discard any input pipelined after STARTTLS */
		prot_flush(nntp_in);

		cmd_starttls(0);
	    }
	    else if (!strcmp(cmd.s, "Stat")) {
		mode = ARTICLE_STAT;
		goto article;
	    }
	    else if (!nntp_authstate && !allowanonymous) goto nologin;
	    else if (!strcmp(cmd.s, "Slave")) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		prot_printf(nntp_out, "202 Slave status noted\r\n");
	    }
	    else goto badcmd;
	    break;

	case 'T':
	    if (!strcmp(cmd.s, "Takethis")) {
		mode = POST_TAKETHIS;
		goto ihave;
	    }
	    else goto badcmd;
	    break;

	case 'X':
	    if (!strcmp(cmd.s, "Xhdr")) {
		goto hdr;
	    }
	    else if (!strcmp(cmd.s, "Xover")) {
		goto over;
	    }
	    else if (!strcmp(cmd.s, "Xpat")) {
		char *msgid;

		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* header */
		if (c != ' ') goto missingargs;

		/* gobble extra whitespace (hack for Mozilla) */
		while ((c = prot_getc(nntp_in)) == ' ');
		prot_ungetc(c, nntp_in);

		c = getword(nntp_in, &arg2); /* range */
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg3); /* wildmat */
		if (c == EOF) goto missingargs;

		/* XXX per RFC 2980, we can have multiple patterns */

		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if (parserange(arg2.s, &uid, &last, &msgid, &be) != -1) {
		    if (be) {
			prot_printf(be->out, "%s %s %s %s\r\n",
				    cmd.s, arg1.s, arg2.s, arg3.s);

			if (be != backend_current) {
			    r = read_response(be, 0, &result);
			    if (r) goto noopengroup;

			    prot_printf(nntp_out, "%s", result);
			    if (!strncmp(result, "221", 3)) {
				pipe_to_end_of_response(be, 0);
			    }
			}
		    }
		    else
			cmd_hdr(cmd.s, arg1.s, arg3.s, msgid, uid, last);
		}

		if (msgid) goto prevgroup;
	    }
	    else goto badcmd;
	    break;

	default:
	  badcmd:
	    prot_printf(nntp_out, "500 Unrecognized command\r\n");
	    eatline(nntp_in, c);
	}

	continue;

      noperm:
	prot_printf(nntp_out, "502 Permission denied\r\n");
	eatline(nntp_in, c);
	continue;

      nologin:
	prot_printf(nntp_out, "480 Authentication required\r\n");
	eatline(nntp_in, c);
	continue;

      cmddisabled:
	prot_printf(nntp_out, "503 \"%s\" disabled\r\n", cmd.s);
	eatline(nntp_in, c);
	continue;

      extraargs:
	prot_printf(nntp_out, "501 Unexpected extra argument\r\n");
	eatline(nntp_in, c);
	continue;

      missingargs:
	prot_printf(nntp_out, "501 Missing argument\r\n");
	eatline(nntp_in, c);
	continue;

      baddatetime:
	prot_printf(nntp_out, "501 Bad date/time\r\n");
	continue;

      nogroup:
	prot_printf(nntp_out, "411 No such newsgroup (%s)\r\n",
		    error_message(r));

      prevgroup:
	/* Return to previously selected group */
	if (*curgroup &&
	    (!group_state || strcmp(curgroup, group_state->mailbox->name))) {
	    open_group(curgroup, 1, NULL, NULL);
	}

	continue;

      noopengroup:
	prot_printf(nntp_out, "412 No newsgroup selected\r\n");
	continue;

      nocurrent:
	prot_printf(nntp_out, "420 Current article number is invalid\r\n");
	continue;
    }
}


int do_active(char *name, void *rock)
{
    struct list_rock *lrock = (struct list_rock *) rock;
    int r, postable;
    struct backend *be;

    /* open the group */
    r = open_group(name, 1, &be, &postable);
    if (r) {
	/* can't open group, skip it */
    }
    else if (be) {
	if (!hash_lookup(be->hostname, &lrock->server_table)) {
	    /* add this server to our table */
	    hash_insert(be->hostname, (void *)0xDEADBEEF, &lrock->server_table);
	}
    }
    else {
	prot_printf(nntp_out, "%s %u %u %c\r\n", name,
		    group_state->exists ? index_getuid(group_state, group_state->exists) :
		    group_state->mailbox->i.last_uid,
		    group_state->exists ? index_getuid(group_state, 1) :
		    group_state->mailbox->i.last_uid+1,
		    postable ? 'y' : 'n');
	index_close(&group_state);
    }

    return 0;
}


httpClientNoticeRequest(HTTPRequestPtr request, int novalidate)
{
    HTTPConnectionPtr connection = request->connection;
    ObjectPtr object = request->object;
    int serveNow = (request == connection->request);
    int validate = 0;
    int conditional = 0;
    int local, haveData;
    int rc;

    assert(!request->chandler);

    if(request->error_code) {
        if((request->flags & REQUEST_FORCE_ERROR) || REQUEST_SIDE(request) ||
           request->object == NULL ||
           (request->object->flags & OBJECT_LOCAL) ||
           (request->object->flags & OBJECT_ABORTED) ||
           (relaxTransparency < 1 && !proxyOffline)) {
            if(serveNow) {
                connection->flags |= CONN_WRITER;
                return httpClientRawErrorHeaders(connection,
                                                 request->error_code,
                                                 retainAtom(request->
                                                            error_message),
                                                 0, request->error_headers);
            } else {
                return 1;
            }
        }
    }

    if(REQUEST_SIDE(request)) {
        assert(!(request->flags & REQUEST_REQUESTED));
        if(serveNow) {
            assert(!request->chandler);
            request->chandler =
                conditionWait(&request->object->condition,
                              httpClientGetHandler,
                              sizeof(request), &request);
            if(request->chandler == NULL) {
                do_log(L_ERROR, "Couldn't register condition handler.\n");
                connection->flags |= CONN_WRITER;
                httpClientRawError(connection, 500,
                                   internAtom("Couldn't register "
                                              "condition handler"),
                                   0);
                return 1;
            }
            connection->flags |= CONN_WRITER;
            rc = object->request(request->object,
                                 request->method,
                                 request->from, request->to,
                                 request,
                                 request->object->request_closure);
        }
        return 1;
    }

    local = urlIsLocal(object->key, object->key_size);
    objectFillFromDisk(object, request->from,
                       request->method == METHOD_HEAD ? 0 : 1);

    /* The spec doesn't strictly forbid 206 for non-200 instances, but doing
       that breaks some client software. */
    if(object->code && object->code != 200) {
        request->from = 0;
        request->to = -1;
    }

    if(request->condition && request->condition->ifrange) {
        if(!object->etag ||
           strcmp(object->etag, request->condition->ifrange) != 0) {
            request->from = 0;
            request->to = -1;
        }
    }

    if(object->flags & OBJECT_DYNAMIC) {
        request->from = 0;
        request->to = -1;
    }

    if(request->method == METHOD_HEAD ||
       request->object->code == 204 ||
       request->object->code < 200)
        haveData = !(request->object->flags & OBJECT_INITIAL);
    else
        haveData =
            (request->object->length >= 0 &&
             request->object->length <= request->from) ||
            (objectHoleSize(request->object, request->from) == 0);

    if(request->flags & REQUEST_REQUESTED)
        validate = 0;
    else if(novalidate || (!local && proxyOffline))
        validate = 0;
    else if(local)
        validate =
            objectMustRevalidate(request->object, &request->cache_control);
    else if(request->cache_control.flags & CACHE_ONLY_IF_CACHED)
        validate = 0;
    else if((request->object->flags & OBJECT_FAILED) &&
            !(object->flags & OBJECT_INPROGRESS) &&
            !relaxTransparency)
        validate = 1;
    else if(request->method != METHOD_HEAD &&
            !objectHasData(object, request->from, request->to) &&
            !(object->flags & OBJECT_INPROGRESS))
        validate = 1;
    else if(objectMustRevalidate((relaxTransparency <= 1 ?
                                  request->object : NULL),
                                 &request->cache_control))
        validate = 1;
    else
        validate = 0;

    if(request->cache_control.flags & CACHE_ONLY_IF_CACHED) {
        validate = 0;
        if(!haveData) {
            if(serveNow) {
                connection->flags |= CONN_WRITER;
                return httpClientRawError(connection, 504,
                                          internAtom("Object not in cache"),
                                          0);
            } else
                return 1;
        }
    }

    if(!(request->object->flags & OBJECT_VALIDATING) &&
       ((!validate && haveData) ||
        (request->object->flags & OBJECT_FAILED))) {
        if(serveNow) {
            connection->flags |= CONN_WRITER;
            lockChunk(request->object, request->from / CHUNK_SIZE);
            return httpServeObject(connection);
        } else {
            return 1;
        }
    }

    if((request->flags & REQUEST_REQUESTED) &&
       !(request->object->flags & OBJECT_INPROGRESS)) {
        /* This can happen either because the server side ran out of
           memory, or because it is using HEAD validation.  We mark
           the object to be fetched again. */
        request->flags &= ~REQUEST_REQUESTED;
    }

    if(serveNow) {
        connection->flags |= CONN_WRITER;
        if(!local && proxyOffline)
            return httpClientRawError(connection, 502,
                                      internAtom("Disconnected operation "
                                                 "and object not in cache"),
                                      0);
        request->chandler =
            conditionWait(&request->object->condition, httpClientGetHandler,
                          sizeof(request), &request);
        if(request->chandler == NULL) {
            do_log(L_ERROR, "Couldn't register condition handler.\n");
            return httpClientRawError(connection, 503,
                                      internAtom("Couldn't register "
                                                 "condition handler"), 0);
        }
    }

    if(request->object->flags & OBJECT_VALIDATING)
        return 1;

    conditional = (haveData && request->method == METHOD_GET);
    if(!mindlesslyCacheVary && (request->object->cache_control & CACHE_VARY))
        conditional = conditional && (request->object->etag != NULL);

    conditional =
        conditional && !(request->object->cache_control & CACHE_MISMATCH);

    if(!(request->object->flags & OBJECT_INPROGRESS))
        request->object->flags |= OBJECT_VALIDATING;
    rc = request->object->request(request->object,
                                  conditional ? METHOD_CONDITIONAL_GET :
                                  request->method,
                                  request->from, request->to, request,
                                  request->object->request_closure);
    if(rc < 0) {
        if(request->chandler)
            unregisterConditionHandler(request->chandler);
        request->chandler = NULL;
        request->object->flags &= ~OBJECT_VALIDATING;
        request->object->flags |= OBJECT_FAILED;
        if(request->error_message)
            releaseAtom(request->error_message);
        request->error_code = 503;
        request->error_message = internAtom("Couldn't schedule get");
    }
    return 1;
}


delayedHttpClientContinue(HTTPConnectionPtr connection)
{
    TimeEventHandlerPtr event;
    event = scheduleTimeEvent(-1, httpClientContinueDelayed,
                              sizeof(connection), &connection);
    if(!event)
        return -1;
    return 1;
}