#include "mcdp.h"
#include "getkey.h"
#include "categories.h"
#include "getfilesize.h"

/**
 * Copyright (C) 2001-2006 Tino Reichardt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2, as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/**
 * todo
 * - add some error checking @ the buffer_putsa() functions!
 * - submitting via email
 * - show server and email before sending ...
 */

/* libowfat */
#include "dns.h"
#include "byte.h"
#include "socket.h"
#include "uint16.h"
#include "open.h"

#define MCDP_CDDBP_LEVEL 5


/**
 * for various remote methods
 */
static int  db_connect(stralloc *fqdn, uint16 port);
static void db_logline(int direction, stralloc *line);
#define D_NOTE       0
#define D_CDDB_SEND  1
#define D_CDDB_RETR  2
#define D_CDDB_FAIL  3
static void db_press_key2continue(void);
static int  db_savefile(void);
static void db_ask_matches(stralloc *categ, stralloc *discid);
static void db_ask_categories(stralloc *categ);
static int db_ask_submitmode(void);

/**
 * cddbp protocol functions
 */
static int  db_cddbp_connect(void);
static void db_cddbp_sendcmd(stralloc *cmd);
static int  db_cddbp_getresp(stralloc *resp);
static void db_cddbp_disconnect(void);
static int  db_cddbp_write_sendfile(void);


/**
 * http and http_proxy protocol functions
 */
static int  db_http_connect(char *server);
static void db_http_sendcmd(stralloc *cmd);
static int  db_http_getresp(stralloc *resp);
static int  db_proxy_connect(void);
static void db_proxy_addauth(stralloc *request);


/**
 * internal variables
 */
static int  cddb_fd;
static char cddb_iBUF[BUFSIZE]; buffer cddb_in;
static char cddb_oBUF[BUFSIZE]; buffer cddb_out;

static const char *cddbdata="[... TRACK DATA ...]";
static const char *dots=" ...";

/**
 * db_connect - setup the connection the the server
 * - resolves ip of some server
 * - creates a socket and connects to the given port
 *
 * @param fqdn  the full qualified domain name to connect to
 * @param port  the port number to connect to
 * @return      zero on succuess, -1 on failure
 */
static int db_connect(stralloc *fqdn, uint16 port)
{
	stralloc ip, sa;
	int r;

	/* initialize the strallocs */
	stralloc_init(&ip);
	stralloc_init(&sa);

	/* resolve the ip of the server */
	if (!stralloc_copys(&sa, lang_msg(MSG_DB_RESOLVING))) die_nomem();
	if (!stralloc_cat(&sa, fqdn)) die_nomem();
	if (!stralloc_cats(&sa, dots)) die_nomem();
	db_logline(D_NOTE, &sa);

	r = dns_ip6(&ip, fqdn);
	if (ip.len == 0) {
		if (!stralloc_copys(&sa, lang_msg(MSG_DB_RESOLVING_FAILED))) die_nomem();
		if (!stralloc_cat(&sa, fqdn)) die_nomem();
		if (!stralloc_cats(&sa, ":")) die_nomem();
		if (!stralloc_cat_uint(&sa, port)) die_nomem();
		if (!stralloc_cats(&sa, dots)) die_nomem();
		db_logline(D_CDDB_FAIL, &sa);
		stralloc_free(&ip);
		stralloc_free(&sa);
		return -1;
	}

	if (!stralloc_copys(&sa, lang_msg(MSG_DB_CONNECTING))) die_nomem();
	if (!stralloc_cat_ip6(&sa, ip.s)) die_nomem();
	if (!stralloc_cats(&sa, ":")) die_nomem();
	if (!stralloc_cat_uint(&sa, port)) die_nomem();
	if (!stralloc_cats(&sa, dots)) die_nomem();
	db_logline(D_NOTE, &sa);

	/* create socket and connect to it */
	r = cddb_fd = socket_tcp6();
	if (r == -1) {
		stralloc_free(&ip);
		stralloc_free(&sa);
		return -1;
	}

	r = socket_connect6(cddb_fd, ip.s, port, 0);
	if (r == -1) {
		if (!stralloc_copys(&sa, lang_msg(MSG_DB_CONNECTING_FAILED))) die_nomem();
		if (!stralloc_cat(&sa, fqdn)) die_nomem();
		if (!stralloc_cats(&sa, ":")) die_nomem();
		if (!stralloc_cat_uint(&sa, port)) die_nomem();
		if (!stralloc_cats(&sa, dots)) die_nomem();
		db_logline(D_CDDB_FAIL, &sa);
		stralloc_free(&ip);
		stralloc_free(&sa);
		return -1;
	}

	if (!io_fd(cddb_fd)) {
		stralloc_free(&ip);
		stralloc_free(&sa);
		return -1;
	}

	buffer_init(&cddb_in, (int(*)())read, cddb_fd, cddb_iBUF,
		sizeof cddb_iBUF);
	buffer_init(&cddb_out, (int(*)())write, cddb_fd, cddb_oBUF,
		sizeof cddb_oBUF);

	stralloc_free(&ip);
	stralloc_free(&sa);

	return 0;
}

/**
 * db_logline - show the user what's going on
 *
 * @param direction  type of information
 * @param line       the content for the right side of the main window
 * @return           nothing
 */
static void db_logline(int direction, stralloc *line)
{
	stralloc sa, left, right;
	char *s, *t;
	int l=0, i;
	
	stralloc_init(&sa);
	stralloc_init(&left);
	stralloc_init(&right);
	switch (direction) {
	case D_NOTE:
		/* empty, no special information here! */
		l=c_note.len;
		if (!stralloc_cat(&left, &c_note)) die_nomem();
		break;
	case D_CDDB_RETR:
		l=c_cddb_retr.len;
		if (!stralloc_cat(&left, &c_cddb_retr)) die_nomem();
		if (!stralloc_cats(&left, "cddb: ")) die_nomem();
		break;
	case D_CDDB_SEND:
		l=c_cddb_send.len;
		if (!stralloc_cat(&left, &c_cddb_send)) die_nomem();
		if (!stralloc_cats(&left, "mcdp: ")) die_nomem();
		break;
	case D_CDDB_FAIL:
		l=c_cddb_fail.len;
		if (!stralloc_cat(&left, &c_cddb_fail)) die_nomem();
		if (!stralloc_cats(&left, "failure: ")) die_nomem();
		break;
	}

	/* replace all '\r\n' with '\0' and show them later as extra lines */
	if (!stralloc_copy(&sa, line)) die_nomem();
	sa.s[sa.len]=0;

	for (s=t=sa.s; *t; t++, s++) {
		if (*t == '\r') {
			if (*++t == '\n') { sa.len--; *s=0; }
		} else *s=*t; /* no '\n' */
	} *s=0; /* terminate new end */

	for (i=0, s=sa.s; i < (int)sa.len; i+=str_len(s+i)+1) {
		if (i) {
			if (!stralloc_copys(&left, "")) die_nomem();
			l=0;
		}
		if (!stralloc_copys(&right, sa.s+i)) die_nomem();
		show_main_colorS(l, &left, 0, &right);
	}

	flush();
	stralloc_free(&sa);
	stralloc_free(&left);
	stralloc_free(&right);

	return;
}

/**
 * db_press_key2continue - wait for some userinput
 *
 * @return nothing
 */
