<commit_before>package ch.uzh.csg.paymentlib;

import java.nio.charset.Charset;
import java.util.Arrays;

import android.app.Activity;
import android.util.Log;
import ch.uzh.csg.mbps.customserialization.DecoderFactory;
import ch.uzh.csg.mbps.customserialization.InitMessagePayee;
import ch.uzh.csg.mbps.customserialization.PaymentRequest;
import ch.uzh.csg.mbps.customserialization.PaymentResponse;
import ch.uzh.csg.nfclib.CustomHostApduService;
import ch.uzh.csg.nfclib.CustomHostApduService2;
import ch.uzh.csg.nfclib.IMessageHandler;
import ch.uzh.csg.nfclib.NfcEvent;
import ch.uzh.csg.paymentlib.container.ServerInfos;
import ch.uzh.csg.paymentlib.container.UserInfos;
import ch.uzh.csg.paymentlib.exceptions.IllegalArgumentException;
import ch.uzh.csg.paymentlib.messages.PaymentError;
import ch.uzh.csg.paymentlib.messages.PaymentMessage;
import ch.uzh.csg.paymentlib.persistency.IPersistencyHandler;
import ch.uzh.csg.paymentlib.persistency.PersistedPaymentRequest;
import ch.uzh.csg.paymentlib.util.Config;

//TODO: javadoc
public class PaymentRequestHandler {
	
	public static final String TAG = "##NFC## PaymentRequestHandler";
	
	public static final byte[] ACK = new byte[] { (byte) 0xAC };
	
	private IPaymentEventHandler paymentEventHandler;
	private UserInfos userInfos;
	private ServerInfos serverInfos;
	private IUserPromptPaymentRequest userPrompt;
	private IPersistencyHandler persistencyHandler;
	private MessageHandler messageHandler;
	
	private int nofMessages = 0;
	private boolean aborted = false;
	
	public PaymentRequestHandler(Activity activity, IPaymentEventHandler paymentEventHandler, UserInfos userInfos, ServerInfos serverInfos, IUserPromptPaymentRequest userPrompt, IPersistencyHandler persistencyHandler) throws IllegalArgumentException {
		checkParameters(activity, paymentEventHandler, userInfos, serverInfos, userPrompt, persistencyHandler);
		
		this.paymentEventHandler = paymentEventHandler;
		this.userInfos = userInfos;
		this.serverInfos = serverInfos;
		this.userPrompt = userPrompt;
		this.persistencyHandler = persistencyHandler;
		this.messageHandler = new MessageHandler();
		
		CustomHostApduService c = new CustomHostApduService(activity, nfcEventHandler, messageHandler);
		CustomHostApduService2.init(c);
	}
	
	private void checkParameters(Activity activity, IPaymentEventHandler paymentEventHandler, UserInfos userInfos, ServerInfos serverInfos, IUserPromptPaymentRequest userPrompt, IPersistencyHandler persistencyHandler) throws IllegalArgumentException {
		if (activity == null)
			throw new IllegalArgumentException("The activity cannot be null.");
		
		if (paymentEventHandler == null)
			throw new IllegalArgumentException("The payment event handler cannot be null.");
		
		if (userInfos == null)
			throw new IllegalArgumentException("The user infos cannot be null.");
		
		if (serverInfos == null)
			throw new IllegalArgumentException("The server infos cannot be null.");
		
		if (userPrompt == null)
			throw new IllegalArgumentException("The user prompt cannot be null.");
		
		if (persistencyHandler == null)
			throw new IllegalArgumentException("The persistency handler cannot be null.");
	}
	
	private NfcEvent nfcEventHandler = new NfcEvent() {
		
		@Override
		public void handleMessage(Type event, Object object) {
			if (aborted)
				return;
			
			switch (event) {
			case INIT_FAILED:
			case FATAL_ERROR:
				aborted = true;
				paymentEventHandler.handleMessage(PaymentEvent.ERROR, null);
				break;
			case CONNECTION_LOST:
				nofMessages = 0;
				break;
			case INITIALIZED: //do nothing
				break;
			case MESSAGE_RECEIVED: //do nothing, handle in IMessageHandler
				break;
			case MESSAGE_SENT_HCE: //do nothing
				break;
			case MESSAGE_SENT:// do nothing, concerns only the NfcTransceiver
				break;
			default:
				break;
			}
		}
		
	};
	
	private byte[] getError(PaymentError err) {
		aborted = true;
		paymentEventHandler.handleMessage(PaymentEvent.ERROR, err);
		return new PaymentMessage().error().payload(new byte[] { err.getCode() }).bytes();
	}
	
	/*
	 * only for test purposes
	 */
	protected MessageHandler getMessageHandler() {
		return new MessageHandler();
	}
	
