/* $Id: pgp.cpp,v 1.22 2004/01/02 04:05:36 fesnel Exp $ */
/*******************************************************************************
 *   This program is part of the XFMail email client.                          *
 *                                                                             *
 *   Copyright : (C) 1995-1998 Gennady B. Sorokopud (gena@NetVision.net.il)    *
 *               (C) 1995 Ugen. J. S. Antsilevich (ugen@latte.worldbank.org)   *
 *               (C) 1998-2004 by the Archimedes Project                       *
 *                   http://sourceforge.net/projects/archimedes                *
 *                                                                             *
 *             --------------------------------------------                    *
 *                                                                             *
 *   This program is free software; you can redistribute it and/or modify      *
 *   it under the terms of the GNU General Public License as published by      *
 *   the Free Software Foundation; either version 2 of the License, or         *
 *   (at your option) any later version.                                       *
 *                                                                             *
 *   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., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA.  *
 *                                                                             *
 *   Additional Permission granted:                                            *
 *                                                                             *
 *   This program is designed to use the XForms library, so we consider        *
 *   permission to link to that non-GPL-compatible library is implicit.        *
 *   However, in case this is not considered so, we explicitly state:          *
 *                                                                             *
 *   "As a special exception, the Archimedes Project, with the permission      *
 *    of all earlier copyright holders, formally gives permission to link      *
 *    this program with the XForms library, and distribute the resulting       *
 *    executable without the source code for XForms in the source              *
 *    distribution".                                                           *
 *                                                                             *
 ******************************************************************************/

#include <fmail.h>
#include <umail.h>
#include <pgp.h>
#include <dialogs.h>
#include <configform.h>

static FD_config_pgp *pgp_obj;
static FD_Lookup_PGPId *form_lookup_pgpid = NULL;

extern char *phrase;

int get_pgpchoice(FD_config_pgp * obj, int set_default);
int get_pgpversion(FD_config_pgp * obj);

void PGP_Mime_Call(FL_OBJECT * obj, long param) {
}

void PGP_Prog_Call(FL_OBJECT * obj, long param) {
}

void PGP_User_Call(FL_OBJECT * obj, long param) {
}

void cb_input_passphrase(FL_OBJECT * obj, long param) {
}

void PGP_Timer_Call(FL_OBJECT * obj, long param) {
	pgp_timer_cb();
}

char *input_passphrase() {
	char *rphrase;
	FD_Passphrase *pass;
	int ptimer = Config.getInt("pgptimer", 120);
	if(!phrase) { /* no passphrase, go and get it */
  	  pass = create_form_Passphrase();

  	  fl_show_form(pass->Passphrase, FL_PLACE_MOUSE, FL_TRANSIENT,
				  "Input Passphrase");
      fl_set_input_maxchars(pass->Input_Passphrase,0);
  	  fl_do_only_forms();
  	  phrase = strdup((char *) fl_get_input(pass->Input_Passphrase));
  	  scramble(phrase);
  	  if(pgp_timer) {
	  	  if(ptimer == PGP_MAX_TIMER)
		  	  fl_set_timer(pgp_timer, 0);
	  	  else
		  	  fl_set_timer(pgp_timer, ptimer * 60);
  	  }
  	  fl_hide_form(pass->Passphrase);
  	  fl_free_form(pass->Passphrase);
  	  fl_free(pass);
	}
	/*pass phrase is here, just scramble it */
	/* retp: one more goto went to the drain*/
	rphrase = strdup(phrase);
	scramble(rphrase);

	if(ptimer == 0)
		pgp_timer_cb();

	return rphrase;
}

/*
 *      inbuf: file with text to encode/decode
 *       mode: ENCODE|SIGN or DECODE
 *       recp: if mode&(ENCODE|SIGN), the list of recipient(s)
 * passphrase: if mode==DECODE, or mode==SIGN, the pass phrase
 * see also "pgp/doc/appnote.doc"
 */
void PGP_Conf_Lookup_CB(FL_OBJECT * obj, long data) {
	int keyid, ftype;
	char buf[12];
	struct _mail_addr *addr;

	addr = get_address(Config.getString("from", "").c_str(),ADDR_GET_FIRST);
	keyid = PGP_Lookup_PGPId(addr ? get_charset_addr_line(NULL, addr, &ftype) :
					 user_n, pgp_obj->config_pgp);
	if(addr)
		discard_address(addr);

	if(keyid) {
		snprintf(buf, sizeof(buf), "0x%X", keyid);
		fl_set_input(pgp_obj->PGP_User, buf);
	}

	return;
}