static void db_press_key2continue(void)
{
	stralloc sa;
	
	stralloc_init(&sa);
	if (!stralloc_copys(&sa, lang_msg(MSG_CONTINUE))) die_nomem();
	db_logline(D_NOTE, &sa);
	getkey(lang_msg(MSG_CONTINUE_KEY));
	stralloc_free(&sa);

	return;
}

/**
 * some notes about the cddb protocol
 *
 * <<< code Initial response from server ...
 * 200      OK, read/write allowed
 * 201      OK, read only
 * 432      No connections allowed: permission denied
 * 433      No connections allowed: X users allowed, Y currently active
 * 434      No connections allowed: system load too high
 *
 * >>> cddb hello username hostname clientname version
 * <<< code hello and welcome username@hostname running clientname version
 * 200      Handshake successful
 * 402      Already shook hands
 * 431      Handshake not successful, closing connection
 *
 * >>> proto level [we support version 5]
 * <<< code message
 * 200      CDDB protocol level: current cur_level, supported supp_level
 * 201      OK, protocol version now: cur_level
 * 501      Illegal protocol level.
 * 502      Protocol level already cur_level.
 *
 * >>> cddb query discid ntrks off1 off2 ... total-secs
 * <<< code message
 * 200      Found exact match
 * 202      No match found
 * 210      Found exact matches, list follows (until terminating marker)
 * 211      Found inexact matches, list follows (until terminating marker)
 * 403      Database entry is corrupt
 * 409      No handshake
 *
 * >>> cddb read categ discid
 * <<< code message
 * 210      OK, CDDB database entry follows (until terminating marker)
 * 401      Specified CDDB entry not found.
 * 402      Server error.
 * 403      Database entry is corrupt.
 * 409      No handshake.
 *
 * >>> cddb write categ discid
 * <<< code message
 * 320      OK, input CDDB data (until terminating marker)
 * 401      Permission denied.
 * 402      Server file system full/file access failed.
 * 409      No handshake.
 * >>>      Client CDDB data, (until terminating marker)
 * <<< code message
 * 200      CDDB entry accepted
 * 501      Entry rejected: reason for rejection.
 *
 * >>> quit
 * <<< code message
 * 230      Closing connection.  Goodbye.
 * 530      Server error.
 *
 * /TR 2006-03-08
 *
 * -> we support read/write via cddbp! <-
 *
 */


/**
 * db_cddbp_connect - setup the connection the the server
 * - resolves ip of the cddb server and connect to it
 *
 * @return zero on succuess, -1 on failure
 */
static int db_cddbp_connect(void)
{
	unsigned int port;
	stralloc fqdn;
	long l;
	int r;

	stralloc_init(&fqdn);

	l = str_rchr(mcdp_env_cddb_server, ':');
	if (!mcdp_env_cddb_server[l]) {
		port=(unsigned int)DEFAULT_CDDBPORT;
		if (!stralloc_copys(&fqdn, mcdp_env_cddb_server))
			die_nomem();
	} else {
		scan_uint(mcdp_env_cddb_server+l+1, &port);
		if (!stralloc_copyb(&fqdn, mcdp_env_cddb_server, l))
			die_nomem();
	}
	
	r = db_connect(&fqdn, port);
	stralloc_free(&fqdn);

	return r;
}

/**
 * db_cddbp_disconnect - disconnect from cddb server
 * - close all buffers and the socket filedescriptor
 *
 * @return nothing
 */
static void db_cddbp_disconnect(void)
{
	stralloc sa;

	stralloc_init(&sa);
	if (!stralloc_copys(&sa, "quit")) die_nomem();
	db_cddbp_sendcmd(&sa);
	(void)db_cddbp_getresp(&sa);
	stralloc_free(&sa);

	db_press_key2continue();

	cddb_in.fd=-1;  buffer_close(&cddb_in);
	cddb_out.fd=-1; buffer_close(&cddb_out);
	io_close(cddb_fd);

	return;
}


/**
 * db_cddbp_sendcmd - send a new command to the cddb server
 *
 * @param cmd  contains to command to send without "\r\n"
 * @return     nothing
 */
static void db_cddbp_sendcmd(stralloc *cmd)
{
	db_logline(D_CDDB_SEND, cmd);
	if (!stralloc_cats(cmd, "\r\n")) die_nomem();
	io_wantwrite(cddb_fd); io_wait(); io_dontwantwrite(cddb_fd);
	buffer_putsaflush(&cddb_out, cmd);

	return;
}

/**
 * db_cddbp_getresp - read a response from cddb server
 *
 * @param resp  the first line of the response is written tp this stralloc
 * @return      the cddb code of the servers reply
 */
static int db_cddbp_getresp(stralloc *resp)
{
	int r;

	if (!stralloc_copys(resp, "")) die_nomem();
	io_wantread(cddb_fd); io_wait(); io_dontwantread(cddb_fd);
	for (;;) {
		r = buffer_getline_sa(&cddb_in, resp);
		if (r <= 0) {
			/* EOF, wait again */
			io_wantread(cddb_fd);
			io_wait();
			io_dontwantread(cddb_fd);
		}
		if (r == 1) break; /* we have a full line */
	}

	/* get cddb code from server */
	r = resp->s[0] - '0';
	r = r * 10 + (resp->s[1] - '0');
	r = r * 10 + (resp->s[2] - '0');
	stralloc_chomp(resp);
	db_logline(D_CDDB_RETR, resp);

	return r;
}

/**
 * db_read_writefile - write cddb file
 * - read cddb file from server and write it to disk
 *
 * @return nothing
 */
static int db_savefile(void)
{
	stralloc sa, file;
	int fd, r;

	fd = open_trunc(cd->cddbfile.s);
	if (fd == -1) return -1;

	stralloc_init(&sa);
	stralloc_init(&file);
	for (;;) {
		r = buffer_getnewline_sa(&cddb_in, &sa);
		if (r == 0) {
			/* EOF, wait again */
			io_wantread(cddb_fd);
			io_wait();
			io_dontwantread(cddb_fd);
		}

		 /* look for ".\r\n" */
		if (sa.len == 3)
		if (sa.s[0] == '.')
		if (sa.s[1] == '\r')
		if (sa.s[2] == '\n') break;

		/* "xyz\r\n" -> "xyz\n" */
		stralloc_chomp(&sa);
		if (!stralloc_cat(&file, &sa)) die_nomem();
		if (!stralloc_cats(&file, "\n")) die_nomem();
	}

	if (!stralloc_copys(&sa, cddbdata)) die_nomem();
	db_logline(D_NOTE, &sa);

	/* write it to file */
	if (write(fd, file.s, file.len) != (int)file.len) return -1;
	if (close(fd) != 0) return -1;

	stralloc_free(&sa);
	stralloc_free(&file);

	return 0;
}

/**
 * db_read_matches - asks the user what to do
 * - read all found matches from buffer
 * - show the user the list
 * - writes the selected match to the two stralloc's
 *
 * @param categ   selected category
 * @param discid  selected discid
 * @return        nothing
 */