	/*
	 * only for test purposes
	 */
	protected NfcEvent getNfcEventHandler() {
		return nfcEventHandler;
	}
	
	protected class MessageHandler implements IMessageHandler {
		
		private PersistedPaymentRequest persistedPaymentRequest;
		private volatile boolean serverResponseArrived = false;


		public byte[] handleMessage(byte[] message) {
			Log.d(TAG, "got payment message: "+Arrays.toString(message));
			if (aborted)
				return null;
			
			nofMessages++;
			PaymentMessage pm = new PaymentMessage().bytes(message);
			if (pm.isError()) {
				try {
					PaymentError paymentError = PaymentError.getPaymentError(pm.payload()[0]);
					return getError(paymentError);
				} catch (Exception e) {
					Log.d(TAG, "exception", e);
					return getError(PaymentError.UNEXPECTED_ERROR);
				}
			}
			
			if (pm.isPayer()) {
				switch (nofMessages) {
				case 1:
					byte[] bytes = userInfos.getUsername().getBytes(Charset.forName("UTF-8"));
					return new PaymentMessage().payee().payload(bytes).bytes();
				case 2:
					serverResponseArrived = true;
					
					try {
						Log.d(TAG, "DBG1: "+Arrays.toString(pm.payload()));
						PaymentResponse paymentResponse = DecoderFactory.decode(PaymentResponse.class, pm.payload());
						boolean signatureValid = paymentResponse.verify(serverInfos.getPublicKey());
						if (!signatureValid) {
							Log.d(TAG, "exception sig not valid " + serverInfos.getPublicKey());
							return getError(PaymentError.UNEXPECTED_ERROR);
						} else {
							persistencyHandler.delete(persistedPaymentRequest);
							
							switch (paymentResponse.getStatus()) {
							case FAILURE:
								paymentEventHandler.handleMessage(PaymentEvent.ERROR, PaymentError.SERVER_REFUSED);
								break;
							case SUCCESS:
								paymentEventHandler.handleMessage(PaymentEvent.SUCCESS, paymentResponse);
								break;
							case DUPLICATE_REQUEST:
								paymentEventHandler.handleMessage(PaymentEvent.ERROR, PaymentError.DUPLICATE_REQUEST);
								break;
							}<commit_msg>Added timeout to PaymentRequestHandler use case payee init.<commit_after>package ch.uzh.csg.paymentlib;

import java.nio.charset.Charset;
import java.util.Arrays;

import android.app.Activity;
import android.util.Log;
import ch.uzh.csg.mbps.customserialization.DecoderFactory;
import ch.uzh.csg.mbps.customserialization.InitMessagePayee;
import ch.uzh.csg.mbps.customserialization.PaymentRequest;
import ch.uzh.csg.mbps.customserialization.PaymentResponse;
import ch.uzh.csg.nfclib.CustomHostApduService;
import ch.uzh.csg.nfclib.CustomHostApduService2;
import ch.uzh.csg.nfclib.IMessageHandler;
import ch.uzh.csg.nfclib.NfcEvent;
import ch.uzh.csg.paymentlib.container.ServerInfos;
import ch.uzh.csg.paymentlib.container.UserInfos;
import ch.uzh.csg.paymentlib.exceptions.IllegalArgumentException;
import ch.uzh.csg.paymentlib.messages.PaymentError;
import ch.uzh.csg.paymentlib.messages.PaymentMessage;
import ch.uzh.csg.paymentlib.persistency.IPersistencyHandler;
import ch.uzh.csg.paymentlib.persistency.PersistedPaymentRequest;
import ch.uzh.csg.paymentlib.util.Config;

//TODO: javadoc
public class PaymentRequestHandler {
	
	public static final String TAG = "##NFC## PaymentRequestHandler";
	
	public static final byte[] ACK = new byte[] { (byte) 0xAC };
	
	private IPaymentEventHandler paymentEventHandler;
	private UserInfos userInfos;
	private ServerInfos serverInfos;
	private IUserPromptPaymentRequest userPrompt;
	private IPersistencyHandler persistencyHandler;
	private MessageHandler messageHandler;
	
	private int nofMessages = 0;
	private boolean aborted = false;
	
	public PaymentRequestHandler(Activity activity, IPaymentEventHandler paymentEventHandler, UserInfos userInfos, ServerInfos serverInfos, IUserPromptPaymentRequest userPrompt, IPersistencyHandler persistencyHandler) throws IllegalArgumentException {
		checkParameters(activity, paymentEventHandler, userInfos, serverInfos, userPrompt, persistencyHandler);
		
		this.paymentEventHandler = paymentEventHandler;
		this.userInfos = userInfos;
		this.serverInfos = serverInfos;
		this.userPrompt = userPrompt;
		this.persistencyHandler = persistencyHandler;
		this.messageHandler = new MessageHandler();
		
		CustomHostApduService c = new CustomHostApduService(activity, nfcEventHandler, messageHandler);
		CustomHostApduService2.init(c);
	}
	
	private void checkParameters(Activity activity, IPaymentEventHandler paymentEventHandler, UserInfos userInfos, ServerInfos serverInfos, IUserPromptPaymentRequest userPrompt, IPersistencyHandler persistencyHandler) throws IllegalArgumentException {
		if (activity == null)
			throw new IllegalArgumentException("The activity cannot be null.");
		
		if (paymentEventHandler == null)
			throw new IllegalArgumentException("The payment event handler cannot be null.");
		
		if (userInfos == null)
			throw new IllegalArgumentException("The user infos cannot be null.");
		
		if (serverInfos == null)
			throw new IllegalArgumentException("The server infos cannot be null.");
		
		if (userPrompt == null)
			throw new IllegalArgumentException("The user prompt cannot be null.");
		
		if (persistencyHandler == null)
			throw new IllegalArgumentException("The persistency handler cannot be null.");
	}
	
	private NfcEvent nfcEventHandler = new NfcEvent() {
		
		@Override
		public void handleMessage(Type event, Object object) {
			if (aborted)
				return;
			
			switch (event) {
			case INIT_FAILED:
			case FATAL_ERROR:
				aborted = true;
				paymentEventHandler.handleMessage(PaymentEvent.ERROR, null);
				break;
			case CONNECTION_LOST:
				nofMessages = 0;
				break;
			case INITIALIZED: //do nothing
				break;
			case MESSAGE_RECEIVED: //do nothing, handle in IMessageHandler
				break;
			case MESSAGE_SENT_HCE: //do nothing
				break;
			case MESSAGE_SENT:// do nothing, concerns only the NfcTransceiver
				break;
			default:
				break;
			}
		}
		
	};
	
	private byte[] getError(PaymentError err) {
		aborted = true;
		paymentEventHandler.handleMessage(PaymentEvent.ERROR, err);
		return new PaymentMessage().error().payload(new byte[] { err.getCode() }).bytes();
	}
	
	/*
	 * only for test purposes
	 */
	protected MessageHandler getMessageHandler() {
		return new MessageHandler();
	}
	
	/*
	 * only for test purposes
	 */
	protected NfcEvent getNfcEventHandler() {
		return nfcEventHandler;
	}
	
	protected class MessageHandler implements IMessageHandler {
		
		private PersistedPaymentRequest persistedPaymentRequest;
		private volatile boolean serverResponseArrived = false;


		public byte[] handleMessage(byte[] message) {
			Log.d(TAG, "got payment message: "+Arrays.toString(message));
			if (aborted)
				return null;
			
			nofMessages++;
			PaymentMessage pm = new PaymentMessage().bytes(message);
			if (pm.isError()) {
				try {
					PaymentError paymentError = PaymentError.getPaymentError(pm.payload()[0]);
					return getError(paymentError);
				} catch (Exception e) {
					Log.d(TAG, "exception", e);
					return getError(PaymentError.UNEXPECTED_ERROR);
				}
			}
			
			if (pm.isPayer()) {
				switch (nofMessages) {
				case 1:
					byte[] bytes = userInfos.getUsername().getBytes(Charset.forName("UTF-8"));
					
					Thread t = new Thread(new TimeoutHandler());
					t.start();
					
					return new PaymentMessage().payee().payload(bytes).bytes();
				case 2:
					serverResponseArrived = true;
					
					try {
						Log.d(TAG, "DBG1: "+Arrays.toString(pm.payload()));
						PaymentResponse paymentResponse = DecoderFactory.decode(PaymentResponse.class, pm.payload());
						boolean signatureValid = paymentResponse.verify(serverInfos.getPublicKey());
						if (!signatureValid) {
							Log.d(TAG, "exception sig not valid " + serverInfos.getPublicKey());
							return getError(PaymentError.UNEXPECTED_ERROR);
						} else {
							persistencyHandler.delete(persistedPaymentRequest);
							
							switch (paymentResponse.getStatus()) {
							case FAILURE:
								paymentEventHandler.handleMessage(PaymentEvent.ERROR, PaymentError.SERVER_REFUSED);
								break;
							case SUCCESS:
								paymentEventHandler.handleMessage(PaymentEvent.SUCCESS, paymentResponse);
								break;
							case DUPLICATE_REQUEST:
								paymentEventHandler.handleMessage(PaymentEvent.ERROR, PaymentError.DUPLICATE_REQUEST);
								break;
							}
{'fraction_non_alphanumeric': 0.09333868378812199, 'fraction_numerical': 0.0016051364365971107, 'mean_word_length': 4.443861948449104, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3025071', 'n_tokens_mistral': 4302, 'n_tokens_neox': 3588, 'n_words': 841}