From ddec2c5c7b7842536d6d705c2de20f96e16c8aa8 Mon Sep 17 00:00:00 2001
From: rsanchez <rsanchez@curisit.net>
Date: Wed, 22 Oct 2014 17:38:57 +0000
Subject: [PATCH] #2021 feature - Added blocked request table and refactoring License actions

---
 securis/src/main/java/net/curisit/securis/services/ApiResource.java                       |   86 +++--
 securis/src/main/java/net/curisit/securis/db/BlockedRequest.java                          |   85 ++++++
 securis/src/main/java/net/curisit/securis/db/User.java                                    |   10 
 securis/src/main/resources/static/js/catalogs.json                                        |    6 
 securis/src/main/java/net/curisit/securis/db/PackStatus.java                              |   18 
 securis/src/main/java/net/curisit/securis/db/Pack.java                                    |   19 
 securis/src/main/java/net/curisit/securis/db/License.java                                 |   27 -
 securis/src/main/java/net/curisit/securis/SeCurisServer.java                              |    4 
 securis/pom.xml                                                                           |   12 
 securis/src/main/resources/static/licenses.html                                           |   43 +-
 securis/src/main/java/net/curisit/securis/db/LicenseStatus.java                           |   40 ++
 securis/src/main/java/net/curisit/securis/db/LicenseHistory.java                          |    2 
 securis/src/main/java/net/curisit/securis/services/LicenseResource.java                   |  227 ++++++++++++++-
 securis/src/main/java/net/curisit/securis/utils/EmailManager.java                         |  196 ++++++++++++++
 securis/src/main/resources/securis-server.properties                                      |    5 
 securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java |   20 +
 securis/src/main/resources/db/schema.sql                                                  |   16 
 securis/src/main/java/net/curisit/securis/LicenseGenerator.java                           |   18 
 18 files changed, 699 insertions(+), 135 deletions(-)

diff --git a/securis/pom.xml b/securis/pom.xml
index 7b638ff..1f5b23b 100644
--- a/securis/pom.xml
+++ b/securis/pom.xml
@@ -59,13 +59,23 @@
   	<dependency>
   		<groupId>net.curisit</groupId>
   		<artifactId>securis-client</artifactId>
-  		<version>0.9.8-SNAPSHOT</version>
+  		<version>1.0.1-SNAPSHOT</version>
   	</dependency>
   	<dependency>
   		<groupId>org.hibernate</groupId>
   		<artifactId>hibernate-c3p0</artifactId>
   		<version>4.3.6.Final</version>
   	</dependency>
+  	<dependency>
+  		<groupId>org.apache.httpcomponents</groupId>
+  		<artifactId>httpclient</artifactId>
+  		<version>4.4-beta1</version>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.apache.httpcomponents</groupId>
+  		<artifactId>httpmime</artifactId>
+  		<version>4.4-beta1</version>
+  	</dependency>
   </dependencies>
 	  <build>
 		<plugins>
diff --git a/securis/src/main/java/net/curisit/securis/LicenseGenerator.java b/securis/src/main/java/net/curisit/securis/LicenseGenerator.java
index 40954f4..b614e4d 100644
--- a/securis/src/main/java/net/curisit/securis/LicenseGenerator.java
+++ b/securis/src/main/java/net/curisit/securis/LicenseGenerator.java
@@ -11,7 +11,6 @@
 import java.security.Signature;
 import java.security.SignatureException;
 import java.security.spec.InvalidKeySpecException;
-import java.text.MessageFormat;
 import java.util.Date;
 import java.util.Map;
 import java.util.TreeMap;
@@ -57,13 +56,11 @@
      * @return
      * @throws SeCurisException
      */
-    public LicenseBean generateLicense(RequestBean req, Map<String, Object> metadata, Date expirationDate, String licenseType, String licenseCode)
+    public LicenseBean generateLicense(RequestBean req, Map<String, Object> metadata, Date expirationDate, String licenseCode, String appName)
             throws SeCurisException {
-        LOG.info(MessageFormat.format("Generating license: MAC: {0}, Customer code: {1}, AppCode: {2}", req.getMacAddresses(), req.getCustomerCode(),
-                req.getAppCode()));
+        LOG.debug("Generating license: MAC: {}, Customer code: {}, AppName: {}", req.getMacAddresses(), req.getCustomerCode(), appName);
         LicenseBean license = new LicenseBean(req);
-        license.setLicenseType(licenseType);
-        license.setLicenseCode(licenseCode);
+        license.setAppName(appName);
         license.setExpirationDate(expirationDate);
         license.setMetadata(metadata);
         sign(license);
@@ -82,7 +79,7 @@
         SignedLicenseBean signedLic = new SignedLicenseBean(license);
         byte[] json;
         try {
-            json = JsonUtils.toJSON(signedLic, true).getBytes("utf-8");
+            json = JsonUtils.toPrettyJSON(signedLic).getBytes("utf-8");
             Files.write(Paths.get(file.toURI()), json, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
         } catch (UnsupportedEncodingException e) {
             LOG.error("Error creating json doc from license: " + license, e);
@@ -97,7 +94,6 @@
     }
 
     /**
-     * TODO: This method should be removed from client code.
      * 
      * @param licBean
      * @return
@@ -113,7 +109,8 @@
         Signature signature;
         try {
             signature = Signature.getInstance(SignatureHelper.SIGNATURE_GENERATION_ALGORITHM);
-            signature.initSign(sh.generatePrivateKey(new File(System.getProperty("user.home") + File.separator + ".SeCuris" + File.separator + "keys" + File.separator + "securis_private_key.pkcs8")));
+            signature.initSign(sh.generatePrivateKey(new File(System.getProperty("user.home") + File.separator + ".SeCuris" + File.separator + "keys"
+                    + File.separator + "securis_private_key.pkcs8")));
 
             sh.prepareSignature(signature, licBean);
 
@@ -147,7 +144,7 @@
         metadata.put("timeThreshold", 0);
         metadata.put("datasetPrefix", "BP");
         metadata.put("extendedMode", true);
-        
+
         Date expirationDate = new Date(new Date().getTime() + (1000L * 3600 * 24 * 365 * 10));
         LicenseBean lic = LicenseGenerator.getInstance().generateLicense(req, metadata, expirationDate, "CI-01", "LIC-CURISTEC-0001");
         LicenseGenerator.getInstance().save(lic, new File("/Users/cproberto/Desktop/AxelLicCI.lic"));
@@ -156,4 +153,3 @@
 
     }
 }
-
diff --git a/securis/src/main/java/net/curisit/securis/SeCurisServer.java b/securis/src/main/java/net/curisit/securis/SeCurisServer.java
index 1e7bb8d..8b28615 100644
--- a/securis/src/main/java/net/curisit/securis/SeCurisServer.java
+++ b/securis/src/main/java/net/curisit/securis/SeCurisServer.java
@@ -65,6 +65,10 @@
         CONSOLE.info("    $ ./securis-server.sh {start|stop}");
     }
     
+    private void testMail() {
+        
+    }
+    
     public static void main(String[] args) throws Exception {
         String command; 
         if (args.length > 0) {
diff --git a/securis/src/main/java/net/curisit/securis/db/BlockedRequest.java b/securis/src/main/java/net/curisit/securis/db/BlockedRequest.java
new file mode 100644
index 0000000..2233432
--- /dev/null
+++ b/securis/src/main/java/net/curisit/securis/db/BlockedRequest.java
@@ -0,0 +1,85 @@
+package net.curisit.securis.db;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+/**
+ * Entity implementation class for Entity: pack
+ * 
+ */
+@JsonAutoDetect
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+@Entity
+@Table(name = "blocked_request")
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class BlockedRequest implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    private String hash;
+
+    @Column(name = "request_data")
+    @JsonProperty("request_data")
+    private String requestData;
+
+    @Column(name = "creation_timestamp")
+    @JsonProperty("creation_timestamp")
+    private Date creationTimestamp;
+
+    @JsonIgnore
+    @ManyToOne
+    @JoinColumn(name = "blocked_by")
+    private User blockedBy;
+
+    public Date getCreationTimestamp() {
+        return creationTimestamp;
+    }
+
+    public void setCreationTimestamp(Date creationTimestamp) {
+        this.creationTimestamp = creationTimestamp;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof BlockedRequest))
+            return false;
+        BlockedRequest other = (BlockedRequest) obj;
+        return hash.equals(other.hash);
+    }
+
+    @Override
+    public int hashCode() {
+
+        return (hash == null ? 0 : hash.hashCode());
+    }
+
+    public String getRequestData() {
+        return requestData;
+    }
+
+    public void setRequestData(String requestData) {
+        this.requestData = requestData;
+    }
+
+    public User getBlockedBy() {
+        return blockedBy;
+    }
+
+    public void setBlockedBy(User blockedBy) {
+        this.blockedBy = blockedBy;
+    }
+}
diff --git a/securis/src/main/java/net/curisit/securis/db/License.java b/securis/src/main/java/net/curisit/securis/db/License.java
index 869574e..239797c 100644
--- a/securis/src/main/java/net/curisit/securis/db/License.java
+++ b/securis/src/main/java/net/curisit/securis/db/License.java
@@ -63,7 +63,7 @@
     @JoinColumn(name = "canceled_by")
     private User canceledBy;
 