static void db_ask_matches(stralloc *categ, stralloc *discid)
{
	stralloc line, matches, left, right, numbers;
	unsigned int i=0;
	char *x;
	int r;

	stralloc_init(&line);
	stralloc_init(&matches);
	stralloc_init(&left);
	stralloc_init(&right);
	stralloc_init(&numbers);
	for (;;) {
		r = buffer_getnewline_sa(&cddb_in, &line);
		if (r <= 0) {
			/* EOF, wait again */
			io_wantread(cddb_fd);
			io_wait();
			io_dontwantread(cddb_fd);
		}

		 /* look for ".\r\n" */
		if (line.len == 3)
		if (line.s[0] == '.')
		if (line.s[1] == '\r')
		if (line.s[2] == '\n') break;

		/* "xyz\r\n" -> "xyz\n" */
		stralloc_chomp(&line); i++;
		if (!stralloc_cat(&matches, &line)) die_nomem();
		if (!stralloc_0(&matches)) die_nomem();

		if (!stralloc_copys(&left, "match:")) die_nomem();
		if (!stralloc_copys(&right, "[")) die_nomem();
		if (!stralloc_cat_uint(&right, i)) die_nomem();
		if (!stralloc_cats(&right, "] ")) die_nomem();
		if (!stralloc_cat(&right, &line)) die_nomem();

		/* xxx: we can only save one-digit numbers! */
		if (!stralloc_cat_uint(&numbers, i)) die_nomem();
		show_main(&left, &right);
	}

	flush();
	numbers.s[numbers.len]=0;
	r = getkey(numbers.s);
	for (i=0, x=matches.s; (int)i != r; x+=str_len(x)+1, i++);

	r = str_chr(x, ' ');
	if (!stralloc_copyb(categ, x, r)) die_nomem();

	x += r+1;
	r = str_chr(x, ' ');
	if (!stralloc_copyb(discid, x, r)) die_nomem();

	stralloc_free(&line);
	stralloc_free(&matches);
	stralloc_free(&left);
	stralloc_free(&right);
	stralloc_free(&numbers);

	return;
}

/**
 * db_ask_categories - ask the user for a category
 *
 * @param categ  this initialized stralloc gets the selected category
 * @return       nothing
 */
static void db_ask_categories(stralloc *categ)
{
	stralloc sa, right;
	int r;

	stralloc_init(&sa);
	stralloc_init(&right);

	/* ask, in which catogory the new entry should be saved */
	if (!stralloc_copys(&sa, "")) die_nomem();
	if (!stralloc_copys(&right, lang_msg(MSG_CATEGORY_QUESTION))) die_nomem();
	show_main(&sa, &right);
	for (r=MSG_CATEGORY01; r < MSG_CATEGORY_KEYS; r++) {
		if (!stralloc_copys(&right, lang_msg(r))) die_nomem();
		show_main(&sa, &right);
	}
	flush();
	r = getkey(lang_msg(MSG_CATEGORY_KEYS));
	if (!stralloc_copys(categ, categories[r])) die_nomem();

	stralloc_free(&sa);
	stralloc_free(&right);

	return;
}

/**
 * db_ask_categories - ask the user for the submit mode
 *
 * @return       0 = test-submit / 1 = real-submit
 */
static int db_ask_submitmode(void)
{
	stralloc sa, right;
	int r;

	stralloc_init(&sa);
	stralloc_init(&right);

	/* ask, in which catogory the new entry should be saved */
	if (!stralloc_copys(&sa, "")) die_nomem();
	if (!stralloc_copys(&right, lang_msg(MSG_SUBMITMODE_QUESTION))) die_nomem();
	show_main(&sa, &right);
	if (!stralloc_copys(&right, lang_msg(MSG_SUBMITMODE_TEST))) die_nomem();
	show_main(&sa, &right);
	if (!stralloc_copys(&right, lang_msg(MSG_SUBMITMODE_REAL))) die_nomem();
	show_main(&sa, &right);
	flush();
	r = getkey(lang_msg(MSG_SUBMITMODE_KEYS));

	stralloc_free(&sa);
	stralloc_free(&right);

	return r;
}

/**
 * db_cddbp_write_sendfile - send cddb file to server
 * - read cddb file from disk and send it to server
 *
 * @return nothing
 */
static int db_cddbp_write_sendfile(void)
{
	stralloc line;
	buffer b;

	if (buffer_mmapread(&b, cd->cddbfile.s) == -1) {
		return -1;
	}

	io_wantwrite(cddb_fd); io_wait(); io_dontwantwrite(cddb_fd);
	stralloc_init(&line);
	while (buffer_getnewline_sa(&b, &line) == 1) {
		buffer_putsa(&cddb_out, &line);
	}
	buffer_putsflush(&cddb_out, ".\r\n");
	buffer_close(&b);

	if (!stralloc_copys(&line, cddbdata)) die_nomem();
	db_logline(D_NOTE, &line);

	stralloc_free(&line);

	return 0;
}

/**
 * db_cddb_read - get cddb file from cddb server
 *
 * @return nothing
 */
void db_cddbp_read(void)
{
	stralloc cmd, resp, categ, discid;
	int r, code;

	stralloc_init(&cmd);
	stralloc_init(&resp);
	cd->db=DB_NOENT; /* default result */

	r = db_cddbp_connect();
	if (r == -1) {
		db_press_key2continue();
		return;
	}
	code = db_cddbp_getresp(&resp);

	switch (code) {
	case 200: /* read/write ok */
	case 201: /* read only */
		break;
	default:
		stralloc_free(&cmd);
		stralloc_free(&resp);
		db_cddbp_disconnect();
		return;
	}

	/* 1. cddb hello username hostname clientname version */
	if (!stralloc_copys(&cmd, "cddb hello ")) die_nomem();
	if (!stralloc_cats(&cmd, mcdp_env_username)) die_nomem();
	if (!stralloc_cats(&cmd, " ")) die_nomem();
	if (!stralloc_cats(&cmd, mcdp_env_hostname)) die_nomem();
	if (!stralloc_cats(&cmd, " mcdp ")) die_nomem();
	if (!stralloc_cats(&cmd, mcdp_version)) die_nomem();
	db_cddbp_sendcmd(&cmd);
	code = db_cddbp_getresp(&resp);

	if (code != 200) {
		stralloc_free(&cmd);
		stralloc_free(&resp);
		db_cddbp_disconnect();
		return;
	}

	/* 2. cddb proto level */
	if (!stralloc_copys(&cmd, "proto ")) die_nomem();
	if (!stralloc_cat_uint(&cmd, MCDP_CDDBP_LEVEL)) die_nomem();
	db_cddbp_sendcmd(&cmd);
	code = db_cddbp_getresp(&resp);

	switch (code) {
	case 200: /* ... not important */
	case 201: /* switched to 5 */
		break;
	default:
		stralloc_free(&cmd);
		stralloc_free(&resp);
		db_cddbp_disconnect();
		return;
	}

	/* 3. cddb query discid ntrks off1 off2 ... total-secs */
	if (!stralloc_copys(&cmd, "cddb query ")) die_nomem();
	if (!stralloc_cat_x32pw(&cmd, cd->discid, 8, '0')) die_nomem();
	if (!stralloc_cats(&cmd, " ")) die_nomem();
	if (!stralloc_cat_uint(&cmd, cd->titles)) die_nomem();
	if (!stralloc_cats(&cmd, " ")) die_nomem();
	for (r=1; r<=cd->titles; r++) {
		if (!stralloc_cat_uint(&cmd, cd->t[r].cddb)) die_nomem();
		if (!stralloc_cats(&cmd, " ")) die_nomem();
	}
	if (!stralloc_cat_uint(&cmd, cd->t[0].cddb/75)) die_nomem();
	db_cddbp_sendcmd(&cmd);
	code = db_cddbp_getresp(&resp);

	stralloc_init(&categ);
	stralloc_init(&discid);
	switch (code) {
	case 200:
		/* 200 misc a90c9d0c Queen / Innuendo */
		r = str_chr(resp.s+4, ' ');
		if (str_len(resp.s+4) == (unsigned int)r) {
			db_cddbp_disconnect();
			return;
		}
		if (!stralloc_copyb(&categ, resp.s+4, r)) die_nomem();
		if (!stralloc_cat_x32pw(&discid, cd->discid, 8, '0')) die_nomem();
		break;
	case 210:
		/* Found exact matches, list follows (until terminating marker) */
	case 211:
		/* Found inexact matches, list follows (until terminating marker) */
		db_ask_matches(&categ, &discid);
		break;
	case 202:
		/* No match found */
	default:
		stralloc_free(&cmd);
		stralloc_free(&resp);
		stralloc_free(&categ);
		stralloc_free(&discid);
		db_cddbp_disconnect();
		return;
	}

	/* 4. cddb read categ discid */
	if (!stralloc_copys(&cmd, "cddb read ")) die_nomem();
	if (!stralloc_cat(&cmd, &categ)) die_nomem();
	if (!stralloc_cats(&cmd, " ")) die_nomem();
	if (!stralloc_cat(&cmd, &discid)) die_nomem();
	db_cddbp_sendcmd(&cmd);
	code = db_cddbp_getresp(&resp);

	stralloc_free(&cmd);
	stralloc_free(&resp);
	stralloc_free(&categ);
	stralloc_free(&discid);

	if (code != 210) {
		db_cddbp_disconnect();
		return;
	}
	if (db_savefile() == -1) {
		db_cddbp_disconnect();
		return;
	}

	db_cddbp_disconnect();
	cd->db=DB_CDDB;

	return;
}