void pgp_conf(int set_default, FD_config_pgp * form) {
	pgp_obj = form;

    string sPgpProg = Config.get("pgp", _PATH_PGP, set_default);
	string sPgpKeyRing = Config.get("pgpkeyring", "", set_default);
    string sPgpKeyServer = Config.get("pgpkeyserver", "", set_default);
    string sPgpUser = Config.get("pgpuser", user_n, set_default);

    int iPgpSign = Config.getIntDefault("pgpsign", 0, set_default);
    int iPgpMime = Config.getIntDefault("pgpmime", 0, set_default) + 1;
    int iPgpUseKeyRing = Config.getIntDefault("pgpusekeyring", 0, set_default);
    int iPgpUseHttp = Config.getIntDefault("pgpusehttp", 0, set_default);
    int iPgpEncryptBcc = Config.getIntDefault("pgp_encrypt_bcc", 0, set_default);

	fl_clear_choice(pgp_obj->PGP_Mime);
	fl_addto_choice(pgp_obj->PGP_Mime, "Text");
	fl_addto_choice(pgp_obj->PGP_Mime, "application/pgp");
	fl_addto_choice(pgp_obj->PGP_Mime, "Multipart");
	fl_set_choice(pgp_obj->PGP_Mime, iPgpMime);
	fl_set_input(pgp_obj->PGP_Prog, sPgpProg.c_str());
	fl_set_input(pgp_obj->PGP_Keyring,sPgpKeyRing.c_str());
	fl_set_input(pgp_obj->PGP_KeyServer, sPgpKeyServer.c_str());
	fl_set_input(pgp_obj->PGP_User, sPgpUser.c_str());
	fl_set_button(pgp_obj->PGP_Sign, iPgpSign);
	fl_set_button(pgp_obj->PGP_Use_Keyring, iPgpUseKeyRing);
	fl_set_button(pgp_obj->PGP_Use_HTTP, iPgpUseHttp);
	fl_set_button(pgp_obj->PGP_Encrypt_Bcc, iPgpEncryptBcc);
	fl_clear_choice(pgp_obj->PGP_500);
	fl_addto_choice(pgp_obj->PGP_500, "PGP 2.6");
	fl_addto_choice(pgp_obj->PGP_500, "PGP 5.0");
	fl_addto_choice(pgp_obj->PGP_500, "PGP 6.5");
	fl_addto_choice(pgp_obj->PGP_500, "GnuPG");
	fl_set_choice(pgp_obj->PGP_500, get_pgpchoice(pgp_obj, set_default));

    return;
}

int get_pgpchoice(FD_config_pgp * obj, int set_default) {
	switch(Config.getIntDefault("pgpversion", PGP_DEFAULT_VERS, set_default)) {
		case 263:
			return 1;
		case 651:
		    return 3; 
	    case 95:
			return 4;
		default:
			return 2;
	}
}

void handle_pgp_input(FD_config_pgp * form) {
	pgp_obj = form;

    string sPgpProg = ((char *) fl_get_input(pgp_obj->PGP_Prog));
    string sPgpKeyRing = ((char *) fl_get_input(pgp_obj->PGP_Keyring));
    string sPgpKeyServer = ((char *) fl_get_input(pgp_obj->PGP_KeyServer));
    string sPgpUser = ((char *) fl_get_input(pgp_obj->PGP_User));

    int iPgpMime = fl_get_choice(pgp_obj->PGP_Mime) - 1;
    int iPgpSign = fl_get_button(pgp_obj->PGP_Sign);
    int iPgpUseKeyRing = fl_get_button(pgp_obj->PGP_Use_Keyring);
    int iPgpUseHttp = fl_get_button(pgp_obj->PGP_Use_HTTP);
    int iPgpEncryptBcc = fl_get_button(pgp_obj->PGP_Encrypt_Bcc);
    int iPgpVersion = get_pgpversion(pgp_obj);

	Config.set("pgp", sPgpProg);
	Config.set("pgpkeyring", sPgpKeyRing);
	Config.set("pgpkeyserver", sPgpKeyServer);
	Config.set("pgpuser", sPgpUser);
	Config.set("pgpmime", iPgpMime);
	Config.set("pgpsign", iPgpSign);
	Config.set("pgpusekeyring", iPgpUseKeyRing);
	Config.set("pgpusehttp", iPgpUseHttp);
	Config.set("pgp_encrypt_bcc", iPgpEncryptBcc);
	Config.set("pgpversion", iPgpVersion);

    return;
}

int get_pgpversion(FD_config_pgp * obj) {
	switch(fl_get_choice(obj->PGP_500)) {
		case 1:
			return 263;
	    case 3:
		    return 651;     
		case 4:
			return 95;
		default:
			return 500;
	}
}