-    private int status;
+    private LicenseStatus status;
 
     @Column(name = "full_name")
     @JsonProperty("full_name")
@@ -186,11 +186,11 @@
         }
     }
 
-    public int getStatus() {
+    public LicenseStatus getStatus() {
         return status;
     }
 
-    public void setStatus(int status) {
+    public void setStatus(LicenseStatus status) {
         this.status = status;
     }
 
@@ -289,21 +289,14 @@
     }
 
     public static class Status {
-        public static final int CREATED = 1;
-        public static final int REQUESTED = 2;
-        public static final int PREACTIVE = 3;
-        public static final int ACTIVE = 4;
-        public static final int EXPIRED = 5;
-        public static final int CANCELED = 6;
-        public static final int DELETED = 7;
 
         private static final Map<Integer, List<Integer>> transitions = Utils.createMap( //
-                Action.REQUEST, Arrays.asList(CREATED, REQUESTED), //
-                Action.ACTIVATION, Arrays.asList(REQUESTED, PREACTIVE, EXPIRED), //
-                Action.SEND, Arrays.asList(ACTIVE, PREACTIVE), //
-                Action.DOWNLOAD, Arrays.asList(ACTIVE, PREACTIVE), //
-                Action.CANCEL, Arrays.asList(ACTIVE, PREACTIVE, REQUESTED, EXPIRED), //
-                Action.DELETE, Arrays.asList(CANCELED, CREATED) //
+                Action.REQUEST, Arrays.asList(LicenseStatus.CREATED, LicenseStatus.REQUESTED), //
+                Action.ACTIVATION, Arrays.asList(LicenseStatus.REQUESTED, LicenseStatus.PRE_ACTIVE, LicenseStatus.EXPIRED), //
+                Action.SEND, Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE), //
+                Action.DOWNLOAD, Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE), //
+                Action.CANCEL, Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE, LicenseStatus.REQUESTED, LicenseStatus.EXPIRED), //
+                Action.DELETE, Arrays.asList(LicenseStatus.CANCELLED, LicenseStatus.CREATED) //
 
                 );
 
@@ -315,7 +308,7 @@
          * @param newStatus
          * @return
          */