/**
 * db_cddb_write - put cddb file to cddb server
 *
 * @return nothing
 */
void db_cddbp_write(void)
{
	stralloc sa, cmd, resp, categ, right;
	int r, code;

	stralloc_init(&sa);
	stralloc_init(&cmd);
	stralloc_init(&resp);
	stralloc_init(&categ);
	stralloc_init(&right);
	cd->db=DB_NOENT; /* default result */

	r = db_cddbp_connect();
	if (r == -1) {
		db_press_key2continue();
		return;
	}
	code = db_cddbp_getresp(&resp);

	if (code != 200) {
		if (!stralloc_copys(&sa, lang_msg(MSG_DB_NOPOSTING))) die_nomem();
		db_logline(D_CDDB_FAIL, &sa);
		db_cddbp_disconnect();
		stralloc_free(&sa);
		return;
	}

	/**
	 * we know, that posting via cddbp is okay, ask for category now!
	 */
	db_ask_categories(&categ);

	/* 1. cddb hello username hostname clientname version */
	if (!stralloc_copys(&cmd, "cddb hello ")) die_nomem();
	if (!stralloc_cats(&cmd, mcdp_env_username)) die_nomem();
	if (!stralloc_cats(&cmd, " ")) die_nomem();
	if (!stralloc_cats(&cmd, mcdp_env_hostname)) die_nomem();
	if (!stralloc_cats(&cmd, " mcdp ")) die_nomem();
	if (!stralloc_cats(&cmd, mcdp_version)) die_nomem();
	db_cddbp_sendcmd(&cmd);
	code = db_cddbp_getresp(&resp);
	if (code != 200) {
		stralloc_free(&cmd);
		stralloc_free(&resp);
		db_cddbp_disconnect();
		return;
	}

	/* 2. cddb proto level */
	if (!stralloc_copys(&cmd, "proto ")) die_nomem();
	if (!stralloc_cat_uint(&cmd, MCDP_CDDBP_LEVEL)) die_nomem();
	db_cddbp_sendcmd(&cmd);
	code = db_cddbp_getresp(&resp);

	switch (code) {
	case 200: /* ... not important */
	case 201: /* switched to 5 */
		break;
	default:
		stralloc_free(&cmd);
		stralloc_free(&resp);
		db_cddbp_disconnect();
		return;
	}

	/* 3. cddb write categ discid */
	if (!stralloc_copys(&cmd, "cddb write ")) die_nomem();
	if (!stralloc_cat(&cmd, &categ)) die_nomem();
	if (!stralloc_cats(&cmd, " ")) die_nomem();
	if (!stralloc_cat_x32pw(&cmd, cd->discid, 8, '0')) die_nomem();
	db_cddbp_sendcmd(&cmd);
	code = db_cddbp_getresp(&resp);
	if (code != 320) {
		stralloc_free(&cmd);
		stralloc_free(&resp);
		stralloc_free(&categ);
		db_cddbp_disconnect();
		return;
	}
	stralloc_free(&categ);

	db_cddbp_write_sendfile();
	code = db_cddbp_getresp(&resp);
	if (code != 200) {
		stralloc_free(&cmd);
		stralloc_free(&resp);
		db_cddbp_disconnect();
		return;
	}

	stralloc_free(&cmd);
	stralloc_free(&resp);
	db_cddbp_disconnect();
	cd->db=DB_CDDB;

	return;
}

/**
 * some notes about the cddb over http protocol
 * - works like the cddbp, but with some changes
 * - the proto and the hello command are issued every time
 * - we have to do only:
 *   - reading: cddb query + cddb read
 *   - cddb write for submitting
 *     - but: we can't test, if the server supports submitting via http :(
 *     - the stat command is to expensive for the servers!
 *
 * /TR 2006-03-08
 */

/**
 * db_http_connect - setup the connection the the server
 * - resolves ip of the cddb server and connect to it
 *
 * @return zero on succuess, -1 on failure
 */
static int db_http_connect(char *server)
{
	stralloc fqdn;
	int r;
	long l;

	/* for proxy connections, we use another function */
	if (mcdp_env_http_proxy) {
		return db_proxy_connect();
	}

	/* initialize the strallocs */
	stralloc_init(&fqdn);

	l = str_chr(server, '/');
	if (!server[l]) return -1;
	if (!stralloc_copyb(&fqdn, server, l)) die_nomem();
	r = db_connect(&fqdn, 80);
	stralloc_free(&fqdn);

	return r;
}

/**
 * db_proxy_connect - setup the connection the the server
 * - connects to proxy, even with proxy authentication
 *
 * @return zero on succuess, -1 on failure
 */
static int db_proxy_connect(void)
{
	unsigned int port;
	stralloc fqdn;
	long l;
	int r;

	stralloc_init(&fqdn);

	l = str_rchr(mcdp_env_http_proxy, ':');
	if (mcdp_env_http_proxy[l] == 0) {
		port=(unsigned int)DEFAULT_PROXYPORT;
		if (!stralloc_copys(&fqdn, mcdp_env_http_proxy))
			die_nomem();
	} else {
		scan_uint(mcdp_env_http_proxy+l+1, &port);
		if (!stralloc_copyb(&fqdn, mcdp_env_http_proxy, l))
			die_nomem();
	}

	r = db_connect(&fqdn, port);
	stralloc_free(&fqdn);

	return r;
}

/**
 * db_proxy_addauth - add authentication header to request
 *
 * @return nothing
 */