int cancel_form_lookup_cb(FL_FORM * obj, void *data) {
	return FL_IGNORE;
}

unsigned long PGP_Lookup_PGPId(const char *name, FL_FORM * form) {
	int i = 0, keyids_size = 100, line, w, h;
	char buf[1024], *userid, geom[16], keyfile[255];
	proc_info pinfo;
	FILE *in;
	unsigned long keyid;
	struct _pgp_keylist *keylist;
	FL_OBJECT *obj;
	int pgpvers = Config.getInt("pgpversion", PGP_DEFAULT_VERS);
    string sPgpProg = Config.get("pgp", _PATH_PGP);

	if(form_lookup_pgpid)
		return 0;

	strcpy(keyfile, get_temp_file("pgpkeys"));
	if(pgpvers == 500)
		snprintf(buf, sizeof(buf), "%sk +batchmode +language=en -l", 
                            sPgpProg.c_str());
	if(pgpvers == 651)
		 /* +compatible=on is dirty hack to avoid total rewriting of the
	 "interpreter" down there. I'll do that later anyways */ 
		snprintf(buf, sizeof(buf), "%s +batchmode +compatible=on +language=en -kv",
                            sPgpProg.c_str());
	if(pgpvers == 95)
		snprintf(buf, sizeof(buf), "%s --batch --list-keys",
                            sPgpProg.c_str());
	if(pgpvers == 263)
		snprintf(buf, sizeof(buf), "%s +batchmode +language=en -kv",
                            sPgpProg.c_str());
	init_pinfo(&pinfo);
	pinfo.ofd = open(keyfile, O_WRONLY | O_CREAT, 00600);
	pinfo.wait = WAIT_BG;

	/* unset PGPPASSFD , otherwise it will hang! */
#ifdef  HAVE_UNSETENV
	unsetenv("PGPPASSFD");
#else
	Unsetenv("PGPPASSFD");
#endif

	if(exec_child(buf, &pinfo) == -1) {
		unlink(keyfile);
		return 0;
	}

	check_extprocs();
	/*
	 * This routine heavily depends on the format of the output of
	 * "pgp +language=en -kv" (2.6.3) or "pgpk +language=en -l" (5.0)
	 * 1. read all lines not beginning with "pub " (usually 7 lines)
	 */
	if((in = fopen(keyfile, "r")) == NULL) {
		display_msg(MSG_WARN, "PGP", "Can not read key list\n");
		unlink(keyfile);
		return 0;
	}

	do {
		if(!fgets(buf, 1024, in)) {
			display_msg(MSG_WARN, "PGP", "Invalid key list\n");
			fclose(in);
			unlink(keyfile);
			return 0;
		}
	} while(strncmp(buf, "pub ", 4) &&
			strncmp(buf, "sec  ", 5) && strncmp(buf, "sec+ ", 5));

	form_lookup_pgpid = create_form_Lookup_PGPId();
	fl_set_form_atclose(form_lookup_pgpid->Lookup_PGPId,
						cancel_form_lookup_cb, NULL);

	fl_freeze_form(form_lookup_pgpid->Lookup_PGPId);

	fl_set_object_label(form_lookup_pgpid->PGP_Lookup, name);
	fl_set_object_lstyle(form_lookup_pgpid->PGP_Lookup,
		charset_style_from_code(supp_charsets[def_charset].charset_code));
	fl_set_object_lsize(form_lookup_pgpid->PGP_Lookup,
						charset_size_from_code(supp_charsets[def_charset].charset_code));

	fl_set_object_label(form_lookup_pgpid->PGP_selected_id, "");
	fl_set_object_lstyle(form_lookup_pgpid->PGP_selected_id,
						 charset_style_from_code(supp_charsets[def_charset].charset_code));
	fl_set_object_lsize(form_lookup_pgpid->PGP_selected_id,
						charset_size_from_code(supp_charsets[def_charset].charset_code));

	fl_set_browser_fontstyle(form_lookup_pgpid->PGP_Lookup_Browser,
							 charset_style_from_code(supp_charsets[def_charset].charset_code));
	fl_set_browser_fontsize(form_lookup_pgpid->PGP_Lookup_Browser,
							charset_size_from_code(supp_charsets[def_charset].charset_code));
	fl_clear_browser(form_lookup_pgpid->PGP_Lookup_Browser);

	/*
	 * 2. interpret all following lines not beginning with a digit (this
	 *    is the last line!) as keys.
	 * Every key's id starts at column 11, the user id at column 31.
	 * Additional user id's belonging to the same key again start at
	 * column 31, but don't have a key id.
	 */
	keylist =
	(struct _pgp_keylist *) malloc(keyids_size *
								   sizeof(struct _pgp_keylist));
	keyid = 0;
	do {
		userid = NULL;
		if(pgpvers == 263 || pgpvers == 651) {
			if(buf[10] != ' ')
				keyid = strtoul(buf + 10, NULL, 16);
			if((userid = strchr(buf + 30, '\n')) != NULL)
				*userid = '\0';
			userid = buf + 30;
		}
		if(pgpvers == 500) {
			if(!strncmp(buf, "pub  ", 5) ||
			   !strncmp(buf, "sec  ", 5) || !strncmp(buf, "sec+ ", 5)) {
				keyid = 0;
				keyid = strtoul(buf + 10, NULL, 16);
				continue;
			}
			if(!strncmp(buf, "sub  ", 5))
				continue;
			if(!strncmp(buf, "uid  ", 5)) {
				if((userid = strchr(buf + 5, '\n')) != NULL)
					*userid = '\0';
				userid = buf + 5;
			} else
				keyid = 0;
		}

		if(pgpvers == 95) {
			keyid = 0;
			if(!strncmp(buf, "pub  ", 5)) {
				if((userid = strchr(buf + 31, '\n')) != NULL)
					*userid = '\0';
				userid = buf + 31;
				keyid = strtoul(buf + 11, NULL, 16);
			}
		}

		if(i == keyids_size) {
			keyids_size += keyids_size;
			keylist = (struct _pgp_keylist *) realloc(keylist,
													  keyids_size *
													  sizeof(struct
															 _pgp_keylist));
		}

		if(keylist == NULL)
			break;

		if((keyid == 0) || (userid == NULL))
			continue;

		keylist[i].userid = strdup(userid);
		keylist[i].keyid = keyid;
		i++;
	} while(fgets(buf, 1024, in) && !isdigit(buf[0]));

	fclose(in);
	unlink(keyfile);

	qsort(keylist, i, sizeof(struct _pgp_keylist),
		  (int (*)(const void *, const void *)) compare_pgpkeys);

	for(line = 0; line < i; line++)
		fl_add_browser_line(form_lookup_pgpid->PGP_Lookup_Browser,
							keylist[line].userid);

	fl_unfreeze_form(form_lookup_pgpid->Lookup_PGPId);
	fl_deactivate_form(form);
	Config.setFlags("pgpgeom", CF_NOTCHANGED);
	w = 350;
	h = 400;
	sscanf(Config.getString("pgpgeom", "").c_str(), "%d %d", &w, &h);
	fl_set_form_minsize(form_lookup_pgpid->Lookup_PGPId, 350, 400);
	fl_set_form_size(form_lookup_pgpid->Lookup_PGPId, w, h);
	fl_show_form(form_lookup_pgpid->Lookup_PGPId,
				 FL_PLACE_FREE, FL_FULLBORDER, "Lookup PGP Id");

	line = 0;
	for(;;) {
		obj = fl_do_forms();
		keyid = 0;
		if(obj == form_lookup_pgpid->PGP_Lookup_Browser) {
			if(
			  (line ==
			   fl_get_browser(form_lookup_pgpid->PGP_Lookup_Browser))
			  && (fl_get_timer(form_lookup_pgpid->PGP_Lookup_Timer) >
				  0.0)) {
				keyid = keylist[line - 1].keyid;
				break;
			}
			line = fl_get_browser(form_lookup_pgpid->PGP_Lookup_Browser);
			snprintf(buf, sizeof(buf), "0x%X",
					 (unsigned int) keylist[line - 1].keyid);
			fl_set_object_label(form_lookup_pgpid->PGP_selected_id, buf);
			fl_set_timer(form_lookup_pgpid->PGP_Lookup_Timer, 0.4);
		}
		if(obj == form_lookup_pgpid->PGP_Lookup_Ok) {
			keyid = line ? keylist[line - 1].keyid : 0;
			break;
		}
		if(obj == form_lookup_pgpid->PGP_Lookup_Cancel) {
			keyid = 0;
			break;
		}
	}

	for(line = 0; line < i; line++)
		free(keylist[line].userid);
	free(keylist);

	sprintf(geom, "%d %d", form_lookup_pgpid->Lookup_PGPId->w,
			form_lookup_pgpid->Lookup_PGPId->h);
	Config.set("pgpgeom", geom);
	fl_hide_form(form_lookup_pgpid->Lookup_PGPId);
	fl_free_form(form_lookup_pgpid->Lookup_PGPId);
	form_lookup_pgpid = NULL;
	fl_activate_form(form);
	return keyid;
}