-        public static boolean isActionValid(Integer action, Integer currentStatus) {
+        public static boolean isActionValid(Integer action, LicenseStatus currentStatus) {
             List<Integer> validStatuses = transitions.get(currentStatus);
 
             return validStatuses != null && validStatuses.contains(currentStatus);
diff --git a/securis/src/main/java/net/curisit/securis/db/LicenseHistory.java b/securis/src/main/java/net/curisit/securis/db/LicenseHistory.java
index 7f0e549..63590f5 100644
--- a/securis/src/main/java/net/curisit/securis/db/LicenseHistory.java
+++ b/securis/src/main/java/net/curisit/securis/db/LicenseHistory.java
@@ -111,8 +111,10 @@
         public static final String ADD_REQUEST = "request";
         public static final String SEND = "send";
         public static final String MODIFY = "modify";
+        public static final String DOWNLOAD = "download";
         public static final String ACTIVATE = "activate";
         public static final String CANCEL = "cancel";
+        public static final String BLOCK = "block";
         public static final String DELETE = "delete";
     }
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/LicenseStatus.java b/securis/src/main/java/net/curisit/securis/db/LicenseStatus.java
new file mode 100644
index 0000000..ff8b51f
--- /dev/null
+++ b/securis/src/main/java/net/curisit/securis/db/LicenseStatus.java
@@ -0,0 +1,40 @@
+package net.curisit.securis.db;
+
+import org.codehaus.jackson.annotate.JsonCreator;
+import org.codehaus.jackson.annotate.JsonValue;
+
+/**
+ * Contains the possible license statuses. For further details:
+ * https://redmine.curistec.com/projects/securis/wiki/LicensesServerManagement
+ * 
+ * @author rob
+ */
+public enum LicenseStatus {
+    CREATED("CR"), REQUESTED("RE"), ACTIVE("AC"), PRE_ACTIVE("PA"), EXPIRED("EX"), CANCELLED("CA");
+
+    private final String code;
+
+    LicenseStatus(String code) {
+        this.code = code;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    @JsonCreator
+    public static LicenseStatus valueFromCode(String code) {
+        for (LicenseStatus ps : LicenseStatus.values()) {
+            if (ps.code.equals(code)) {
+                return ps;
+            }
+        }
+        return null;
+    }
+
+    @JsonValue
+    public String getName() {
+
+        return this.code;
+    }
+}
diff --git a/securis/src/main/java/net/curisit/securis/db/Pack.java b/securis/src/main/java/net/curisit/securis/db/Pack.java
index d8f6df9..dcc977e 100644
--- a/securis/src/main/java/net/curisit/securis/db/Pack.java
+++ b/securis/src/main/java/net/curisit/securis/db/Pack.java
@@ -74,7 +74,7 @@
     @Column(name = "num_licenses")
     @JsonProperty("num_licenses")
     private int numLicenses;
-    
+
     @Column(name = "init_valid_date")
     @JsonProperty("init_valid_date")
     private Date initValidDate;
@@ -155,7 +155,7 @@
         }
         int num = 0;
         for (License lic : licenses) {
-            if (lic.getStatus() == License.Status.ACTIVE) {
+            if (lic.getStatus() == LicenseStatus.ACTIVE || lic.getStatus() == LicenseStatus.PRE_ACTIVE) {
                 num++;
             }
         }
@@ -176,7 +176,7 @@
         }
         int num = 0;
         for (License lic : licenses) {
-            if (lic.getStatus() != License.Status.CANCELED) {
+            if (lic.getStatus() != LicenseStatus.CANCELLED) {
                 num++;
             }
         }
@@ -190,7 +190,7 @@
      */
     @JsonProperty("num_available")
     public int getNumAvailables() {
-        return numLicenses - getNumCreations();
+        return numLicenses - getNumActivations();
     }
 
     @JsonProperty("organization_name")
@@ -250,11 +250,12 @@
 
     @JsonProperty("created_by_name")
     public String getCreatedByname() {
-        return createdBy == null ? null : String.format("%s %s (%s)", createdBy.getFirstName(), createdBy.getLastName() != null ? createdBy.getLastName() : "", createdBy.getUsername());
+        return createdBy == null ? null : String.format("%s %s (%s)", createdBy.getFirstName(),
+                createdBy.getLastName() != null ? createdBy.getLastName() : "", createdBy.getUsername());
     }
 
     @JsonProperty("licensetype_code")
-    public String getLicenseTypcode() {
+    public String getLicenseTypeCode() {
         return licenseType == null ? null : licenseType.getCode();
     }
 
@@ -310,13 +311,13 @@
     public boolean equals(Object obj) {
         if (!(obj instanceof Pack))
             return false;
-        Pack other = (Pack)obj; 
+        Pack other = (Pack) obj;
         return id.equals(other.id);
     }
-    
+
     @Override
     public int hashCode() {
-     
+
         return (id == null ? 0 : id.hashCode());
     }
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/PackStatus.java b/securis/src/main/java/net/curisit/securis/db/PackStatus.java
index b5a436a..bd05a69 100644
--- a/securis/src/main/java/net/curisit/securis/db/PackStatus.java
+++ b/securis/src/main/java/net/curisit/securis/db/PackStatus.java
@@ -3,17 +3,25 @@
 import org.codehaus.jackson.annotate.JsonCreator;
 import org.codehaus.jackson.annotate.JsonValue;
 
+/**
+ * Contains the possible pack statuses. For further details:
+ * https://redmine.curistec.com/projects/securis/wiki/LicensesServerManagement
+ * 
+ * @author rob
+ */
 public enum PackStatus {
-    PENDING("PE"), ACTIVE("AC"),  ON_HOLD("OH"),  EXPIRED("EX"),  CANCELLED("CA");
-    
+    CREATED("CR"), ACTIVE("AC"), ON_HOLD("OH"), EXPIRED("EX"), CANCELLED("CA");
+
     private final String code;
-    PackStatus(String code ) {
+
+    PackStatus(String code) {
         this.code = code;
     }
+
     public String getCode() {
         return code;
     }
-    
+
     @JsonCreator
     public static PackStatus valueFromCode(String code) {
         for (PackStatus ps : PackStatus.values()) {
@@ -26,7 +34,7 @@
 
     @JsonValue
     public String getName() {
-        
+
         return this.code;
     }
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/User.java b/securis/src/main/java/net/curisit/securis/db/User.java
index ea08a53..0899dad 100644
--- a/securis/src/main/java/net/curisit/securis/db/User.java
+++ b/securis/src/main/java/net/curisit/securis/db/User.java
@@ -67,6 +67,8 @@
 
     private String lang;
 
+    private String email;
+
     @JsonIgnore
     @ManyToMany
     @JoinTable(name = "user_organization", //
@@ -223,6 +225,14 @@
         }
     }
 
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
     public static class Rol {
         public static final int ADVANCE = 0x01;
         public static final int ADMIN = 0x02;
diff --git a/securis/src/main/java/net/curisit/securis/services/ApiResource.java b/securis/src/main/java/net/curisit/securis/services/ApiResource.java
index 7c843d0..23913e0 100644
--- a/securis/src/main/java/net/curisit/securis/services/ApiResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/ApiResource.java
@@ -28,8 +28,6 @@
 import net.curisit.securis.beans.SignedLicenseBean;
 import net.curisit.securis.beans.StatusBean;
 import net.curisit.securis.db.License;
-import net.curisit.securis.db.LicenseType;
-import net.curisit.securis.db.Pack;
 import net.curisit.securis.security.BasicSecurityContext;
 import net.curisit.securis.security.Securable;
 import net.curisit.securis.services.exception.SeCurisServiceException;
@@ -96,9 +94,10 @@
         return Response.ok(status).build();
     }
 
-
     /**
-     * Request a new license file based in a RequestBean object sent as parameter 
+     * Request a new license file based in a RequestBean object sent as
+     * parameter
+     * 
      * @param mpfdi
      * @param bsc
      * @return
@@ -108,17 +107,18 @@
     @POST
     @Path("/request")
     @Consumes(MediaType.APPLICATION_JSON)
-    //TODO: Enable this: @Securable
+    // TODO: Enable this: @Securable
     @Produces({
         MediaType.APPLICATION_JSON
     })
     @Transactional
-    public Response createFromRequest(RequestBean request, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException, SeCurisException {
+    public Response createFromRequest(RequestBean request, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException,
+            SeCurisException {
         LOG.info("Request to get license: {}", request);
 
         Map<String, Object> metadata = getLicenseMetadata(request);
         License licDB = getLicenseData(request);
-        
+
         Date expirationDate = licDB.getExpirationDate();
         String licenseTypeCode = licDB.getPack().getLicenseType().getCode();
         String licenseCode = licDB.getCode();
@@ -126,9 +126,10 @@
         SignedLicenseBean signedLic = new SignedLicenseBean(lic);
         return Response.ok(signedLic).build();
     }
-    
+
     /**
      * Returns a License file in JSON format from an uploaded Request file
+     * 
      * @param mpfdi
      * @param bsc
      * @return
@@ -145,12 +146,14 @@
     })
     @Transactional
     @SuppressWarnings("unchecked")
-    public Response createFromRequestFile(MultipartFormDataInput mpfdi, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException, SeCurisException {
+    public Response createFromRequestFile(MultipartFormDataInput mpfdi, @Context BasicSecurityContext bsc) throws IOException,
+            SeCurisServiceException, SeCurisException {
         RequestBean req = new RequestBean();
-        req.setAppCode(mpfdi.getFormDataPart("appCode", String.class, null));
+        req.setPackCode(mpfdi.getFormDataPart("packCode", String.class, null));
+        req.setLicenseTypeCode(mpfdi.getFormDataPart("licenseTypeCode", String.class, null));
+        req.setCustomerCode(mpfdi.getFormDataPart("customerCode", String.class, null));
         req.setArch(mpfdi.getFormDataPart("arch", String.class, null));
         req.setCrcLogo(mpfdi.getFormDataPart("crcLogo", String.class, null));
-        req.setCustomerCode(mpfdi.getFormDataPart("customerCode", String.class, null));
         req.setMacAddresses(mpfdi.getFormDataPart("macAddresses", List.class, null));
         req.setOsName(mpfdi.getFormDataPart("osName", String.class, null));
 
@@ -170,12 +173,13 @@
     @POST
     @Path("/renew")
     @Consumes(MediaType.APPLICATION_JSON)
-    //TODO: Enable this: @Securable
+    // TODO: Enable this: @Securable
     @Produces({
         MediaType.APPLICATION_JSON
     })
     @Transactional
-    public Response renewFromPreviousLicense(LicenseBean previousLic, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException, SeCurisException {
+    public Response renewFromPreviousLicense(LicenseBean previousLic, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException,
+            SeCurisException {
         LOG.info("Renew license: {}", previousLic);
 
         if (previousLic.getExpirationDate().after(DateUtils.addMonths(new Date(), 1))) {
@@ -184,16 +188,17 @@
 
         Map<String, Object> metadata = getLicenseMetadata(previousLic);
         License licDB = getLicenseData(previousLic);
-        
+
         Date expirationDate = licDB.getExpirationDate();
         String licenseTypeCode = licDB.getPack().getLicenseType().getCode();
         String licenseCode = licDB.getCode();
         LicenseBean lic = licenseGenerator.generateLicense(previousLic, metadata, expirationDate, licenseTypeCode, licenseCode);
         return Response.ok(lic).build();
     }
-    
+
     /**
      * Returns a new License file in JSON format based in a previous license
+     * 
      * @param mpfdi
      * @param bsc
      * @return
@@ -210,12 +215,15 @@
     })
     @Transactional
     @SuppressWarnings("unchecked")
-    public Response renewFromLicenseFile(MultipartFormDataInput mpfdi, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException, SeCurisException {
+    public Response renewFromLicenseFile(MultipartFormDataInput mpfdi, @Context BasicSecurityContext bsc) throws IOException,
+            SeCurisServiceException, SeCurisException {
         LicenseBean lic = new LicenseBean();
-        // TODO: Add more license parameters
-        lic.setAppCode(mpfdi.getFormDataPart("appCode", String.class, null));
+
+        lic.setAppName(mpfdi.getFormDataPart("appName", String.class, null));
         lic.setArch(mpfdi.getFormDataPart("arch", String.class, null));
         lic.setCrcLogo(mpfdi.getFormDataPart("crcLogo", String.class, null));
+        lic.setPackCode(mpfdi.getFormDataPart("packCode", String.class, null));
+        lic.setLicenseTypeCode(mpfdi.getFormDataPart("licenseCode", String.class, null));
         lic.setCustomerCode(mpfdi.getFormDataPart("customerCode", String.class, null));
         lic.setMacAddresses(mpfdi.getFormDataPart("macAddresses", List.class, null));
         lic.setOsName(mpfdi.getFormDataPart("osName", String.class, null));
@@ -227,46 +235,50 @@
 
         return createFromRequest(lic, bsc);
     }
-    
-    
-    
+
     private License getLicenseData(RequestBean req) throws SeCurisException {
-        // TODO: The dummy expiration date is temporal, this info should be read from DB
+        // TODO: The dummy expiration date is temporal, this info should be read
+        // from DB
         License lic = new License();
-        lic.setExpirationDate(new Date(new Date().getTime() + (1000L * 3600 * 24 * 30 )));
-        lic.setCode(req.getAppCode() + "-LIC-INTERNAL");
-        LicenseType lt = new LicenseType();
-        lt.setCode("TYPE-" + req.getAppCode());
-        Pack pack = new Pack();
-        pack.setLicenseType(lt);
-        lic.setPack(pack);
+        // TODO: IMPLEMENT LICENSE GENERATION
+
+        // lic.setExpirationDate(new Date(new Date().getTime() + (1000L * 3600 *
+        // 24 * 30)));
+        // lic.setCode(req.getPackCode() + "-LIC-INTERNAL");
+        // LicenseType lt = new LicenseType();
+        // lt.setCode("TYPE-" + req.getAppCode());
+        // Pack pack = new Pack();
+        // pack.setLicenseType(lt);
+        // lic.setPack(pack);
         return lic;
     }
-        
+
     /**
      * Extract the corresponding metadata for the Request license given
+     * 
      * @param req
      * @return
      * @throws SeCurisException
      */
     @SuppressWarnings("unchecked")
     private Map<String, Object> getLicenseMetadata(RequestBean req) throws SeCurisException {
-        // TODO: The dummy metadata file is temporal, this info should be read from DB
-        File dummyMetadata =  new File(System.getProperty("user.home") + File.separator + ".SeCuris" + File.separator + "dummy_metadata.json");
+        // TODO: The dummy metadata file is temporal, this info should be read
+        // from DB
+        File dummyMetadata = new File(System.getProperty("user.home") + File.separator + ".SeCuris" + File.separator + "dummy_metadata.json");
         Map<String, Object> metadata = null;
         try {
             String metadataJson = IOUtils.toString(dummyMetadata.toURI());
-            metadata = (Map<String, Object>)JsonUtils.json2map(metadataJson).get(req.getAppCode());
-            if (metadata == null)
-                throw new SeCurisException("App code in request is unknown: " + req.getAppCode());
+            metadata = (Map<String, Object>) JsonUtils.json2map(metadataJson).get(req.getLicenseTypeCode());
+            if (metadata == null) {
+                throw new SeCurisException("App code in request is unknown: " + req.getLicenseTypeCode());
+            }
             metadata = new TreeMap<>(metadata);
         } catch (IOException e) {
             LOG.error("Error reading dummy metadata file", e);
             throw new SeCurisException("Error reading dummy metadata file");
         }
-        
+
         return metadata;
     }
 
-        
 }
diff --git a/securis/src/main/java/net/curisit/securis/services/LicenseResource.java b/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
index 4032651..2930626 100644
--- a/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
@@ -1,11 +1,15 @@
 package net.curisit.securis.services;
 
 import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
+import java.nio.file.Files;
+import java.text.MessageFormat;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.TreeMap;
+import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Provider;
@@ -13,6 +17,8 @@
 import javax.persistence.TypedQuery;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
@@ -27,16 +33,26 @@
 
 import net.curisit.integrity.commons.JsonUtils;
 import net.curisit.integrity.commons.Utils;
+import net.curisit.integrity.exception.CurisException;
 import net.curisit.securis.DefaultExceptionHandler;
+import net.curisit.securis.LicenseGenerator;
 import net.curisit.securis.SeCurisException;
+import net.curisit.securis.beans.LicenseBean;
 import net.curisit.securis.beans.RequestBean;
+import net.curisit.securis.beans.SignedLicenseBean;
+import net.curisit.securis.db.Application;
 import net.curisit.securis.db.License;
 import net.curisit.securis.db.LicenseHistory;
+import net.curisit.securis.db.LicenseStatus;
 import net.curisit.securis.db.Pack;
+import net.curisit.securis.db.PackMetadata;
 import net.curisit.securis.db.User;
 import net.curisit.securis.security.BasicSecurityContext;
 import net.curisit.securis.security.Securable;
 import net.curisit.securis.services.exception.SeCurisServiceException;
+import net.curisit.securis.services.exception.SeCurisServiceException.ErrorCodes;
+import net.curisit.securis.utils.EmailManager;
+import net.curisit.securis.utils.Params;
 import net.curisit.securis.utils.TokenHelper;
 
 import org.apache.commons.io.IOUtils;
@@ -60,7 +76,13 @@
     TokenHelper tokenHelper;
 
     @Inject
+    EmailManager emailManager;
+
+    @Inject
     Provider<EntityManager> emProvider;
+
+    @Inject
+    LicenseGenerator licenseGenerator;
 
     /**
      * 
@@ -138,11 +160,16 @@
             LOG.error("License with id {} is not active, so It can not downloaded", licId, bsc.getUserPrincipal());
             throw new SeCurisServiceException(Status.FORBIDDEN.getStatusCode(), "License is not active, so It can not be downloaded");
         }
-        return Response.ok(lic.getLicenseData()).build();
+        em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.DOWNLOAD));
+        return Response
+                .ok(lic.getLicenseData())
+                .header("Content-Disposition",
+                        String.format("attachment; filename=\"%s\"", lic.getPack().getLicenseType().getApplication().getLicenseFilename())).build();
     }
 
     /**
      * Activate the given license
+     * 
      * @param licId
      * @param bsc
      * @return
@@ -168,8 +195,9 @@
                     + " can not be activated from the current license status");
         }
 
-        lic.setStatus(License.Status.ACTIVE);
+        lic.setStatus(LicenseStatus.ACTIVE);
         lic.setModificationTimestamp(new Date());
+
         em.persist(lic);
         User user = getUser(bsc.getUserPrincipal().getName(), em);
         em.persist(createLicenseHistoryAction(lic, user, LicenseHistory.Actions.ACTIVATE));
@@ -178,6 +206,7 @@
 
     /**
      * Send license file by email to the organization
+     * 
      * @param licId
      * @param bsc
      * @return
@@ -192,13 +221,34 @@
     @Produces({
         MediaType.APPLICATION_JSON
     })
-    public Response send(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
+    public Response send(@PathParam("licId") Integer licId, @DefaultValue("false") @FormParam("add_cc") Boolean addCC,
+            @Context BasicSecurityContext bsc) throws SeCurisServiceException, SeCurisException {
 
         EntityManager em = emProvider.get();
         License lic = getCurrentLicense(licId, bsc, em);
+        Application app = lic.getPack().getLicenseType().getApplication();
+        File licFile = null;
+        if (lic.getLicenseData() == null) {
+            throw new SeCurisServiceException(Status.NOT_FOUND.getStatusCode(), "There is no license file available");
+        }
 
         User user = getUser(bsc.getUserPrincipal().getName(), em);
-        // TODO: Send mail with lic file
+        try {
+            String subject = MessageFormat.format(Params.get(Params.KEYS.EMAIL_LIC_DEFAULT_SUBJECT), lic.getPack().getAppName());
+            String email_tpl = IOUtils.toString(this.getClass().getResourceAsStream("/lic_email_template.en"));
+            String body = MessageFormat.format(email_tpl, lic.getFullName(), app.getName());
+            licFile = createTemporaryLicenseFile(lic, app.getLicenseFilename());
+
+            emailManager.sendEmail(subject, body, lic.getEmail(), addCC ? user.getEmail() : null, licFile);
+        } catch (IOException e) {
+            LOG.error("Error creating temporary license file", e);
+            throw new SeCurisServiceException(Status.NOT_FOUND.getStatusCode(), "There is no license file available");
+        } finally {
+            if (licFile != null) {
+                licFile.delete();
+            }
+        }
+
         lic.setModificationTimestamp(new Date());
         em.persist(lic);
         em.persist(createLicenseHistoryAction(lic, user, LicenseHistory.Actions.SEND, "Email sent to: " + lic.getEmail()));
@@ -207,6 +257,7 @@
 
     /**
      * Cancel given license
+     * 
      * @param licId
      * @param bsc
      * @return
@@ -221,7 +272,8 @@
     @Produces({
         MediaType.APPLICATION_JSON
     })
-    public Response cancel(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
+    public Response cancel(@PathParam("licId") Integer licId, @FormParam("reason") String reason, @Context BasicSecurityContext bsc)
+            throws SeCurisServiceException {
 
         EntityManager em = emProvider.get();
         License lic = getCurrentLicense(licId, bsc, em);
@@ -232,12 +284,17 @@
                     + " can not be canceled from the current license status");
         }
 
-        lic.setStatus(License.Status.CANCELED);
+        if (reason == null && (lic.getStatus() == LicenseStatus.ACTIVE || lic.getStatus() == LicenseStatus.PRE_ACTIVE)) {
+            LOG.error("To cancel an active License we need a reason, lic ID: {}, user: {}", lic.getId(), bsc.getUserPrincipal().getName());
+            throw new SeCurisServiceException(Status.FORBIDDEN.getStatusCode(), "Active license with id " + licId
+                    + " can not be canceled without a reason");
+        }
+
+        lic.setStatus(LicenseStatus.CANCELLED);
         lic.setModificationTimestamp(new Date());
         em.persist(lic);
 
-        User user = getUser(bsc.getUserPrincipal().getName(), em);
-        em.persist(createLicenseHistoryAction(lic, user, LicenseHistory.Actions.CANCEL));
+        em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.CANCEL, "Cancelation reason: " + reason));
         return Response.ok(lic).build();
     }
 
@@ -250,39 +307,102 @@
     })
     @Transactional
     public Response create(License lic, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
-        LOG.info("Creating new license from create()");
         EntityManager em = emProvider.get();
         Pack pack = null;
         if (lic.getPackId() != null) {
             pack = em.find(Pack.class, lic.getPackId());
-            if (pack == null) {
-                LOG.error("License pack with id {} not found in DB", lic.getPackId());
-                return Response.status(Status.NOT_FOUND)
-                        .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License's pack not found with ID: " + lic.getPackId()).build();
-            } else {
-                if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN) && !bsc.getOrganizationsIds().contains(pack.getOrganization().getId())) {
-                    LOG.error("License for pack with id {} can not be created by user {}", pack.getId(), bsc.getUserPrincipal());
-                    return Response.status(Status.UNAUTHORIZED)
-                            .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized action on pack license").build();
-                }
+        }
+
+        if (pack == null) {
+            LOG.error("License pack with id {} not found in DB", lic.getPackId());
+            throw new SeCurisServiceException(ErrorCodes.NOT_FOUND, "License's pack not found with ID: " + lic.getPackId());
+        } else {
+            if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN) && !bsc.getOrganizationsIds().contains(pack.getOrganization().getId())) {
+                LOG.error("License for pack with id {} can not be created by user {}", pack.getId(), bsc.getUserPrincipal());
+                throw new SeCurisServiceException(ErrorCodes.UNAUTHORIZED_ACCESS, "Unathorized action on pack license");
             }
         }
 
         User createdBy = getUser(bsc.getUserPrincipal().getName(), em);
 
-        // TODO: Manage status if request data is set
+        if (lic.getRequestData() != null) {
+            SignedLicenseBean signedLicense = generateLicense(lic, em);
+            // If user provide a request data the license status is passed
+            // directly to ACTIVE
+            lic.setStatus(LicenseStatus.ACTIVE);
+            try {
+                lic.setLicenseData(JsonUtils.toJSON(signedLicense));
+            } catch (CurisException e) {
+                LOG.error("Error generaing license JSON", e);
+                throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
+            }
+        } else {
+            lic.setStatus(LicenseStatus.CREATED);
+        }
         lic.setCreatedBy(createdBy);
-        lic.setStatus(License.Status.CREATED);
         lic.setCreationTimestamp(new Date());
         lic.setModificationTimestamp(lic.getCreationTimestamp());
         em.persist(lic);
         em.persist(createLicenseHistoryAction(lic, createdBy, LicenseHistory.Actions.CREATE));
+        if (lic.getStatus() == LicenseStatus.ACTIVE) {
+            em.persist(createLicenseHistoryAction(lic, createdBy, LicenseHistory.Actions.CREATE, "Activated on creation"));
+        }
 
         return Response.ok(lic).build();
     }
-    
 
-    
+    private SignedLicenseBean generateLicense(License license, EntityManager em) throws SeCurisServiceException {
+        SignedLicenseBean sl = null;
+        Pack pack = em.find(Pack.class, license.getPackId());
+        RequestBean rb = validateRequestData(pack, license.getRequestData());
+        try {
+            LicenseBean lb = licenseGenerator.generateLicense(rb, extractPackMetadata(pack.getMetadata()), license.getExpirationDate(),
+                    pack.getLicenseTypeCode(), license.getCode());
+            sl = new SignedLicenseBean(lb);
+        } catch (SeCurisException e) {
+            throw new SeCurisServiceException(ErrorCodes.INVALID_LICENSE_REQUEST_DATA, "Error generating license: " + e.toString());
+        }
+        return sl;
+    }
+
+    private Map<String, Object> extractPackMetadata(Set<PackMetadata> packMetadata) {
+        Map<String, Object> metadata = new HashMap<>();
+        for (PackMetadata md : packMetadata) {
+            metadata.put(md.getKey(), md.getValue());
+        }
+
+        return metadata;
+    }
+
+    /**
+     * We check if the given request data is valid for the current Pack and has
+     * a valid format
+     * 
+     * @param pack
+     * @param requestData
+     * @throws SeCurisServiceException
+     */
+    private RequestBean validateRequestData(Pack pack, String requestData) throws SeCurisServiceException {
+        RequestBean rb = null;
+        try {
+            rb = JsonUtils.json2object(requestData, RequestBean.class);
+        } catch (CurisException e) {
+            throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data has not a valid format");
+        }
+
+        if (!rb.getCustomerCode().equals(pack.getOrganization().getCode())) {
+            throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data not valid, wrong Organization code");
+        }
+        if (!rb.getLicenseTypeCode().equals(pack.getLicenseTypeCode())) {
+            throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data not valid, wrong License type code");
+        }
+        // TODO: [rsanchez] Verify that if pack code is null we ignore it
+        if (rb.getPackCode() != null && !rb.getPackCode().equals(pack.getCode())) {
+            throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data not valid, wrong Pack code");
+        }
+        return rb;
+    }
+
     @PUT
     @POST
     @Path("/{licId}")
@@ -302,9 +422,22 @@
         currentLicense.setCode(lic.getCode());
         currentLicense.setFullName(lic.getFullName());
         currentLicense.setEmail(lic.getEmail());
-        currentLicense.setRequestData(lic.getRequestData());
+        if (lic.getRequestData() != null) {
+            SignedLicenseBean signedLicense = generateLicense(lic, em);
+            // If user provide a request data the license status is passed
+            // directly to ACTIVE
+            lic.setStatus(LicenseStatus.ACTIVE);
+            try {
+                lic.setLicenseData(JsonUtils.toJSON(signedLicense));
+            } catch (CurisException e) {
+                LOG.error("Error generaing license JSON", e);
+                throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
+            }
+            currentLicense.setRequestData(lic.getRequestData());
+        }
         currentLicense.setModificationTimestamp(new Date());
         em.persist(currentLicense);
+        em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.MODIFY));
 
         return Response.ok(currentLicense).build();
     }
@@ -321,7 +454,29 @@
         EntityManager em = emProvider.get();
         License lic = getCurrentLicense(licId, bsc, em);
 
-        if (lic.getStatus() != License.Status.CANCELED || lic.getStatus() != License.Status.CREATED) {
+        if (lic.getStatus() != LicenseStatus.CANCELLED || lic.getStatus() != LicenseStatus.CREATED) {
+            LOG.error("License {} can not be deleted with status {}", lic.getCode(), lic.getStatus());
+            return Response.status(Status.FORBIDDEN)
+                    .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License can not be deleted in current status").build();
+        }
+
+        em.remove(lic);
+        return Response.ok(Utils.createMap("success", true, "id", licId)).build();
+    }
+
+    @DELETE
+    @Path("/{licId}")
+    @Transactional
+    @Securable
+    @Produces({
+        MediaType.APPLICATION_JSON
+    })
+    public Response block(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
+        LOG.info("Deleting license with id: {}", licId);
+        EntityManager em = emProvider.get();
+        License lic = getCurrentLicense(licId, bsc, em);
+
+        if (lic.getStatus() != LicenseStatus.CANCELLED || lic.getStatus() != LicenseStatus.CREATED) {
             LOG.error("License {} can not be deleted with status {}", lic.getCode(), lic.getStatus());
             return Response.status(Status.FORBIDDEN)
                     .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License can not be deleted in current status").build();
@@ -349,6 +504,11 @@
         return lic;
     }
 
+    private User getUser(BasicSecurityContext bsc, EntityManager em) throws SeCurisServiceException {
+        String username = bsc.getUserPrincipal().getName();
+        return getUser(username, em);
+    }
+
     private User getUser(String username, EntityManager em) throws SeCurisServiceException {
         User user = null;
         if (username != null) {
@@ -358,6 +518,13 @@
             }
         }
         return user;
+    }
+
+    private File createTemporaryLicenseFile(License lic, String licFileName) throws IOException {
+        File f = Files.createTempDirectory("securis-server").toFile();
+        f = new File(f, licFileName);
+        IOUtils.write(lic.getLicenseData(), new FileWriter(f));
+        return f;
     }
 
     private LicenseHistory createLicenseHistoryAction(License lic, User user, String action, String comments) {
@@ -373,4 +540,12 @@
     private LicenseHistory createLicenseHistoryAction(License lic, User user, String action) {
         return createLicenseHistoryAction(lic, user, action, null);
     }
+
+    public static void main(String[] args) throws IOException {
+        File f = Files.createTempDirectory("securis-server").toFile();
+
+        LOG.info("f: {}", f);
+        f = new File(f, "config-server.lic");
+        LOG.info("f: {}", f);
+    }
 }
diff --git a/securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java b/securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java
index 49f7de5..1610f0b 100644
--- a/securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java
+++ b/securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java
@@ -4,15 +4,20 @@
 
 public class SeCurisServiceException extends CurisException {
 
-    private int status = 0;
+    private int errorCode = 0;
 
-    public SeCurisServiceException(int status, String msg) {
+    public SeCurisServiceException(int errorCode, String msg) {
         super(msg);
-        this.status = status;
+        this.errorCode = errorCode;
+    }
+
+    public SeCurisServiceException(String msg) {
+        super(msg);
+        this.errorCode = ErrorCodes.UNEXPECTED_ERROR;
     }
 
     public int getStatus() {
-        return status;
+        return errorCode;
     }
 
     /**
@@ -24,8 +29,13 @@
         public static int UNEXPECTED_ERROR = 1000;
         public static int INVALID_CREDENTIALS = 1001;
         public static int UNAUTHORIZED_ACCESS = 1002;
-        
+        public static int NOT_FOUND = 1003;
+        public static int INVALID_FORMAT = 1004;
+
         public static int INVALID_LICENSE_REQUEST_DATA = 1100;
         public static int LICENSE_NOT_READY_FOR_RENEW = 1101;
+
+        public static int INVALID_REQUEST_DATA = 1201;
+        public static int INVALID_REQUEST_DATA_FORMAT = 1202;
     }
 }
diff --git a/securis/src/main/java/net/curisit/securis/utils/EmailManager.java b/securis/src/main/java/net/curisit/securis/utils/EmailManager.java
new file mode 100644
index 0000000..41c768e
--- /dev/null
+++ b/securis/src/main/java/net/curisit/securis/utils/EmailManager.java
@@ -0,0 +1,196 @@
+package net.curisit.securis.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import javax.inject.Singleton;
+
+import net.curisit.securis.SeCurisException;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.TrustStrategy;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * Component that send emails using Mailgun API:
+ * http://documentation.mailgun.com/user_manual.html#sending-messages
+ * 
+ * @author roberto <roberto.sanchez@curisit.net>
+ */
+@Singleton
+public class EmailManager {
+
+    private static final Logger LOG = LogManager.getLogger(EmailManager.class);
+
+    private final String serverUrl;
+    private final CloseableHttpClient httpClient;
+
+    /**
+     * 
+     * @throws SeCurisException
+     */
+    private EmailManager() throws SeCurisException {
+        String domain = Params.get(Params.KEYS.MAILGUN_DOMAIN);
+        if (domain == null) {
+            throw new SeCurisException("Please, add '" + Params.KEYS.MAILGUN_DOMAIN + "' parameter to config file");
+        }
+        serverUrl = String.format("https://api.mailgun.net/v2/%s/messages", domain);
+        httpClient = createHttpClient();
+
+    }
+
+    private CloseableHttpClient createHttpClient() throws SeCurisException {
+        SSLContextBuilder builder = new SSLContextBuilder();
+        SSLConnectionSocketFactory sslsf = null;
+        try {
+            builder.loadTrustMaterial((KeyStore) null, new TrustStrategy() {
+                @Override
+                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                    return true;
+                }
+            });
+            sslsf = new SSLConnectionSocketFactory(builder.build());
+        } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e1) {
+            LOG.error(e1);
+            throw new SeCurisException("Error creating SSL socket factory");
+        }
+        CredentialsProvider provider = new BasicCredentialsProvider();
+        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("api", Params.get(Params.KEYS.MAILGUN_API_KEY));
+        provider.setCredentials(AuthScope.ANY, credentials);
+
+        return HttpClientBuilder.create().setDefaultCredentialsProvider(provider).setSSLSocketFactory(sslsf).build();
+    }
+
+    /**
+     * Basic method to send emails in text mode with attachment. The method is
+     * synchronous, It waits until server responses.
+     * 
+     * @param subject
+     * @param body
+     * @param to
+     * @param file
+     * @throws SeCurisException
+     * @throws UnsupportedEncodingException
+     */
+    public void sendEmail(String subject, String body, String to, String cc, File file) throws SeCurisException, UnsupportedEncodingException {
+        HttpPost postRequest = new HttpPost(serverUrl);
+
+        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+
+        builder.setCharset(Charset.forName("utf-8"));
+        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
+        builder.addTextBody("from", Params.get(Params.KEYS.EMAIL_FROM_ADDRESS));
+        builder.addTextBody("to", to);
+        if (cc != null)
+            builder.addTextBody("cc", cc);
+        builder.addTextBody("subject", subject, ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), Charset.forName("utf-8")));
+        builder.addTextBody("text", body, ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), Charset.forName("utf-8")));
+        if (file != null)
+            builder.addPart("attachment", new FileBody(file));
+
+        postRequest.setEntity(builder.build());
+        HttpResponse response;
+        try {
+            response = httpClient.execute(postRequest);
+
+            String jsonLic = IOUtils.toString(response.getEntity().getContent());
+            if (response.getStatusLine().getStatusCode() == 200) {
+                LOG.debug("Response content read OK: {}", jsonLic);
+                Map<String, Object> responseBean = JsonUtils.json2map(jsonLic);
+
+                LOG.debug("Response mail read OK: {}", responseBean);
+            } else {
+                throw new SeCurisException("Error sending email, response estatus: " + response.getStatusLine());
+            }
+        } catch (IOException e) {
+            LOG.error("Error sending email", e);
+            throw new SeCurisException("Error sending email");
+        }
+    }
+
+    /**
+     * Basic method to send emails in text mode with attachment. The method is
+     * asynchronous, It returns immediately
+     * 
+     * @param subject
+     * @param body
+     * @param to
+     * @param file
+     * @throws SeCurisException
+     * @throws UnsupportedEncodingException
+     */
+    public void sendEmailAsync(String subject, String body, String to, String cc, File file, EmailCallback callback) throws SeCurisException,
+            UnsupportedEncodingException {
+        Executor ex = Executors.newSingleThreadExecutor();
+        ex.execute(new Runnable() {
+
+            @Override
+            public void run() {
+                try {
+                    EmailManager.this.sendEmail(subject, body, to, cc, file);
+                    callback.success();
+                } catch (UnsupportedEncodingException e) {
+                    callback.error(new SeCurisException("Error sending email", e));
+                } catch (SeCurisException e) {
+                    callback.error(e);
+                }
+
+            }
+        });
+
+    }
+
+    public static interface EmailCallback {
+        public void success();
+
+        public void error(SeCurisException e);
+    }
+
+    public static void main(String[] args) throws SeCurisException, UnsupportedEncodingException {
+        // new EmailManager().sendEmail("España así de bien",
+        // "Me gusta esta prueba\nCon varias líneas\n\n\n--\nNo response",
+        // "info@r75.es", new File(
+        // "/Users/rob/Downloads/test.req"));
+        new EmailManager().sendEmailAsync("España así de bien", "Me gusta esta prueba\nCon varias líneas\n\n\n--\nNo response", "info@r75.es",
+                "dev@r75.es", new File("/Users/rob/Downloads/test.req"), new EmailCallback() {
+
+                    @Override
+                    public void success() {
+                        LOG.info("Success!!!");
+                    }
+
+                    @Override
+                    public void error(SeCurisException e) {
+                        LOG.error("Error: {} !!!", e);
+                    }
+                });
+        LOG.info("Waiting for email to be sent...");
+    }
+
+}
diff --git a/securis/src/main/resources/db/schema.sql b/securis/src/main/resources/db/schema.sql
index 2741405..d16fde7 100644
--- a/securis/src/main/resources/db/schema.sql
+++ b/securis/src/main/resources/db/schema.sql
@@ -14,6 +14,7 @@
   last_name VARCHAR(100) NULL ,
   last_login DATETIME NULL ,
   lang VARCHAR(10) NULL ,