static void db_proxy_addauth(stralloc *request)
{
	stralloc auth;

	if (!mcdp_env_http_proxy_user) {
		return;
	}

	stralloc_init(&auth);
	if (!stralloc_cats(request, mcdp_env_http_proxy_user)) die_nomem();
	if (mcdp_env_http_proxy_pass) {
		if (!stralloc_cats(request, ":")) die_nomem();
		if (!stralloc_cats(request, mcdp_env_http_proxy_pass)) die_nomem();
	}
	if (!stralloc_cats(request, "\r\nProxy-Authorization: Basic ")) die_nomem();
	if (!stralloc_cat_base64(request, auth.s, auth.len)) die_nomem();
	stralloc_free(&auth);

	return;
}


/**
 * db_http_sendcmd - send a new command to the cddb server
 *
 * @param cmd  contains the cddb command, which should be send
 * @return     nothing
 */
static void db_http_sendcmd(stralloc *cmd)
{
	stralloc request, realcmd, host, path;
	long l;

	stralloc_init(&path);
	stralloc_init(&host);
	stralloc_init(&request);
	stralloc_init(&realcmd);
	db_logline(D_CDDB_SEND, cmd);

	/* str_chr() was tested @ connect time */
	l = str_chr(mcdp_env_cddb_webserver, '/');
	
	/* fill host and path information of webserver */
	if (!stralloc_copyb(&host, mcdp_env_cddb_webserver, l)) die_nomem();
	if (mcdp_env_http_proxy) {
		/* we have to give the whole url here */
		if (!stralloc_copys(&path, "http://")) die_nomem();
		if (!stralloc_cats(&path, mcdp_env_cddb_webserver)) die_nomem();
	} else {
		/* we give only the path here */
		if (!stralloc_copys(&path, mcdp_env_cddb_webserver+l)) die_nomem();
	}

	/* create a full command
	 * -> we have: cddb+read+rock+12345678
	 * -> append:  &hello=joe+my.host.com+xmcd+2.1&proto=5
	 */
	if (!stralloc_copy(&realcmd, cmd)) die_nomem();
	if (!stralloc_cats(&realcmd, "&hello=")) die_nomem();
	if (!stralloc_cats(&realcmd, mcdp_env_username)) die_nomem();
	if (!stralloc_cats(&realcmd, "+")) die_nomem();
	if (!stralloc_cats(&realcmd, mcdp_env_hostname)) die_nomem();
	if (!stralloc_cats(&realcmd, "+mcdp+")) die_nomem();
	if (!stralloc_cats(&realcmd, mcdp_version)) die_nomem();
	if (!stralloc_cats(&realcmd, "&proto=")) die_nomem();
	if (!stralloc_cat_uint(&realcmd, MCDP_CDDBP_LEVEL)) die_nomem();

	/* GET /path/with-cmd HTTP/1.0\r\n
	 * User-Agent: mcdp 0.6\r\n
	 * Accept: *X*\r\n
	 * Host: mcdp_env_webserver\r\n
	 * \r\n
	 *
	 * cmd: cddb+query+a90c9d0c+12+150+...+221640+3231&hello=root+p28tr+mcdp+0.6&proto=5
	 * cmd: cddb+read+data+a90c9d0c&hello=root+p28tr+mcdp+0.6&proto=5
	 * XXX: add some urlencoding ? but we have no critical characters?!
	 */
	if (!stralloc_copys(&request, "GET ")) die_nomem();
	if (!stralloc_cat(&request, &path)) die_nomem();
	if (!stralloc_cats(&request, "?cmd=")) die_nomem();
	if (!stralloc_cat(&request, &realcmd)) die_nomem();
	if (!stralloc_cats(&request, " HTTP/1.0")) die_nomem();
	if (!stralloc_cats(&request, "\r\nUser-Agent: mcdp ")) die_nomem();
	if (!stralloc_cats(&request, mcdp_version)) die_nomem();
	if (!stralloc_cats(&request, "\r\nAccept: */*")) die_nomem();
	if (!stralloc_cats(&request, "\r\nHost: ")) die_nomem();
	if (!stralloc_cat(&request, &host)) die_nomem();
	db_proxy_addauth(&request); /* add authentication header, if needed */
	if (!stralloc_cats(&request, "\r\n\r\n")) die_nomem();

	/* send it! */
	io_wantwrite(cddb_fd); io_wait(); io_dontwantwrite(cddb_fd);
	buffer_putsaflush(&cddb_out, &request);

	stralloc_free(&host);
	stralloc_free(&path);
	stralloc_free(&request);
	stralloc_free(&realcmd);

	return;
}

/**
 * db_http_getresp - read a response from cddb server
 *
 * @param resp  the first line of the response is written tp this stralloc
 * @return      nothing
 */
static int db_http_getresp(stralloc *resp)
{
	stralloc line;
	unsigned int code=0;
	int r;


	if (!stralloc_copys(resp, "")) die_nomem();
	stralloc_init(&line);

	/* read http headers end some time ... */
	io_wantread(cddb_fd); io_wait(); io_dontwantread(cddb_fd);
	for (;;) {
		r = buffer_getnewline_sa(&cddb_in, &line);
		if (r <= 0) {
			/* EOF, wait again */
			io_wantread(cddb_fd);
			io_wait();
			io_dontwantread(cddb_fd);
		}

		/* look for some http error ... */
		line.s[line.len]=0;
		if (str_start(line.s, "HTTP/1.")) {
			r = scan_uint(line.s+9, &code);
			if ((r != 3) || (code != 200)) {
				db_logline(D_CDDB_FAIL, &line);
				return -1;
			}
		}

		if (line.len == 2)
		if (line.s[0] == '\r')
		if (line.s[1] == '\n') break;
	}

	for (;;) {
		r = buffer_getline_sa(&cddb_in, resp);
		if (r <= 0) {
			/* EOF, wait again */
			io_wantread(cddb_fd);
			io_wait();
			io_dontwantread(cddb_fd);
		}
		if (r == 1) break; /* we have a full line */
	}

	/* get cddb code from server */
	r = resp->s[0] - '0';
	r = r * 10 + (resp->s[1] - '0');
	r = r * 10 + (resp->s[2] - '0');
	stralloc_chomp(resp);
	db_logline(D_CDDB_RETR, resp);
	stralloc_free(&line);

	return r;
}

/**
 * db_http_read - get cddb file from cddb webserver
 *
 * @return nothing
 */