+  email VARCHAR(150) NULL ,
   creation_timestamp DATETIME NULL ,  
   modification_timestamp DATETIME NULL ,  
   PRIMARY KEY (username));
@@ -78,7 +79,7 @@
   num_licenses INT NOT NULL ,
   init_valid_date DATE NOT NULL default today(),
   end_valid_date DATE NOT NULL default today(),
-  status VARCHAR(2) NOT NULL default 'PE',
+  status VARCHAR(2) NOT NULL default 'CR',
   comments VARCHAR(1024) NULL ,
   license_type_id INT NOT NULL,  
   organization_id INT NOT NULL,  
@@ -113,7 +114,7 @@
   expiration_date DATETIME NULL ,  
   canceled_by varchar(45) NULL ,  
   created_by varchar(45) NULL ,  
-  status INT NOT NULL default 0,  
+  status VARCHAR(2) NOT NULL default 'CR',  
   PRIMARY KEY (id));
   
   
@@ -127,5 +128,12 @@
   comments VARCHAR(512) ,
   PRIMARY KEY (id));
   
-  
-  
\ No newline at end of file
+   
+drop table IF EXISTS blocked_request;
+CREATE TABLE IF NOT EXISTS blocked_request (
+  hash VARCHAR(256) NOT NULL ,
+  request_data VARCHAR(1024) NULL ,
+  blocked_by varchar(45) NULL ,  
+  creation_timestamp DATETIME NOT NULL default now(),  
+  PRIMARY KEY (id));
+   
\ No newline at end of file
diff --git a/securis/src/main/resources/securis-server.properties b/securis/src/main/resources/securis-server.properties
index 80f083f..5c98340 100644
--- a/securis/src/main/resources/securis-server.properties
+++ b/securis/src/main/resources/securis-server.properties
@@ -12,3 +12,8 @@
 ssl.keystore.type = PKCS12
 ssl.keystore.password = curist3c
 ssl.keystore.alias = 