void db_http_read(void)
{
	stralloc sa, cmd, resp, categ, discid;
	int r, code;

	stralloc_init(&sa);
	stralloc_init(&cmd);
	stralloc_init(&resp);
	cd->db=DB_NOENT; /* default result */

	r = db_http_connect(mcdp_env_cddb_webserver);
	if (r == -1) {
		db_press_key2continue();
		return;
	}

	/* 1. cddb query discid ntrks off1 off2 ... total-secs */
	if (!stralloc_copys(&cmd, "cddb+query+")) die_nomem();
	if (!stralloc_cat_x32pw(&cmd, cd->discid, 8, '0')) die_nomem();
	if (!stralloc_cats(&cmd, "+")) die_nomem();
	if (!stralloc_cat_uint(&cmd, cd->titles)) die_nomem();
	if (!stralloc_cats(&cmd, "+")) die_nomem();
	for (r=1; r<=cd->titles; r++) {
		if (!stralloc_cat_uint(&cmd, cd->t[r].cddb)) die_nomem();
		if (!stralloc_cats(&cmd, "+")) die_nomem();
	}
	if (!stralloc_cat_uint(&cmd, cd->t[0].cddb/75)) die_nomem();
	db_http_sendcmd(&cmd);
	code = db_http_getresp(&resp);

	stralloc_init(&categ);
	stralloc_init(&discid);
	switch (code) {
	case 200:
		/* 200 misc a90c9d0c Queen / Innuendo */
		r = str_chr(resp.s+4, ' ');
		if (str_len(resp.s+4) == (unsigned int)r) {
			db_press_key2continue();
			return;
		}
		if (!stralloc_copyb(&categ, resp.s+4, r)) die_nomem();
		if (!stralloc_cat_x32pw(&discid, cd->discid, 8, '0')) die_nomem();
		break;
	case 210:
		/* Found exact matches, list follows (until terminating marker) */
	case 211:
		/* Found inexact matches, list follows (until terminating marker) */
		db_ask_matches(&categ, &discid);
		break;
	case 202:
		/* No match found */
	default:
		stralloc_free(&cmd);
		stralloc_free(&resp);
		stralloc_free(&categ);
		stralloc_free(&discid);
		db_press_key2continue();
		return;
	}

	/* reconnect for the next command (what about keep-alive?) */
	r = db_http_connect(mcdp_env_cddb_webserver);
	if (r == -1) {
		db_press_key2continue();
		return;
	}

	/* 2. cddb read categ discid */
	if (!stralloc_copys(&cmd, "cddb+read+")) die_nomem();
	if (!stralloc_cat(&cmd, &categ)) die_nomem();
	if (!stralloc_cats(&cmd, "+")) die_nomem();
	if (!stralloc_cat(&cmd, &discid)) die_nomem();
	db_http_sendcmd(&cmd);
	code = db_http_getresp(&resp);

	stralloc_free(&cmd);
	stralloc_free(&resp);
	stralloc_free(&categ);
	stralloc_free(&discid);

	/* was the read okay */
	if (code != 210) {
		db_press_key2continue();
		return;
	}

	/* was the file written correctly */
	if (db_savefile() == -1) {
		db_press_key2continue();
		return;
	}

	/* all went well */
	db_press_key2continue();
	cd->db=DB_CDDB;
	

	return;
}

/**
 *
 * 2. Submission via http
 * ----------------------
 *
 * The method of submitting via http is, to transmit the entry to the database
 * through a CGI program, which is present at the same location as the cddb.cgi.
 * The standard address for this is:
 *
 * http://freedb_server/~cddb/submit.cgi
 *
 * where freedb_server is the address of the selected freedb-server. 
 *
 * You may implement a button or somesuch in your software's user interface to
 * initiate submissions. Rejected submissions are automatically returned via
 * e-mail to the sender specified in the "User-Email" header field (see below)
 * with an explanation of the reason for the rejection.
 *
 * To submit via the cgi-program, you must use the "POST" method of sending data;
 * "GET" is not supported. Several HTTP "Entity-Header" fields (described below)
 * must be included in the data followed by a blank line, followed by the
 * "Entity-Body" (the freedb entry) in the format described in the database-format 
 * specification.
 * The header fields are:
 *
 * Category: valid_freedb_category
 * Discid: cd_discid
 * User-Email: username@domain
 * Submit-Mode: test_or_submit
 * Content-Length: freedb_entry_length
 * Charset: character_set_of_freedb_entry (optional)
 * X-Cddbd-Note: message_for_user         (optional)
 *
 * Where
 *
 * - "valid_freedb_category" is a valid freedb category (valid categories are:
 *   blues, classical, country, data, folk, jazz, misc, newage, reggae, rock,
 *   soundtrack). Submissions with an invalid category specified will be rejected.
 *
 * - "cd_discid" is the 8-digit hex CDDB/freedb disc ID of the entry (see 
 *   Appendix A). This has to be exactly the same disc ID that appears in the
 *   "DISCID=" section of the entry being submitted. If not, the entry will be
 *   rejected.
 *
 * - "username@domain" is the valid e-mail address of the submitting user. This
 *   is required, as the server must be able to notify the submitter, if the
 *   submission was rejected. So you should _never_ fill this with a default
 *   value. Your program should refuse to submit anything, if the user has not
 *   specified an e-mail-address.
 *
 * - "test_or_submit" is the word "test" or "submit" (without the surrounding
 *   quotes), which specifies if the submission is a test submission or a real
 *   submission to the database (test submissions are described below).
 *
 * - "freedb_entry_length" is the size in bytes of the freedb entry being
 *   submitted. This number covers only the "Entity-Body" (without the blank line
 *   that separates it from the header).
 *
 * - "character_set_of_freedb_entry" is US-ASCII, ISO-8859-1 or UTF-8 (can be lower
 *   case). This specifies the character set the submitted entry has been encoded
 *   in. If your application knows the user's character set, then you should
 *   specify it here. If your program supports UTF-8 and protocol level 6,
 *   you should always use UTF-8 for submissions and specify UTF-8 as the charset
 *   of the e-mail, even if the submission contains no 8bit-characters and would 
 *   therefore also be valid as US-ASCII. Only submissions specifying UTF-8 as the
 *   charset in the headers are allowed to update existing entries that contain
 *   characters, which can not be represented in US-ASCII or ISO-8859-1.
 *   The default charset value if no charset is specified is ISO-8859-1 for
 *   backwards compatibility.
 *
 * - "message_for_user" is an arbitrary message to be included at the top of
 *   any rejection notice that may be sent to the submitting user. The length of
 *   the arbitrary message is limited to 70 chars.
 *
 * To see, how a correct submission should look like, take a look at the following
 * example:
 *
 * POST /~cddb/submit.cgi HTTP/1.0
 * Category: newage
 * Discid: 4306eb06
 * User-Email: joe@myhost.home.com
 * Submit-Mode: submit
 * Charset: ISO-8859-1
 * X-Cddbd-Note: Sent by free CD player - Questions: support@freecdplayer.org.
 * Content-Length: 960
 *
 * # xmcd
 * #
 * # Track frame offsets:
 * [ data omitted for brevity ]
 * PLAYORDER=
 *
 * Note the blank line between the "Content-Length" header field and "# xmcd"
 * which marks the beginning of the freedb entry. This complies to the standard 
 * http header behaviour where the http header information is separated by a 
 * single newline from the body content. Check http://www.w3.org/Protocols/
 * for more information on the http in general.
 *
 * The CGI does a quick check on the submitted data (a more thoroughly check of
 * the entry is made later by the server, which notifies the user by e-mail if the
 * submission has been rejected) and responds to a submission
 * with a 3-digit response code followed by a description. 
 * Currently the following response-codes are used:
 *
 * 200 OK, submission has been sent.
 * 500 Missing required header information.
 * 500 Internal Server Error: [description].
 * 501 Invalid header information [details].
 *
 * where "details" can be one of the following:
 * freedb category
 * disc ID
 * email address
 * charset
 *
 * The body of the freedb submission must be sent exactly as described in 
 * Appendix B. Do _not_ encode the data in any way before transmitting it; data
 * must be sent as raw text. Windows programmers should not use the Windows URL
 * encode function prior to calling the submit CGI program. Doing so may lead to
 * corrupt data being sent, causing rejected submissions.
 *
 * Please do not allow a user to submit CD database entries that have completely
 * unfilled contents (i.e., blank information in the disc artist/title as well as
 * the track titles, or filled with useless default information like "track 1",
 * "track 2", etc.). While the current CD database server checks and rejects
 * submissions that have a blank DTITLE line, it doesn't (and can't feasibly)
 * check the track titles effectively, nor can it check any of these fields
 * if they are filled with a default string.  If it were, it would have to be
 * hacked to know about the default strings of every possible client.
 *
 * Thus, please design your client with this in mind. This is a somewhat
 * tricky thing to do, as some CDs contain blank tracks with no titles
 * and you need to allow for that.  An example minimum requirement
 * that a CD player client should meet is listed below:
 *
 * 1. Don't allow the "send" or "submit" feature to be activated if the CD
 *    database information form is not edited at all.
 * 2. Check that the disc artist/title contains something (that the user
 *    typed in).
 * 3. Check that the majority of the tracks have a title filled in by the user.
 *
 * This should minimize the amount of useless garbage being submitted to the 
 * CD database.
 *
 * Before you release your software, please be sure that it produces submissions
 * that adhere to the freedb file format, and that the frame offset, disc
 * length, and disc ID information are correctly computed.
 * For testing, please make your software sends submissions with the
 * "Submit-Mode" HTTP header field set to "test".
 *
 * freedb submissions sent in test mode will be sanity-checked by the freedb
 * server and pass/fail confirmation will be sent back to the submitter, but 
 * the submission will not actually be deposited in the CD database. Please do 
 * _not_ send submissions in "submit" mode until you have tested your program 
 * with several different CD's.
 *
 * When you feel your application is ready to support submissions, we would 
 * appreciate, if you would contact us and provide a copy of your program 
 * before releasing it, so we can check if everything is really OK.
 */


/**
 * db_http_write - put cddb file to cddb webserver
 *
 * @return nothing
 */
void db_http_write(void)
{
	stralloc line, host, path, mode, categ, request;
	unsigned int content_len;
	buffer b;
	long l;
	int r;

	stralloc_init(&line);
	stralloc_init(&path);
	stralloc_init(&host);
	stralloc_init(&mode);
	stralloc_init(&categ);
	stralloc_init(&request);

	l = (long)getfilesize(cd->cddbfile.s);
	if (l == -1) {
		db_press_key2continue();
		return;
	}
	content_len = (unsigned int)l;

	if (buffer_mmapread(&b, cd->cddbfile.s) == -1) {
		db_press_key2continue();
		return;
	}

	/* get category and submit mode from user */
	db_ask_categories(&categ);
	r = db_ask_submitmode();
	if (r == 0) {
		if (!stralloc_copys(&mode, "test")) die_nomem();
	} else {
		if (!stralloc_copys(&mode, "submit")) die_nomem();
	}

	r = db_http_connect(mcdp_env_cddb_submit);
	if (r == -1) {
		buffer_close(&b);
		db_press_key2continue();
		stralloc_free(&categ);
		stralloc_free(&mode);
		return;
	}

	/* str_chr() was tested @ connect time */
	l = str_chr(mcdp_env_cddb_submit, '/');
	
	/* fill host and path information of webserver */
	if (!stralloc_copyb(&host, mcdp_env_cddb_submit, l)) die_nomem();
	if (mcdp_env_http_proxy) {
		/* we have to give the whole url here */
		if (!stralloc_copys(&path, "http://")) die_nomem();
		if (!stralloc_cats(&path, mcdp_env_cddb_submit)) die_nomem();
	} else {
		/* we give only the path here */
		if (!stralloc_copys(&path, mcdp_env_cddb_submit+l)) die_nomem();
	}

	/*
	 * Category: valid_freedb_category
	 * Discid: cd_discid
	 * User-Email: username@domain
	 * Submit-Mode: test_or_submit
	 * Content-Length: freedb_entry_length
	 * Charset: character_set_of_freedb_entry (optional)
	 * X-Cddbd-Note: message_for_user         (optional)

	 * POST /~cddb/submit.cgi HTTP/1.0
	 * Host: freedb.freedb.org
	 * Category: newage
	 * Discid: 4306eb06
	 * User-Email: joe@myhost.home.com
	 * Submit-Mode: submit
	 * Charset: ISO-8859-1
	 * X-Cddbd-Note: Sent by free CD player - Questions: support@freecdplayer.org.
	 * Content-Length: 960
	 */

	if (!stralloc_copys(&request, "POST ")) die_nomem();
	if (!stralloc_cat(&request, &path)) die_nomem();
	if (!stralloc_cats(&request, " HTTP/1.0")) die_nomem();
	if (!stralloc_cats(&request, "\r\nHost: ")) die_nomem();
	if (!stralloc_cat(&request, &host)) die_nomem();
	db_proxy_addauth(&request); /* add authentication header, if needed */
	if (!stralloc_cats(&request, "\r\nUser-Agent: mcdp ")) die_nomem();
	if (!stralloc_cats(&request, mcdp_version)) die_nomem();
	if (!stralloc_cats(&request, "\r\nCategory: ")) die_nomem();
	if (!stralloc_cat(&request, &categ)) die_nomem();
	if (!stralloc_cats(&request, "\r\nDiscid: ")) die_nomem();
	if (!stralloc_cat_x32pw(&request, cd->discid, 8, '0')) die_nomem();
	if (!stralloc_cats(&request, "\r\nUser-Email: ")) die_nomem();
	if (mcdp_env_email) {
		if (!stralloc_cats(&request, mcdp_env_email)) die_nomem();
	} else {
		if (!stralloc_cats(&request, "nomail@nohost")) die_nomem();
	}
	if (!stralloc_cats(&request, "\r\nSubmit-Mode: ")) die_nomem();
	if (!stralloc_cat(&request, &mode)) die_nomem();
	if (!stralloc_cats(&request, "\r\nCharset: ISO-8859-1")) die_nomem();
	if (!stralloc_cats(&request, "\r\nX-Cddbd-Note: Sent by mcdp ")) die_nomem();
	if (!stralloc_cats(&request, mcdp_version)) die_nomem();
	if (!stralloc_cats(&request, " - questions: milky-mcdp@mcmilk.de")) die_nomem();
	if (!stralloc_cats(&request, "\r\nContent-Length: ")) die_nomem();
	if (!stralloc_cat_uint(&request, content_len)) die_nomem();
	if (!stralloc_cats(&request, "\r\n\r\n")) die_nomem();


	/* send it! */
	db_logline(D_CDDB_SEND, &request);
	io_wantwrite(cddb_fd); io_wait(); io_dontwantwrite(cddb_fd);
	buffer_putsa(&cddb_out, &request);

	io_wantwrite(cddb_fd); io_wait(); io_dontwantwrite(cddb_fd);
	while (buffer_getnewline_sa(&b, &line) == 1) {
		buffer_putsa(&cddb_out, &line);
	}
	buffer_flush(&cddb_out);
	buffer_close(&b);

	if (!stralloc_copys(&line, cddbdata)) die_nomem();
	db_logline(D_NOTE, &line);

	db_press_key2continue();
	if (!stralloc_copys(&request, "")) die_nomem();
	db_http_getresp(&request);
	db_press_key2continue();

	stralloc_free(&line);
	stralloc_free(&path);
	stralloc_free(&host);
	stralloc_free(&mode);
	stralloc_free(&categ);
	stralloc_free(&request);

	return;
}