+
+email.from.address = SeCuris support securis@curisit.net
+mailgun.domain = m.curisit.net
+mailgun.api.key = key-7dekyqx56nc0irbnyk4z39ezhsrikqi8
+email.lic.default.subject = SeCuris - License file for {0} application
\ No newline at end of file
diff --git a/securis/src/main/resources/static/js/catalogs.json b/securis/src/main/resources/static/js/catalogs.json
index c0da309..ccb1948 100644
--- a/securis/src/main/resources/static/js/catalogs.json
+++ b/securis/src/main/resources/static/js/catalogs.json
@@ -154,6 +154,12 @@
 		"readOnly" : true,
 		"mandatory" : true
 	}, {
+		"name" : "email",
+		"display" : "Email",
+		"type" : "email",
+		"maxlength" : 150,
+		"mandatory" : true
+	}, {
 		"name" : "first_name",
 		"display" : "First name",
 		"type" : "string",
diff --git a/securis/src/main/resources/static/licenses.html b/securis/src/main/resources/static/licenses.html
index d9a9056..2389f82 100644
--- a/securis/src/main/resources/static/licenses.html
+++ b/securis/src/main/resources/static/licenses.html
@@ -228,10 +228,21 @@
 				</div>
 
 				<div class="form-group">
-					<div class="col-md-offset-3 col-md-10" id="saveContainer">
+					<div class="col-md-offset-3 col-md-10 " id="saveContainer">
 						<button id="save" type="submit" class="btn btn-primary">
 							<span i18n class="glyphicon glyphicon-floppy-disk"></span> Save
 						</button>
+						<button id="acc" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
+							<span i18n class="glyphicon glyphicon-align-justify"></span> Actions
+							<span class="caret"></span>
+						</button>
+						<ul class="dropdown-menu" role="menu">
+						    <li><a href="#">Activate</a></li>
+						    <li><a href="#">On hold</a></li>
+						    <li class="divider"></li>
+						    <li><a href="#">Invalidate</a></li>
+						    <li><a href="#">Delete</a></li>
+						  </ul>
 					</div>
 				</div>
 			</form>
@@ -534,26 +545,18 @@
 						<button id="save" type="submit" class="btn btn-primary">
 							<span i18n class="glyphicon glyphicon-floppy-disk"></span> Save
 						</button>
-						<button id="activate" class="btn btn-success"
-							ng-click="activateLicense(license)"
-							ng-if="isActionVisible('activate', license)">
-							<span i18n class="glyphicon glyphicon-check"></span> Activate
+						<button id="acc" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
+							<span i18n class="glyphicon glyphicon-align-justify"></span> Actions
+							<span class="caret"></span>
 						</button>
-						<button id="send" class="btn btn-info"
-							ng-click="sendLicense(license)"
-							ng-if="isActionVisible('send', license)">
-							<span i18n class="glyphicon glyphicon-send"></span> Send
-						</button>
-						<button id="cancel" class="btn btn-warning"
-							ng-click="cancelLicense(license)"
-							ng-if="isActionVisible('cancel', license)">
-							<span i18n class="glyphicon glyphicon-ban-circle"></span> Cancel
-						</button>
-						<button id="remove" class="btn btn-danger"
-							ng-click="removeLicense(license)"
-							ng-if="isActionVisible('delete', license)">
-							<span i18n class="glyphicon glyphicon-trash"></span> Delete
-						</button>
+						<ul class="dropdown-menu" role="menu">
+						    <li ng-if="isActionVisible('activate', license)"><a ng-click="activateLicense(license)" href="#">Activate</a></li>
+						    <li ng-if="isActionVisible('download', license)"><a ng-click="downloadLicense(license)" href="#">Download</a></li>
+						    <li ng-if="isActionVisible('send', license)"><a ng-click="sendLicense(license)" href="#">Send by email</a></li>
+						    <li ng-if="isActionVisible('cancel', license)"><a ng-click="cancelLicense(license)" href="#">Invalidate</a></li>
+						    <li ng-if="isActionVisible('delete', license)"><a ng-click="removeLicense(license)" href="#">Delete</a></li>
+						  </ul>
+						
 					</div>
 				</div>
 			</form>

--
Gitblit v1.3.2