/**
 * 1. Submission via e-mail
 * ------------------------
 *
 * The method of submission is to send the entry to following address via e-mail:
 *
 *     freedb-submit@freedb.org
 *
 * You may implement a button or somesuch in your software's user-interface to
 * facilitate this.  The destination e-mail address should be made
 * user-configurable.  Submissions are silently accepted, and no confirmation
 * of receipt is sent to the submitter.  Rejected submissions are automatically
 * returned to the sender via e-mail with an explanation of the reason for the
 * rejection.
 * 
 * There should be one e-mail message per freedb entry.  The e-mail subject line
 * should be in the form "cddb category discid".  For example:
 * 
 * Subject: cddb rock 850f970b
 * 
 * You may optionally set an additional "X-Cddbd-Note:" header line, specifying
 * an arbitrary message to be included at the top of any rejection notice that 
 * may be sent to the submitting user. You could e.g. add a note about your 
 * support-address give the user a chance to contact you, if there are problems 
 * to successfully submit using your program. The length of the arbitrary message 
 * is limited to 70 chars.
 * 
 * The body of the e-mail message should be in the format of a freedb file entry
 * as described in the database format specification. The messages should contain
 * only plain ASCII text. Do not attach encoded information or add special escape
 * sequences.
 * 
 * The master freedb accepts only submissions in the US-ASCII, ISO-8859-1 and
 * UTF-8 character sets. If your program supports UTF-8 and protocol level 6,
 * you should always use UTF-8 for submissions and specify UTF-8 as the charset
 * of the e-mail, even if the submission contains no 8bit-characters and would 
 * therefore also be valid as US-ASCII. Only submissions specifying UTF-8 as the
 * charset in the e-mail headers are allowed to update existing entries that
 * contain characters, which can not be represented in US-ASCII or ISO-8859-1.
 * 
 * Note that the disc ID specified in the e-mail subject line should also appear
 * in the list of disc IDs in the DISCID= field of the freedb file entry. If not,
 * it is considered an error and the submission will be rejected.
 * 
 * You should only allow categories that are currently supported by the freedb.
 * Valid categories are: blues, classical, country, data, folk, jazz, misc,
 * newage, reggae, rock and soundtrack 
 * Submissions specifying unsupported categories will be rejected.
 * 
 * Please do not allow a user to submit CD database entries that have completely
 * unfilled contents (i.e., blank information in the disc artist/title as well as
 * the track titles, or filled with useless default information like "track 1",
 * "track 2", etc.). While the current CD database server checks and rejects
 * submissions that have a blank DTITLE line, it doesn't (and can't feasibly)
 * check the track titles effectively, nor can it check any of these fields
 * if they are filled with a default string.  If it were, it would have to be
 * hacked to know about the default strings of every possible client.
 * 
 * Thus, please design your client with this in mind. This is a somewhat
 * tricky thing to do, as some CDs contain blank tracks with no titles
 * and you need to allow for that.  An example minimum requirement
 * that a CD player client should meet is listed below:
 * 
 * 1. Don't allow the "send" or "submit" feature to be activated if the CD
 *    database information form is not edited at all.
 * 2. Check that the disc artist/title contains something (that the user
 *    typed in).
 * 3. Check that the majority of the tracks have a title filled in by the user.
 *    
 * This should minimize the amount of useless garbage being submitted
 * into the CD database.
 * 
 * Before you release your software, please be sure that it produces submissions
 * that adhere to the freedb file format, and that the frame offset, disc
 * length, and disc ID information are correctly computed. For testing, please
 * make your software send submissions to the following e-mail address (rather
 * than the real submission site at freedb-submit@freedb.org):
 * 
 *     test-submit@freedb.org
 * 
 * 
 * freedb submissions sent to the test e-mail address will be sanity-checked 
 * by the freedb server and pass/fail confirmation will be sent back to the 
 * submitter, but the submission will not actually be deposited in the CD 
 * database. Please do _not_ send submissions in "submit" mode until you have 
 * tested your program with several different CD's.
 * 
 * When you feel your application is ready to support submissions, we would 
 * appreciate, if you would contact us and provide a copy of your program 
 * before releasing it, so we can check if everything is really OK.
 */

/**
 * db_email_write - put cddb file to cddb webserver
 * - hm, we can't check, if the server supports posting
 * - has someone an idea ? (If yes, mail me!)
 * - XXX: todo
 *
 * @return nothing
 */
void db_email_write(void)
{
	stralloc mode, categ;
	unsigned int content_len;
	long l;
	int r;

	stralloc_init(&mode);
	stralloc_init(&categ);

	l = (long)getfilesize(cd->cddbfile.s);
	if (l == -1) {
		db_press_key2continue();
		return;
	}
	content_len = (unsigned int)l;

	/* get category and submit mode from user */
	db_ask_categories(&categ);
	r = db_ask_submitmode();
	if (r == 0) {
		if (!stralloc_copys(&mode, "test-submit@freedb.org")) die_nomem();
	} else {
		if (!stralloc_copys(&mode, "freedb-submit@freedb.org")) die_nomem();
	}

	/**
	 * we send directly to some freedb mailserver
	 * - look up dnsmx and connect to it
	 *
	 * <<< code Initial response from server ...
	 * 220 OK, all other codes mean: failed
	 *
	 * >>> HELO $hostname
	 * <<< code response from server
	 * 250 OK, all other failed
	 *
	 * >>> MAIL FROM:<$email>
	 * <<< code response from server
	 * 250 OK, all other failed
	 *
	 * >>> RCPT TO:<$freedb-address>
	 * <<< code recipient response from server
	 * 250 OK, all other failed
	 *
	 * >>> DATA
	 * <<< code data response from server
	 * 354 OK, all other failed
	 *
	 * [track data...] and ".\r\n"
	 * >>> QUIT
	 * <<< goodbye response from server
	 * 2xx OK, all other failed, but not important
	 *
	 * /TR 2006-03-21
	 */

	stralloc_free(&mode);
	stralloc_free(&categ);

	return;
}

#if 0
void smtp()
{
  unsigned long code;
  int flagbother;
  int i;
 
  if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
 
  substdio_puts(&smtpto,"HELO ");
  substdio_put(&smtpto,helohost.s,helohost.len);
  substdio_puts(&smtpto,"\r\n");
  substdio_flush(&smtpto);
  if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
 
  substdio_puts(&smtpto,"MAIL FROM:<");
  substdio_put(&smtpto,sender.s,sender.len);
  substdio_puts(&smtpto,">\r\n");
  substdio_flush(&smtpto);
  code = smtpcode();
  if (code >= 500) quit("DConnected to "," but sender was rejected");
  if (code >= 400) quit("ZConnected to "," but sender was rejected");
 
  flagbother = 0;
  for (i = 0;i < reciplist.len;++i) {
    substdio_puts(&smtpto,"RCPT TO:<");
    substdio_put(&smtpto,reciplist.sa[i].s,reciplist.sa[i].len);
    substdio_puts(&smtpto,">\r\n");
    substdio_flush(&smtpto);
    code = smtpcode();
    if (code >= 500) {
      out("h"); outhost(); out(" does not like recipient.\n");
      outsmtptext(); zero();
    }
    else if (code >= 400) {
      out("s"); outhost(); out(" does not like recipient.\n");
      outsmtptext(); zero();
    }
    else {
      out("r"); zero();
      flagbother = 1;
    }
  }
  if (!flagbother) quit("DGiving up on ","");
 
  substdio_putsflush(&smtpto,"DATA\r\n");
  code = smtpcode();
  if (code >= 500) quit("D"," failed on DATA command");
  if (code >= 400) quit("Z"," failed on DATA command");
 
  blast();
  code = smtpcode();
  flagcritical = 0;
  if (code >= 500) quit("D"," failed after I sent the message");
  if (code >= 400) quit("Z"," failed after I sent the message");
  quit("K"," accepted message");
}
#endif

